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

1.1       snw         1: /*
1.8     ! snw         2:  *   $Id: transact.c,v 1.7 2025/04/09 19:52:02 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.8     ! snw        27:  *   Revision 1.7  2025/04/09 19:52:02  snw
        !            28:  *   Eliminate as many warnings as possible while building with -Wall
        !            29:  *
1.7       snw        30:  *   Revision 1.6  2025/03/24 04:13:12  snw
                     31:  *   Replace action macro dat with fra_dat to avoid symbol conflict on OS/2
                     32:  *
1.6       snw        33:  *   Revision 1.5  2025/03/24 02:54:47  snw
                     34:  *   Transaction compat fixes for OS/2
                     35:  *
1.5       snw        36:  *   Revision 1.4  2025/03/09 19:50:47  snw
                     37:  *   Second phase of REUSE compliance and header reformat
                     38:  *
1.4       snw        39:  *
                     40:  * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
                     41:  * SPDX-License-Identifier: AGPL-3.0-or-later
1.1       snw        42:  **/
                     43: 
                     44: #include <string.h>
                     45: #include <stddef.h>
                     46: #include <stdio.h>
                     47: #include <stdlib.h>
                     48: #include <unistd.h>
                     49: #include <errno.h>
                     50: 
                     51: #include "mpsdef.h"
                     52: #include "transact.h"
                     53: #include "iftab.h"
                     54: #include "journal.h"
                     55: #include "shmmgr.h"
                     56: #include "mref.h"
                     57: #include "tp_check.h"
                     58: 
                     59: #define FALSE   0
                     60: #define TRUE    1
                     61: 
