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