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

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

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