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