Annotation of freem/src/locktab.c, revision 1.6

1.1       snw         1: /*
1.6     ! snw         2:  *   $Id: locktab.c,v 1.5 2025/03/24 02:57:25 snw Exp $
1.1       snw         3:  *    lock table implementation
                      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) 2021, 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: locktab.c,v $
1.6     ! snw        27:  *   Revision 1.5  2025/03/24 02:57:25  snw
        !            28:  *   Shared memory compatibility fixes for OS/2
        !            29:  *
1.5       snw        30:  *   Revision 1.4  2025/03/09 19:14:25  snw
                     31:  *   First phase of REUSE compliance and header reformat
                     32:  *
1.4       snw        33:  *
                     34:  * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
                     35:  * SPDX-License-Identifier: AGPL-3.0-or-later
1.1       snw        36:  **/
                     37: 
                     38: #include <stdio.h>
                     39: #include <stdlib.h>
                     40: #include <unistd.h>
                     41: #include <time.h>
                     42: #include <string.h>
                     43: 
                     44: #include "mpsdef.h"
                     45: #include "locktab.h"
                     46: #include "shmmgr.h"
                     47: #include "mref.h"
                     48: #include "transact.h"
                     49: 
1.5       snw        50: #if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
1.1       snw        51: union semun {
                     52:     int              val;    /* Value for SETVAL */
                     53:     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
                     54:     unsigned short  *array;  /* Array for GETALL, SETALL */
                     55:     struct seminfo  *__buf;  /* Buffer for IPC_INFO
                     56:                                 (Linux-specific) */
                     57: };
                     58: #endif
                     59: 
                     60: int semid_locktab;
                     61: 
                     62: int locktab_list_count(char *key);
                     63: 
                     64: void locktab_init(void)
                     65: {
                     66:     union semun arg;
                     67:     key_t lt_sk;
                     68: 
                     69:     lt_sk = ftok (config_file, 3);
                     70:     
                     71:     if (first_process) {
                     72: 
                     73:         semid_locktab = semget (lt_sk, 1, 0666 | IPC_CREAT);
                     74:         if (semid_locktab == -1) {
                     75:             fprintf (stderr, "locktab_init:  failed to create lock table semaphore\r\n");
                     76:             exit (1);
                     77:         }
                     78: 
                     79:         arg.val = 1;
                     80:         if (semctl (semid_locktab, 0, SETVAL, arg) == -1) {
                     81:             fprintf (stderr, "locktab_init:  failed to initialize lock table semaphore\r\n");
                     82:             exit (1);
                     83:         }
                     84:         
                     85:     }
                     86:     else {
                     87: 
                     88:         semid_locktab = semget (lt_sk, 1, 0);
                     89:         if (semid_locktab == -1) {
                     90:             fprintf (stderr, "locktab_init:  could not attach to lock table semaphore\r\n");
                     91:             exit (1);
                     92:         }
                     93:         
                     94:     }
                     95:     
                     96:     return;
                     97:     
                     98: }
                     99: 
                    100: short locktab_get_sem(void)
                    101: {
                    102:     int tries;
                    103:     struct sembuf s = {0, -1, 0};
                    104: 
                    105:     for (tries = 0; tries < 5; tries++) {
                    106: 
                    107:         if (semop (semid_locktab, &s, 1) != -1) {
                    108:             return TRUE;
                    109:         }
                    110: 
                    111:         sleep (1);
                    112: 
                    113:     }
                    114:     
                    115:     return FALSE;
                    116: }
                    117: 
                    118: void locktab_release_sem(void)
                    119: {
                    120:     struct sembuf s = {0, 1, 0};
                    121: 
                    122:     semop (semid_locktab, &s, 1);
                    123: 
                    124: }
                    125: 
                    126: void lock(char *lockarg, long time_out, char type)
                    127: {
                    128:     char *key = &(lockarg[1]);
                    129:     char a = lockarg[0];
                    130:     
                    131:     if (shm_config == NULL) {
                    132:         fprintf (stderr, "lock:  global LOCK operation attemped before shared memory available.\r\n");
                    133:         return;
                    134:     }
                    135: 
                    136:     switch (a) {
                    137: 
                    138:         case '+':
                    139:             locktab_increment (key, time_out, FALSE);
                    140:             break;
                    141:             
                    142:         case '-':
                    143:             locktab_decrement (key, time_out);
                    144:             break;
                    145:             
                    146:         case SP:
                    147:             locktab_unlock_all ();
                    148:             locktab_increment (key, time_out, TRUE);
                    149:             break;
                    150: 
                    151:         default:
                    152:             break;
                    153:             
                    154:     }
                    155: 
                    156:     return;
                    157:     
                    158: }
                    159: 
                    160: void locktab_increment(char *key, long lck_timeout, short old_lock)
                    161: {
                    162:     short lck_action;
                    163:     char chk_ns[256];
                    164:     int nref_ct = locktab_list_count (key);
                    165: 
                    166:     if (old_lock) {
                    167:         lck_action = lock_old;
                    168:     }
                    169:     else {
                    170:         lck_action = lock_inc;
                    171:     }
                    172: 
                    173:     strncpy (chk_ns, nsname, 256);
                    174:     
                    175:     switch (lck_timeout) {
                    176:             
                    177:         case -1: /* blocking lock (no timeout) */
                    178: 
                    179:             if (nref_ct > 1) {
                    180:                 /* this is a lock list */
                    181:                 char *nref;
                    182:                 char tmps[255];
                    183: 
                    184:                 int i;        
                    185:                 int successes = 0;
                    186:                 int attempts = 0;
                    187:                 int list_pos = 0;
                    188:                 
                    189:                 char *attempt_status = (char *) malloc (nref_ct * sizeof (char));
                    190:                 NULLPTRCHK(attempt_status,"locktab_increment");
                    191: 
                    192:                 for (i = 0; i < nref_ct; i++) attempt_status[i] = (char) FALSE;
                    193:                 
                    194:                 stcpy (tmps, key);
                    195:                 stcnv_m2c (tmps);                               
                    196:                 
                    197:                 nref = strtok (tmps, "\001\201");
                    198: 
                    199:                 do {
                    200:                     
                    201:                     list_pos = 0;                    
                    202:                     attempts = 0;
                    203:                     successes = 0;
                    204:                     
                    205:                     for (;;) {                    
                    206: 
                    207:                         attempts++;
                    208:                        
                    209:                         if (attempt_status[list_pos] == FALSE) {
                    210:                             
                    211:                             if (locktab_insert (nref) != NULL) {
                    212:                                 successes++;
                    213:                                 attempt_status[list_pos] = TRUE;
                    214:                             }
                    215:                             else {
                    216:                                 locktab_decrement (nref, -1L);
                    217:                                 attempt_status[list_pos] = FALSE;
                    218:                             }
                    219: 
                    220:                         }
                    221:                         
                    222:                         nref = strtok (NULL, "\001\201");                    
                    223:                         if (nref == NULL) break;
                    224: 
                    225:                         list_pos++;
                    226:                         
                    227:                     }                        
                    228: 
                    229:                 } while (successes < nref_ct);
                    230: 
                    231:                 free (attempt_status);
                    232:                 if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                    233:                 return;
                    234:                 
                    235:             }
                    236:             else {
                    237:                 for (;;) {
                    238:                     
                    239:                     if (locktab_insert (key) != NULL) {
                    240:                         if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                    241:                         return;
                    242:                     }
                    243:                     else {
                    244:                         sleep (1);
                    245:                     }
                    246:                 
                    247:                 }
                    248:             }
                    249: 
                    250:             
                    251:         case 0: /* lock that returns immediately */
                    252: 
                    253:             if (nref_ct > 1) {
                    254:                 /* this is a lock list */
                    255:                 char *nref;
                    256:                 char tmps[255];
                    257:                 
                    258:                 int successes = 0;
                    259:                 int attempts = 0;
                    260:                 
                    261:                 stcpy (tmps, key);
                    262:                 stcnv_m2c (tmps);                               
                    263:                 
                    264:                 nref = strtok (tmps, "\001\201");
                    265: 
                    266:                 for (;;) {                    
                    267:                     attempts++;
                    268: 
                    269:                     if (locktab_insert (nref) != NULL) {
                    270:                         successes++;
                    271:                     }
                    272:                     else {
                    273:                         locktab_decrement (nref, 0L);
                    274:                         test = 0;
                    275:                         return;
                    276:                     }
                    277:                     
                    278:                     nref = strtok (NULL, "\001\201");                    
                    279:                     if (nref == NULL) break;
                    280:                 }                        
                    281: 
                    282:                 test = 1;
                    283:                 if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);      
                    284:                 return;
                    285:               
                    286:             }
                    287:             else {
                    288:             
                    289:                 if (locktab_insert (key) != NULL) {
                    290:                     test = 1;
                    291:                     if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                    292:                 }
                    293:                 else {
                    294:                     test = 0;
                    295:                 }
                    296: 
                    297:             }
                    298: 
                    299:             break;
                    300: 
                    301:         case 1: /* special case: lock with 1-second timeout */
                    302: 
                    303:             if (locktab_insert (key) != NULL) {
                    304:                 test = 1;            
                    305:             }
                    306:             else {
                    307:                 sleep (1);
                    308: 
                    309:                 if (locktab_insert (key) != NULL) {
                    310:                     test = 1;
                    311:                     if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                    312:                 }
                    313:                 else {
                    314:                     test = 0;
                    315:                 }
                    316:             }
                    317: 
                    318:             return;
                    319:             
                    320:         default: /* lock with timeout */
                    321:         {
                    322:             time_t start_secs;
                    323:             time_t end_secs;
                    324:             time_t elapsed;
                    325:             locktab_ent_t *lck = NULL;
                    326:             
                    327:             start_secs = time (NULL);
                    328: 
                    329:             if (nref_ct > 1) {
                    330:                 /* this is a lock-list */
                    331:                 printf ("lock-list with timeout\r\n");
                    332:             }
                    333:             else {
                    334:                 
                    335:             
                    336:                 for (;;) {
                    337:                     
                    338:                     
                    339:                     lck = locktab_insert (key);
                    340:                     
                    341:                     end_secs = time (NULL);
                    342:                     elapsed = end_secs - start_secs;
                    343:                     
                    344:                     if (lck != NULL) {
                    345:                         test = 1;
                    346:                         return;
                    347:                     }
                    348:                     
                    349:                     if (elapsed >= lck_timeout) {
                    350:                         
                    351:                         if (lck == NULL) {
                    352:                             test = 0;          
                    353:                         }
                    354:                         else {
                    355:                             if (tp_level > 0) tp_add_op (TRUE, lck_action, key, chk_ns);
                    356:                             test = 1;
                    357:                         }
                    358:                         
                    359:                         return;
                    360:                         
                    361:                     }
                    362: 
                    363:                     sleep (1); /* prevent CPU pegging */
                    364:                 
                    365:                 } /* timeout loop */
                    366: 
                    367:                 return;
                    368:             }
                    369:             
                    370:         } /* lock with timeout */
                    371:     } /* switch (timeout) */
                    372:             
                    373:             
                    374: }
                    375: 
                    376: void locktab_decrement(char *key, long lck_timeout)
                    377: {
                    378: 
                    379:     locktab_ent_t *lck = locktab_find (key);
                    380:     
                    381:     if (lck != NULL) {
                    382: 
                    383:         if (tp_level > lck->tp_level) {
                    384:             merr_raise (M41);
                    385:             return;
                    386:         }
                    387:         
                    388:         if (lck->ct > 0) lck->ct--;       
                    389:         
                    390:         if (lck->ct == 0) {
                    391:             lck->owner_job = 0;
                    392:             strcpy (lck->namespace, "<REUSABLE>");
                    393: 
                    394:             ssvn_lock_remove (lck->nref);
                    395:         }
                    396: 
                    397:         if (lck->owner_job != 0) {
                    398:             ssvn_lock_add (lck->nref, lck->owner_job, lck->ct);
                    399:         }
                    400:         
                    401:     }
                    402: 
                    403:     if (lck_timeout > -1) test = 1;
                    404:     
                    405: }
                    406: 
                    407: void locktab_unlock_all(void)
                    408: {
                    409:     locktab_ent_t *lck;
                    410:     
                    411:     for (lck = shm_config->hdr->locktab_head; lck != NULL; lck = lck->next) {
                    412: 
                    413:         if (lck->owner_job == pid) {
                    414: 
                    415:             if (tp_level > lck->tp_level) {
                    416:                 merr_raise (M41);
                    417:                 return;
                    418:             }
                    419:             
                    420:             lck->ct = 0;
                    421:             lck->owner_job = 0;
                    422:             strcpy (lck->namespace, "<REUSABLE>");
                    423: 
                    424:             ssvn_lock_remove (lck->nref);
                    425:             
                    426:         }
                    427: 
                    428:     }       
                    429:     
                    430: }
                    431: 
                    432: locktab_ent_t *locktab_find(char *key)
                    433: {
                    434: 
                    435:     locktab_ent_t *lck;
                    436:     char chk_ns[255];
                    437: 
                    438:     if (key[1] == '%') {
1.6     ! snw       439:         snprintf (chk_ns, sizeof (chk_ns) - 1, "SYSTEM");
1.1       snw       440:     }
                    441:     else {
1.6     ! snw       442:         snprintf (chk_ns, sizeof (chk_ns) - 1, "%s", nsname);
1.1       snw       443:     }
                    444: 
                    445:     for (lck = shm_config->hdr->locktab_head; lck != NULL; lck = lck->next) {
                    446: 
                    447:         if ((stcmp (lck->nref, key) == 0) && (strcmp (lck->namespace, chk_ns) == 0)) {
                    448: 
                    449:             if (lck->owner_job != pid) {
                    450:                 return (locktab_ent_t *) NULL;
                    451:             }
                    452:             else {
                    453:                 return lck;
                    454:             }
                    455:             
                    456:         }
                    457:         
                    458:     }
                    459: 
                    460:     return (locktab_ent_t *) NULL;
                    461:     
                    462: }
                    463: 
                    464: locktab_ent_t *locktab_insert(char *key)
                    465: {
                    466:     locktab_ent_t *l;
                    467:     char chk_ns[255];
                    468: 
                    469:     freem_ref_t *ik;
                    470:     freem_ref_t *ok;
                    471:     
                    472:     ik = malloc (sizeof (freem_ref_t));
                    473:     NULLPTRCHK(ik,"locktab_insert");
                    474: 
                    475:     ik = mref_init (ik, MREF_RT_GLOBAL, "");
                    476: 
                    477:     ok = malloc (sizeof (freem_ref_t));
                    478:     NULLPTRCHK(ok,"locktab_insert");    
                    479: 
                    480:     ik = internal_to_mref (ik, key);
                    481:     
                    482:     if (key[1] == '%') {
1.6     ! snw       483:         snprintf (chk_ns, sizeof (chk_ns) - 1, "SYSTEM");
1.1       snw       484:     }
                    485:     else {
1.6     ! snw       486:         snprintf (chk_ns, sizeof (chk_ns) - 1, "%s", nsname);
1.1       snw       487:     }
                    488:     
                    489:     for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
                    490:         ok = mref_init (ok, MREF_RT_GLOBAL, "");
                    491:         ok = internal_to_mref (ok, l->nref);
                    492:         
                    493:         if (((stcmp (l->nref, key) == 0) || (mref_is_descendant (ok, ik) == TRUE)) && (strcmp (l->namespace, chk_ns) == 0)) {
                    494:             
                    495:             /* nref already owned by another job */
                    496:             if ((l->owner_job != pid) && (l->ct > 0)) {
                    497:                 free (ik);
                    498:                 free (ok);
                    499:                 return NULL;
                    500:             }
                    501:             else {
                    502:                 if ((mref_is_descendant (ok, ik)) && (pid == l->owner_job)) {
                    503:                     if (locktab_find (key) == NULL) goto new_insert;
                    504:                 }
                    505:                 
                    506:                 /* increment the lock and return */
                    507:                 l->ct++;
                    508: 
                    509:                 /* if this was a lock with a counter of zero belonging to another pid,
                    510:                  * re-use it and take ownership of it.
                    511:                  */
                    512:                 if (l->owner_job != pid) l->owner_job = pid;
                    513: 
                    514:                 l->tp_level = tp_level;
                    515:                 
                    516:                 ssvn_lock_add (l->nref, l->owner_job, l->ct);
                    517: 
                    518:                 free (ik);
                    519:                 free (ok);
                    520:                 
                    521:                 return l;
                    522:             }
                    523:             
                    524:         }
                    525: 
                    526:     }
                    527: 
                    528: new_insert:
                    529:     /* no lock exists for key: this is a new insert */            
                    530:     l = (locktab_ent_t *) shm_alloc (sizeof (locktab_ent_t));
                    531:     if (l == (locktab_ent_t *) NULL) {
                    532:         free (ik);
                    533:         free (ok);
                    534:         return (locktab_ent_t *) NULL;
                    535:     }
                    536: 
                    537:     stcpy (l->nref, key);
