![]() ![]() | ![]() |
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: *
1.3 ! snw 18: * Author: Serena Willis <snw@coherent-logic.com>
1.1 snw 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 {
1.2 snw 444: snprintf (chk_ns, 255, "%s", nsname);
1.1 snw 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: }