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