1.6     ! snw       538:     snprintf (l->namespace, sizeof (l->namespace) - 1, "%s", chk_ns);
1.1       snw       539:         
                    540:     l->owner_job = pid;
                    541:     l->ct = 1;
                    542:     
                    543:     l->next = shm_config->hdr->locktab_head;
                    544:     shm_config->hdr->locktab_head = l;
                    545: 
                    546:     ssvn_lock_add (l->nref, l->owner_job, l->ct);
                    547: 
                    548:     free (ik);
                    549:     free (ok);
                    550:     
                    551:     return l;
                    552: }
                    553: 
                    554: 
                    555: int locktab_count(char *key)
                    556: {
                    557:     locktab_ent_t *l;
                    558:     int ct = 0;
                    559: 
                    560:     for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
                    561:         if (stcmp (l->nref, key) == 0) ct++;
                    562:     }
                    563: 
                    564:     return ct;
                    565: }
                    566: 
                    567: int locktab_list_count(char *key)
                    568: {
                    569:     int i;
                    570:     int lct = 0;
                    571:     
                    572:     for (i = 0; i < stlen (key); i++) {
                    573:         if (key[i] == '\001') lct++;        
                    574:     }
                    575: 
                    576:     return lct;
                    577: }
                    578: 
                    579: unsigned long locktab_pages(void)
                    580: {
                    581: 
                    582:     locktab_ent_t *l;
                    583:     unsigned long bytes = 0;
                    584:     unsigned long pages = 0;
                    585:     float extra;
                    586:     
                    587:     for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
                    588:         bytes += sizeof (locktab_ent_t);
                    589:     }
                    590: 
                    591:     pages = bytes / PG_SIZE;
                    592:     extra = bytes % PG_SIZE;
                    593:     
                    594:     if (extra > 0) {
                    595:         pages++;
                    596:     }
                    597: 
                    598:     return pages;
                    599:   
                    600: }
                    601: 
                    602: unsigned long locktab_bytes(void)
                    603: {
                    604: 
                    605:     locktab_ent_t *l;
                    606:     unsigned int ct = 0;
                    607:     unsigned long bytes = 0;
                    608: 
                    609:     for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
                    610:         ct++;
                    611:         bytes += sizeof (locktab_ent_t);
                    612:     }
                    613: 
                    614:     return bytes;
                    615: 
                    616: }
                    617: 
                    618: void locktab_dump(void)
                    619: {
                    620:     
                    621:     locktab_ent_t *l;
                    622:     unsigned long ct = 0;
                    623:     unsigned long tot = 0;
                    624:     freem_ref_t *r;
                    625:     char *ref_ext;
                    626:     
                    627:     r = (freem_ref_t *) malloc (sizeof (freem_ref_t));
                    628:     NULLPTRCHK(r,"locktab_dump");
                    629: 
                    630:     ref_ext = (char *) malloc (STRLEN * sizeof (char));
                    631:     NULLPTRCHK(ref_ext,"locktab_dump");
                    632:     
                    633:     printf ("%-20s%-20s%-20s%s\r\n", "NAMESPACE", "PID", "COUNT", "KEY");
                    634:     printf ("%-20s%-20s%-20s%s\r\n", "---------", "---", "-----", "---");
                    635: 
                    636:     if (shm_config->hdr->locktab_head == NULL) {
                    637:         printf ("\r\n*** lock table empty ***\r\n");
                    638:         free (r);
                    639:         return;
                    640:     }
                    641: 
                    642:     
                    643:     for (l = shm_config->hdr->locktab_head; l != NULL; l = l->next) {
                    644: 
                    645:         mref_init (r, MREF_RT_GLOBAL, "");
                    646:         internal_to_mref (r, l->nref);
                    647:         mref_to_external (r, ref_ext);
                    648: 
                    649:         if (l->owner_job) {
                    650:             printf ("%-20s%-20d%-20d%s\r\n", l->namespace, l->owner_job, l->ct, ref_ext);
                    651:             ct++;
                    652:         }
                    653:         
                    654:         tot++;
                    655:         
                    656:     }
                    657: 
                    658:     printf ("\r\n\tActive LOCK table entries:            %ld\r\n", ct);
                    659:     printf (    "\tReusable LOCK table entries:          %ld\r\n", tot - ct);
                    660:     printf (    "\tShared memory pages:                  %ld\r\n", locktab_pages ());
                    661:     printf (    "\tShared memory bytes:                  %ld\r\n", locktab_bytes ());
                    662: 
                    663:     free (r);
                    664:     free (ref_ext);
                    665:     
                    666:     return;
                    667:     
                    668: }

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