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

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

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