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

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 {
        !           444:         snprintf (chk_ns, 255, nsname);
        !           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>