1.5       snw        62: #if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
1.1       snw        63: union semun {
                     64:     int              val;    /* Value for SETVAL */
                     65:     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
                     66:     unsigned short  *array;  /* Array for GETALL, SETALL */
                     67:     struct seminfo  *__buf;  /* Buffer for IPC_INFO
                     68:                                 (Linux-specific) */
                     69: };
                     70: #endif
                     71: 
                     72: void m_log (int, const char *);
                     73: 
                     74: int semid_tp;
                     75: int tp_committing = FALSE;
                     76: int tp_level = 0;
                     77: tp_transaction transactions[TP_MAX_NEST];
                     78: 
                     79: 
                     80: void tp_init(void)
                     81: {
                     82:     union semun arg;
                     83:     key_t tp_sk;
                     84: 
                     85:     tp_sk = ftok (config_file, 4);
                     86:     
                     87:     if (first_process) {
                     88: 
                     89:         semid_tp = semget (tp_sk, 1, 0666 | IPC_CREAT);
                     90:         if (semid_tp == -1) {
                     91:             fprintf (stderr, "tp_init:  failed to create transaction processing semaphore [errno %d]\r\n", errno);
                     92:             exit (1);
                     93:         }
                     94: 
                     95:         arg.val = 1;
                     96:         if (semctl (semid_tp, 0, SETVAL, arg) == -1) {
                     97:             fprintf (stderr, "tp_init:  failed to initialize transaction processing semaphore\r\n");
                     98:             exit (1);
                     99:         }
                    100: 
                    101:     }
                    102:     else {
                    103:         
                    104:         semid_tp = semget (tp_sk, 1, 0);
                    105:         if (semid_tp == -1) {
                    106:             fprintf (stderr, "tp_init:  could not attach to transaction processing semaphore [errno %d]\r\n", errno);
                    107:             exit (1);
                    108:         }
                    109: 
                    110:     }
                    111: 
                    112:     return;
                    113: 
                    114: }
                    115: 
                    116: short tp_get_sem(void)
                    117: {
                    118:     int tries;
                    119:     struct sembuf s = {0, -1, 0};
                    120: 
                    121:     char msgbuf[100];
                    122: 
                    123:     snprintf (msgbuf, 99, "tp_get_sem:  process %d attempting to acquire transaction processing semaphore", pid);
                    124:     m_log (1, msgbuf);
                    125:     
                    126:     
                    127:     /* our process already owns the semaphore */
                    128:     if (shm_config->hdr->tp_owner == pid) {
                    129: 
                    130:         snprintf (msgbuf, 99, "tp_get_sem:  process %d increments transaction processing semaphore counter", pid);
                    131:         m_log (1, msgbuf);
                    132:     
                    133:         
                    134:         if (first_process == TRUE) {
                    135:             fprintf (stderr, "tp_get_sem:  daemon process increments critical section counter\r\n");
                    136:         }
                    137: 
                    138: 
                    139:         shm_config->hdr->tp_semctr++;
                    140: 
                    141:         return TRUE;
                    142:     }
                    143: 
                    144:     if (first_process == TRUE) {
                    145:         fprintf (stderr, "tp_get_sem:  daemon process enters critical section\r\n");
                    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: 
                    155:             snprintf (msgbuf, 99, "tp_get_sem:  process %d takes transaction processing semaphore", pid);
                    156:             m_log (1, msgbuf);
                    157:     
                    158:             
                    159:             if (first_process == TRUE) {
                    160:                 fprintf (stderr, "tp_get_sem:  daemon process takes transaction processing semaphore\r\n");
                    161:             }
                    162: 
                    163:             return TRUE;
                    164:         }
                    165: 
                    166:         snprintf (msgbuf, 99, "tp_get_sem:  process %d attempting to acquire transaction processing semaphore (tries = %d)", pid, tries);
                    167:         m_log (1, msgbuf);
                    168:     
                    169: 
                    170:         sleep (1);
                    171: 
                    172:     }
                    173: 
                    174:     return FALSE;
                    175:     
                    176: }
                    177: 
                    178: void tp_release_sem(void)
                    179: {
                    180: 
                    181:     char msgbuf[100];
                    182: 
                    183:     if (shm_config->hdr->tp_semctr == 1) {
                    184: 
                    185:         struct sembuf s = {0, 1, 0};
                    186: 
                    187:         if (first_process == TRUE) {
                    188:             fprintf (stderr, "tp_release_sem:  daemon process leaves critical section\r\n");
                    189:         }
                    190: 
                    191:         
                    192:         shm_config->hdr->tp_semctr = 0;
                    193:         shm_config->hdr->tp_owner = 0;
                    194: 
                    195:         if (first_process == TRUE) {
                    196:             fprintf (stderr, "tp_release_sem:  daemon process relinquishes transaction processing semaphore\r\n");
                    197:         }
                    198: 
                    199: 
                    200:         snprintf (msgbuf, 99, "tp_get_sem:  process %d releases transaction processing semaphore", pid);
                    201:         m_log (1, msgbuf);
                    202: 
                    203:         
                    204:         semop (semid_tp, &s, 1);
                    205:         
                    206:     }
                    207:     else {
                    208: 
                    209:         if (first_process == TRUE) {
                    210:             fprintf (stderr, "tp_release_sem:  daemon process decrements critical section counter\r\n");
                    211:         }
                    212:         
                    213:         snprintf (msgbuf, 99, "tp_get_sem:  process %d decrements transaction processing semaphore counter", pid);
                    214:         m_log (1, msgbuf);
                    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: 
                    227:         snprintf (m, 256, "Attempt to exceed TP_MAX_NEST. Transaction aborted.\r\n\201");
                    228:         write_m (m);
                    229: 
                    230:         return FALSE;
                    231:     }
                    232: 
                    233:     if (((serial == TRUE) && (tp_get_sem () == TRUE)) ||
                    234:         (serial == FALSE)) {
                    235: 
                    236:         tp_level++;
                    237:         
                    238:         jnl_ent_write (JNLA_TSTART, "", "");
                    239:     
                    240:         strcpy (transactions[tp_level].tp_id, tp_id);
                    241:         
                    242:         transactions[tp_level].serial = serial;
                    243:         transactions[tp_level].restartable = restartable;
                    244:         
                    245:         transactions[tp_level].opcount = 0;
                    246:       
                    247:         return TRUE;
                    248:         
                    249:     }
                    250:     else {
                    251:         fprintf (stderr, "tp_tstart:  could not get transaction processing semaphore\r\n");
                    252:         exit (1);
                    253:     }
                    254:     
                    255: 
                    256: }
                    257: 
                    258: int tp_add_op(short islock, short action, char *key, char *data)
                    259: {
                    260:     int oc;
                    261:     freem_ref_t *gr;
                    262: 
                    263:     gr = (freem_ref_t *) malloc (sizeof (freem_ref_t));
                    264:     NULLPTRCHK(gr,"tp_add_op");
                    265:     
                    266:     mref_init (gr, MREF_RT_GLOBAL, "");
                    267:     internal_to_mref (gr, key);
                    268:     
                    269:     if (transactions[tp_level].opcount == TP_MAX_OPS) {
                    270:         char m[256];
                    271: 
                    272:         snprintf (m, 256, "Attempt to exceed TP_MAX_OPS at transaction level %d. Rolling back.\r\n\201", tp_level);
                    273:         write_m (m);
                    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: 
1.2       snw       317:     stcpy ((char *) &transactions[tp_level].ops[oc].key, key);
                    318:     stcpy ((char *) &transactions[tp_level].ops[oc].data, data);
1.1       snw       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: 
1.6       snw       500:         case fra_dat:
1.1       snw       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>