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