Annotation of freem/src/transact.c, revision 1.9

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

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