File:  [Coherent Logic Development] / freem / src / transact.c
Revision 1.11: download - view: text, annotated - select for diffs
Tue Apr 15 18:19:40 2025 UTC (3 months, 2 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Further attempts to fix FreeBSD

    1: /*
    2:  *   $Id: transact.c,v 1.11 2025/04/15 18:19:40 snw Exp $
    3:  *    FreeM transaction processing support
    4:  *
    5:  *  
    6:  *   Author: Serena Willis <snw@coherent-logic.com>
    7:  *    Copyright (C) 1998 MUG Deutschland
    8:  *    Copyright (C) 2020, 2025 Coherent Logic Development LLC
    9:  *
   10:  *
   11:  *   This file is part of FreeM.
   12:  *
   13:  *   FreeM is free software: you can redistribute it and/or modify
   14:  *   it under the terms of the GNU Affero Public License as published by
   15:  *   the Free Software Foundation, either version 3 of the License, or
   16:  *   (at your option) any later version.
   17:  *
   18:  *   FreeM is distributed in the hope that it will be useful,
   19:  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   20:  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   21:  *   GNU Affero Public License for more details.
   22:  *
   23:  *   You should have received a copy of the GNU Affero Public License
   24:  *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
   25:  *
   26:  *   $Log: transact.c,v $
   27:  *   Revision 1.11  2025/04/15 18:19:40  snw
   28:  *   Further attempts to fix FreeBSD
   29:  *
   30:  *   Revision 1.10  2025/04/15 16:49:36  snw
   31:  *   Make use of logprintf throughout codebase
   32:  *
   33:  *   Revision 1.9  2025/04/13 04:22:43  snw
   34:  *   Fix snprintf calls
   35:  *
   36:  *   Revision 1.8  2025/04/10 01:24:39  snw
   37:  *   Remove C++ style comments
   38:  *
   39:  *   Revision 1.7  2025/04/09 19:52:02  snw
   40:  *   Eliminate as many warnings as possible while building with -Wall
   41:  *
   42:  *   Revision 1.6  2025/03/24 04:13:12  snw
   43:  *   Replace action macro dat with fra_dat to avoid symbol conflict on OS/2
   44:  *
   45:  *   Revision 1.5  2025/03/24 02:54:47  snw
   46:  *   Transaction compat fixes for OS/2
   47:  *
   48:  *   Revision 1.4  2025/03/09 19:50:47  snw
   49:  *   Second phase of REUSE compliance and header reformat
   50:  *
   51:  *
   52:  * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
   53:  * SPDX-License-Identifier: AGPL-3.0-or-later
   54:  **/
   55: 
   56: #include <string.h>
   57: #include <stddef.h>
   58: #include <stdio.h>
   59: #include <stdlib.h>
   60: #include <unistd.h>
   61: #include <errno.h>
   62: 
   63: #include "mpsdef.h"
   64: #include "transact.h"
   65: #include "iftab.h"
   66: #include "journal.h"
   67: #include "shmmgr.h"
   68: #include "mref.h"
   69: #include "tp_check.h"
   70: #include "log.h"
   71: 
   72: #define FALSE   0
   73: #define TRUE    1
   74: 
   75: #if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
   76: union semun {
   77:     int              val;    /* Value for SETVAL */
   78:     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
   79:     unsigned short  *array;  /* Array for GETALL, SETALL */
   80:     struct seminfo  *__buf;  /* Buffer for IPC_INFO
   81:                                 (Linux-specific) */
   82: };
   83: #endif
   84: 
   85: int semid_tp;
   86: int tp_committing = FALSE;
   87: int tp_level = 0;
   88: tp_transaction transactions[TP_MAX_NEST];
   89: 
   90: 
   91: void tp_init(void)
   92: {
   93:     union semun arg;
   94:     key_t tp_sk;
   95: 
   96:     tp_sk = ftok (config_file, 4);
   97:     
   98:     if (first_process) {
   99: 
  100:         semid_tp = semget (tp_sk, 1, 0666 | IPC_CREAT);
  101:         
  102:         if (semid_tp == -1) {
  103:             logprintf (FM_LOG_FATAL, "tp_init:  failed to create transaction processing semaphore [%s]", strerror (errno));
  104:         }
  105:         else {
  106:             logprintf (FM_LOG_INFO, "tp_init:  got semaphore id %d", semid_tp);
  107:         }
  108: 
  109:         arg.val = 1;
  110:         if (semctl (semid_tp, 0, SETVAL, arg) == -1) {
  111:             logprintf (FM_LOG_FATAL, "tp_init:  failed to initialize transaction processing semaphore [%s]", strerror (errno));
  112:         }
  113: 
  114:     }
  115:     else {
  116:         
  117:         semid_tp = semget (tp_sk, 1, 0);
  118:         if (semid_tp == -1) {
  119:             logprintf (FM_LOG_FATAL, "tp_init:  could not attach to transaction processing semaphore [%s]", strerror (errno));
  120:         }
  121: 
  122:     }
  123: 
  124:     return;
  125: 
  126: }
  127: 
  128: short tp_get_sem(void)
  129: {
  130:     int tries;
  131:     struct sembuf s = {0, -1, 0};
  132: 
  133:     char msgbuf[100];
  134: 
  135:     logprintf (FM_LOG_INFO, "tp_get_sem:  process %d attempting to acquire transaction processing semaphore", pid);
  136:     
  137:     
  138:     /* our process already owns the semaphore */
  139:     if (shm_config->hdr->tp_owner == pid) {
  140: 
  141:         logprintf (FM_LOG_INFO, "tp_get_sem:  process %ld increments transaction processing semaphore counter", pid);
  142:     
  143:         
  144:         if (first_process == TRUE) {
  145:             logprintf (FM_LOG_INFO, "tp_get_sem:  daemon process increments critical section counter");
  146:         }
  147: 
  148: 
  149:         shm_config->hdr->tp_semctr++;
  150: 
  151:         return TRUE;
  152:     }
  153: 
  154:     if (first_process == TRUE) {
  155:         logprintf (FM_LOG_INFO, "tp_get_sem:  daemon process enters critical section");
  156:     }
  157: 
  158:     
  159:     for (tries = 0; tries < 10; tries++) {
  160: 
  161:         if (semop (semid_tp, &s, 1) != -1) {
  162:             shm_config->hdr->tp_owner = pid;
  163:             shm_config->hdr->tp_semctr = 1;
  164: 
  165:             logprintf (FM_LOG_INFO, "tp_get_sem:  process %ld takes transaction processing semaphore", pid);
  166:             
  167:             if (first_process == TRUE) {
  168:                 logprintf (FM_LOG_INFO, "tp_get_sem:  daemon process takes transaction processing semaphore");
  169:             }
  170: 
  171:             return TRUE;
  172:         }
  173: 
  174:         logprintf (FM_LOG_WARNING, "tp_get_sem:  process %ld retries attempting to acquire transaction processing semaphore (tries = %d)", pid, tries);
  175: 
  176:         sleep (1);
  177: 
  178:     }
  179: 
  180:     return FALSE;
  181:     
  182: }
  183: 
  184: void tp_release_sem(void)
  185: {
  186: 
  187:     char msgbuf[100];
  188: 
  189:     if (shm_config->hdr->tp_semctr == 1) {
  190: 
  191:         struct sembuf s = {0, 1, 0};
  192: 
  193:         if (first_process == TRUE) {
  194:             logprintf (FM_LOG_INFO, "tp_release_sem:  daemon process leaves critical section");
  195:         }
  196: 
  197:         
  198:         shm_config->hdr->tp_semctr = 0;
  199:         shm_config->hdr->tp_owner = 0;
  200: 
  201:         if (first_process == TRUE) {
  202:             logprintf (FM_LOG_INFO, "tp_release_sem:  daemon process relinquishes transaction processing semaphore");
  203:         }
  204: 
  205: 
  206:         logprintf (FM_LOG_INFO, "tp_get_sem:  process %ld releases transaction processing semaphore", pid);
  207:         
  208:         semop (semid_tp, &s, 1);
  209:         
  210:     }
  211:     else {
  212: 
  213:         if (first_process == TRUE) {
  214:             logprintf (FM_LOG_INFO, "tp_release_sem:  daemon process decrements critical section counter");
  215:         }
  216:         
  217:         logprintf (FM_LOG_INFO, "tp_get_sem:  process %d decrements transaction processing semaphore counter", pid);
  218:         
  219:         shm_config->hdr->tp_semctr--;
  220:     }
  221: 
  222:     
  223: }
  224: 
  225: int tp_tstart(char *tp_id, short serial, short restartable, char **sym_save)
  226: {
  227:     if (tp_level == TP_MAX_NEST) {
  228:         char m[256];
  229: 
  230:         logprintf (FM_LOG_ERROR, "Attempt to exceed TP_MAX_NEST. Transaction aborted.");
  231: 
  232:         return FALSE;
  233:     }
  234: 
  235:     if (((serial == TRUE) && (tp_get_sem () == TRUE)) ||
  236:         (serial == FALSE)) {
  237: 
  238:         tp_level++;
  239:         
  240:         jnl_ent_write (JNLA_TSTART, "", "");
  241:     
  242:         strcpy (transactions[tp_level].tp_id, tp_id);
  243:         
  244:         transactions[tp_level].serial = serial;
  245:         transactions[tp_level].restartable = restartable;
  246:         
  247:         transactions[tp_level].opcount = 0;
  248:       
  249:         return TRUE;
  250:         
  251:     }
  252:     else {
  253:         logprintf (FM_LOG_FATAL, "tp_tstart:  could not get transaction processing semaphore");
  254:     }
  255:     
  256: 
  257: }
  258: 
  259: int tp_add_op(short islock, short action, char *key, char *data)
  260: {
  261:     int oc;
  262:     freem_ref_t *gr;
  263: 
  264:     gr = (freem_ref_t *) malloc (sizeof (freem_ref_t));
  265:     NULLPTRCHK(gr,"tp_add_op");
  266:     
  267:     mref_init (gr, MREF_RT_GLOBAL, "");
  268:     internal_to_mref (gr, key);
  269:     
  270:     if (transactions[tp_level].opcount == TP_MAX_OPS) {
  271:         char m[256];
  272: 
  273:         logprintf (FM_LOG_ERROR, "attempt to exceed TP_MAX_OPS at transaction level %d; rolling back", tp_level);
  274: 
  275:         free (gr);
  276: 
  277:         tp_trollback (1);
  278:         tp_cleanup (1);
  279: 
  280:         if (transactions[tp_level].serial == TRUE) {
  281:             tp_release_sem();
  282:         }
  283:         
  284:         return FALSE;
  285:     }
  286:     
  287:     /* update transaction-in-flight symbol table */
  288:     switch (action) {
  289: 
  290:         case lock_inc:
  291:         case lock_dec:
  292:         case lock_old:
  293:         case set_sym:
  294:             iftab_insert (action, key, data, tp_level);
  295:             break;
  296: 
  297:         case kill_sym:
  298:             iftab_kill (key);
  299:             break;
  300:             
  301:     }
  302: 
  303:     if (transactions[tp_level].serial == TRUE) {
  304:         /* mark the global for checkpointing */
  305:         cptab_insert (tp_level, gr->name);
  306:     }
  307: 
  308:     free (gr);
  309: 
  310:     transactions[tp_level].opcount = transactions[tp_level].opcount + 1;
  311: 
  312:     oc = transactions[tp_level].opcount;
  313: 
  314:     transactions[tp_level].ops[oc].is_lock = islock;
  315:     transactions[tp_level].ops[oc].action = action;
  316: 
  317:     stcpy ((char *) &transactions[tp_level].ops[oc].key, key);
  318:     stcpy ((char *) &transactions[tp_level].ops[oc].data, data);
  319:        
  320:  
  321:     return TRUE;
  322: }
  323: 
  324: int tp_tcommit(void)
  325: {
  326:     register int i;
  327:     short is_serial = transactions[tp_level].serial;
  328:     
  329:     tp_committing = TRUE;
  330: 
  331:     if (is_serial) {
  332:         /* checkpoint all globals involved in transaction */
  333:         cptab_precommit (tp_level);
  334:     }
  335:     
  336:     for(i = 1; i <= transactions[tp_level].opcount; i++) {
  337:         
  338:         if (transactions[tp_level].ops[i].is_lock == FALSE) {
  339:             global (transactions[tp_level].ops[i].action, transactions[tp_level].ops[i].key, transactions[tp_level].ops[i].data);
  340: 
  341:             if (merr () > OK) goto commit_error;
  342:                 
  343:         }
  344:         
  345:     }
  346: 
  347:     jnl_ent_write (JNLA_TCOMMIT, "\201", "\201");
  348: 
  349:     if (is_serial) {
  350:         cptab_postcommit (tp_level);
  351:     }
  352: 
  353:     goto commit_done;
  354:     
  355: commit_error:
  356:     
  357:     tp_trollback (1);
  358: 
  359: commit_done:
  360: 
  361:     tp_cleanup (1);
  362:     
  363:     tp_committing = FALSE;
  364: 
  365:     if (is_serial) {
  366:         tp_release_sem ();
  367:     }
  368:     
  369:     return TRUE;
  370: }
  371: 
  372: int tp_cleanup(int levels)
  373: {
  374:     register int i;
  375: 
  376:     for (i = tp_level; i >= (((tp_level - levels) >= 0) ? tp_level - levels : 0); i--) {
  377:         iftab_pop_tlevel (i);
  378:     }
  379: 
  380:     tp_level = ((tp_level - levels) >= 0) ? tp_level - levels : 0;
  381: 
  382:     return TRUE;
  383: }
  384: 
  385: int tp_trollback(int levels)
  386: {
  387:     register int i;
  388:     register int j;
  389: 
  390:     for (i = tp_level; i >= (((tp_level - levels) >= 0) ? tp_level - levels : 0); i--) {
  391:         
  392:         for (j = 1; j <= transactions[i].opcount; j++) {
  393:             
  394:             if (transactions[i].ops[j].is_lock == TRUE) {
  395:                 locktab_decrement (transactions[i].ops[j].key, -1);
  396:             }
  397:             
  398:         }
  399: 
  400:         if (transactions[i].serial == TRUE) {
  401:             cptab_rollback (i);
  402:         }
  403:         
  404:     }
  405: 
  406: 
  407:     return TRUE;
  408: }
  409: 
  410: int tp_trestart(void)
  411: {
  412:     return 0;
  413: }
  414: 
  415: void tp_tdump(void)
  416: {
  417: 
  418:     int i, j;
  419: 
  420:     char tkey[256];
  421:     char tdata[256];
  422:     char tact[256];
  423:     
  424:     set_io (UNIX);
  425: 
  426:     if (tp_level == 0) {
  427:         printf("No transaction is active.\n");
  428: 
  429:         return;
  430:     }
  431: 
  432:     for(i = 1; i <= tp_level; i++) {
  433: 
  434:         if(i == tp_level) {
  435:             printf(" $TLEVEL %d*\n", i);
  436:         }
  437:         else {
  438:             printf(" $TLEVEL %d\n", i);
  439:         }
  440: 
  441:         printf("  Operations for Transaction ID: %s [%s%s]\n",
  442:                transactions[i].tp_id,
  443:                ((transactions[i].restartable == TRUE) ? "RESTARTABLE" : "NON-RESTARTABLE"),
  444:                ((transactions[i].serial == TRUE) ? " SERIAL" : " BATCH"));
  445: 
  446:         printf ("\n   %-10s%-15s%-15s\n", "OP. NO.", "ACTION", "KEY/DATA");
  447:         printf ("   %-10s%-15s%-15s\n", "-------", "------", "--------");
  448:         
  449:         
  450:         for(j = 1; j <= transactions[i].opcount; j++) {          
  451:             stcpy (tkey, transactions[i].ops[j].key);
  452:             stcnv_m2c (tkey);
  453:             stcpy (tdata, transactions[i].ops[j].data);
  454:             stcnv_m2c (tdata);
  455: 
  456:             tp_get_op_name (tact, transactions[i].ops[j].action);
  457: 
  458:             if (transactions[i].ops[j].action == set_sym) {
  459:                 printf ("   %-10d%-15s%s=%s\n", j, tact, tkey, tdata);
  460:             }
  461:             else {
  462:                 printf ("   %-10d%-15s%s\n", j, tact, tkey); 
  463:             }
  464:             
  465:         }
  466: 
  467:         cptab_dump (i);
  468:         
  469:     }
  470: 
  471: 
  472:     set_io (MUMPS);
  473: }
  474: 
  475: void tp_get_op_name(char *buf, const short action)
  476: {
  477:     switch (action) {
  478:         
  479:         case set_sym:
  480:             strcpy (buf, "SET");
  481:             break;
  482: 
  483:         case killone:
  484:         case kill_sym:
  485:         case kill_all:
  486:         case killexcl:
  487:             strcpy (buf, "KILL");
  488:             break;
  489: 
  490:         case new_sym:
  491:         case new_all:
  492:         case newexcl:
  493:             strcpy (buf, "NEW");
  494:             break;
  495: 
  496:         case get_sym:
  497:             strcpy (buf, "GET");
  498:             break;
  499: 
  500:         case fra_dat:
  501:             strcpy (buf, "$DATA");
  502:             break;
  503: 
  504:         case fra_order:
  505:             strcpy (buf, "$ORDER");
  506:             break;
  507: 
  508:         case fra_query:
  509:         case bigquery:
  510:             strcpy (buf, "$QUERY");
  511:             break;
  512: 
  513:         case getinc:
  514:             strcpy (buf, "$INCREMENT");
  515:             break;
  516: 
  517:         case getnext:
  518:             strcpy (buf, "$NEXT");
  519:             break;
  520: 
  521:         case lock_inc:
  522:             strcpy (buf, "LOCK (INCR)");
  523:             break;
  524: 
  525:         case lock_old:
  526:             strcpy (buf, "LOCK (TRAD)");
  527:             break;
  528:             
  529:     }
  530:             
  531: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>