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

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

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