Annotation of freem/src/transact.c, revision 1.8
1.1 snw 1: /*
1.8 ! snw 2: * $Id: transact.c,v 1.7 2025/04/09 19:52:02 snw Exp $
1.1 snw 3: * FreeM transaction processing support
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) 2020, 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: transact.c,v $
1.8 ! snw 27: * Revision 1.7 2025/04/09 19:52:02 snw
! 28: * Eliminate as many warnings as possible while building with -Wall
! 29: *
1.7 snw 30: * Revision 1.6 2025/03/24 04:13:12 snw
31: * Replace action macro dat with fra_dat to avoid symbol conflict on OS/2
32: *
1.6 snw 33: * Revision 1.5 2025/03/24 02:54:47 snw
34: * Transaction compat fixes for OS/2
35: *
1.5 snw 36: * Revision 1.4 2025/03/09 19:50:47 snw
37: * Second 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 <string.h>
45: #include <stddef.h>
46: #include <stdio.h>
47: #include <stdlib.h>
48: #include <unistd.h>
49: #include <errno.h>
50:
51: #include "mpsdef.h"
52: #include "transact.h"
53: #include "iftab.h"
54: #include "journal.h"
55: #include "shmmgr.h"
56: #include "mref.h"
57: #include "tp_check.h"
58:
59: #define FALSE 0
60: #define TRUE 1
61:
1.5 snw 62: #if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__OS2__)
1.1 snw 63: union semun {
64: int val; /* Value for SETVAL */
65: struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
66: unsigned short *array; /* Array for GETALL, SETALL */
67: struct seminfo *__buf; /* Buffer for IPC_INFO
68: (Linux-specific) */
69: };
70: #endif
71:
72: void m_log (int, const char *);
73:
74: int semid_tp;
75: int tp_committing = FALSE;
76: int tp_level = 0;
77: tp_transaction transactions[TP_MAX_NEST];
78:
79:
80: void tp_init(void)
81: {
82: union semun arg;
83: key_t tp_sk;
84:
85: tp_sk = ftok (config_file, 4);
86:
87: if (first_process) {
88:
89: semid_tp = semget (tp_sk, 1, 0666 | IPC_CREAT);
90: if (semid_tp == -1) {
91: fprintf (stderr, "tp_init: failed to create transaction processing semaphore [errno %d]\r\n", errno);
92: exit (1);
93: }
94:
95: arg.val = 1;
96: if (semctl (semid_tp, 0, SETVAL, arg) == -1) {
97: fprintf (stderr, "tp_init: failed to initialize transaction processing semaphore\r\n");
98: exit (1);
99: }
100:
101: }
102: else {
103:
104: semid_tp = semget (tp_sk, 1, 0);
105: if (semid_tp == -1) {
106: fprintf (stderr, "tp_init: could not attach to transaction processing semaphore [errno %d]\r\n", errno);
107: exit (1);
108: }
109:
110: }
111:
112: return;
113:
114: }
115:
116: short tp_get_sem(void)
117: {
118: int tries;
119: struct sembuf s = {0, -1, 0};
120:
121: char msgbuf[100];
122:
123: snprintf (msgbuf, 99, "tp_get_sem: process %d attempting to acquire transaction processing semaphore", pid);
124: m_log (1, msgbuf);
125:
126:
127: /* our process already owns the semaphore */
128: if (shm_config->hdr->tp_owner == pid) {
129:
130: snprintf (msgbuf, 99, "tp_get_sem: process %d increments transaction processing semaphore counter", pid);
131: m_log (1, msgbuf);
132:
133:
134: if (first_process == TRUE) {
135: fprintf (stderr, "tp_get_sem: daemon process increments critical section counter\r\n");
136: }
137:
138:
139: shm_config->hdr->tp_semctr++;
140:
141: return TRUE;
142: }
143:
144: if (first_process == TRUE) {
145: fprintf (stderr, "tp_get_sem: daemon process enters critical section\r\n");
146: }
147:
148:
149: for (tries = 0; tries < 10; tries++) {
150:
151: if (semop (semid_tp, &s, 1) != -1) {
152: shm_config->hdr->tp_owner = pid;
153: shm_config->hdr->tp_semctr = 1;
154:
155: snprintf (msgbuf, 99, "tp_get_sem: process %d takes transaction processing semaphore", pid);
156: m_log (1, msgbuf);
157:
158:
159: if (first_process == TRUE) {
160: fprintf (stderr, "tp_get_sem: daemon process takes transaction processing semaphore\r\n");
161: }
162:
163: return TRUE;
164: }
165:
166: snprintf (msgbuf, 99, "tp_get_sem: process %d attempting to acquire transaction processing semaphore (tries = %d)", pid, tries);
167: m_log (1, msgbuf);
168:
169:
170: sleep (1);
171:
172: }
173:
174: return FALSE;
175:
176: }
177:
178: void tp_release_sem(void)
179: {
180:
181: char msgbuf[100];
182:
183: if (shm_config->hdr->tp_semctr == 1) {
184:
185: struct sembuf s = {0, 1, 0};
186:
187: if (first_process == TRUE) {
188: fprintf (stderr, "tp_release_sem: daemon process leaves critical section\r\n");
189: }
190:
191:
192: shm_config->hdr->tp_semctr = 0;
193: shm_config->hdr->tp_owner = 0;
194:
195: if (first_process == TRUE) {
196: fprintf (stderr, "tp_release_sem: daemon process relinquishes transaction processing semaphore\r\n");
197: }
198:
199:
200: snprintf (msgbuf, 99, "tp_get_sem: process %d releases transaction processing semaphore", pid);
201: m_log (1, msgbuf);
202:
203:
204: semop (semid_tp, &s, 1);
205:
206: }
207: else {
208:
209: if (first_process == TRUE) {
210: fprintf (stderr, "tp_release_sem: daemon process decrements critical section counter\r\n");
211: }
212:
213: snprintf (msgbuf, 99, "tp_get_sem: process %d decrements transaction processing semaphore counter", pid);
214: m_log (1, msgbuf);
215:
216: shm_config->hdr->tp_semctr--;
217: }
218:
219:
220: }
221:
222: int tp_tstart(char *tp_id, short serial, short restartable, char **sym_save)
223: {
224: if (tp_level == TP_MAX_NEST) {
225: char m[256];
226:
227: snprintf (m, 256, "Attempt to exceed TP_MAX_NEST. Transaction aborted.\r\n\201");
228: write_m (m);
229:
230: return FALSE;
231: }
232:
233: if (((serial == TRUE) && (tp_get_sem () == TRUE)) ||
234: (serial == FALSE)) {
235:
236: tp_level++;
237:
238: jnl_ent_write (JNLA_TSTART, "", "");
239:
240: strcpy (transactions[tp_level].tp_id, tp_id);
241:
242: transactions[tp_level].serial = serial;
243: transactions[tp_level].restartable = restartable;
244:
245: transactions[tp_level].opcount = 0;
246:
247: return TRUE;
248:
249: }
250: else {
251: fprintf (stderr, "tp_tstart: could not get transaction processing semaphore\r\n");
252: exit (1);
253: }
254:
255:
256: }
257:
258: int tp_add_op(short islock, short action, char *key, char *data)
259: {
260: int oc;
261: freem_ref_t *gr;
262:
263: gr = (freem_ref_t *) malloc (sizeof (freem_ref_t));
264: NULLPTRCHK(gr,"tp_add_op");
265:
266: mref_init (gr, MREF_RT_GLOBAL, "");
267: internal_to_mref (gr, key);
268:
269: if (transactions[tp_level].opcount == TP_MAX_OPS) {
270: char m[256];
271:
272: snprintf (m, 256, "Attempt to exceed TP_MAX_OPS at transaction level %d. Rolling back.\r\n\201", tp_level);
273: write_m (m);
274:
275: free (gr);
276:
277: tp_trollback (1);
278: tp_cleanup (1);
279:
280: if (transactions[tp_level].serial == TRUE) {
281: tp_release_sem();
282: }
283:
284: return FALSE;
285: }
286:
287: /* update transaction-in-flight symbol table */
288: switch (action) {
289:
290: case lock_inc:
291: case lock_dec:
292: case lock_old:
293: case set_sym:
294: iftab_insert (action, key, data, tp_level);
295: break;
296:
297: case kill_sym:
298: iftab_kill (key);
299: break;
300:
301: }
302:
303: if (transactions[tp_level].serial == TRUE) {
304: /* mark the global for checkpointing */
305: cptab_insert (tp_level, gr->name);
306: }
307:
308: free (gr);
309:
310: transactions[tp_level].opcount = transactions[tp_level].opcount + 1;
311:
312: oc = transactions[tp_level].opcount;
313:
314: transactions[tp_level].ops[oc].is_lock = islock;
315: transactions[tp_level].ops[oc].action = action;
316:
1.2 snw 317: stcpy ((char *) &transactions[tp_level].ops[oc].key, key);
318: stcpy ((char *) &transactions[tp_level].ops[oc].data, data);
1.1 snw 319:
320:
321: return TRUE;
322: }
323:
324: int tp_tcommit(void)
325: {
326: register int i;
327: short is_serial = transactions[tp_level].serial;
328:
329: tp_committing = TRUE;
330:
331: if (is_serial) {
332: /* checkpoint all globals involved in transaction */
333: cptab_precommit (tp_level);
334: }
335:
336: for(i = 1; i <= transactions[tp_level].opcount; i++) {
337:
338: if (transactions[tp_level].ops[i].is_lock == FALSE) {
339: global (transactions[tp_level].ops[i].action, transactions[tp_level].ops[i].key, transactions[tp_level].ops[i].data);
340:
341: if (merr () > OK) goto commit_error;
342:
343: }
344:
345: }
346:
347: jnl_ent_write (JNLA_TCOMMIT, "\201", "\201");
348:
349: if (is_serial) {
350: cptab_postcommit (tp_level);
351: }
352:
353: goto commit_done;
354:
355: commit_error:
356:
357: tp_trollback (1);
358:
359: commit_done:
360:
361: tp_cleanup (1);
362:
363: tp_committing = FALSE;
364:
365: if (is_serial) {
366: tp_release_sem ();
367: }
368:
369: return TRUE;
370: }
371:
372: int tp_cleanup(int levels)
373: {
374: register int i;
375:
376: for (i = tp_level; i >= (((tp_level - levels) >= 0) ? tp_level - levels : 0); i--) {
377: iftab_pop_tlevel (i);
378: }
379:
380: tp_level = ((tp_level - levels) >= 0) ? tp_level - levels : 0;
381:
382: return TRUE;
383: }
384:
385: int tp_trollback(int levels)
386: {
387: register int i;
388: register int j;
389:
390: for (i = tp_level; i >= (((tp_level - levels) >= 0) ? tp_level - levels : 0); i--) {
391:
392: for (j = 1; j <= transactions[i].opcount; j++) {
393:
394: if (transactions[i].ops[j].is_lock == TRUE) {
395: locktab_decrement (transactions[i].ops[j].key, -1);
396: }
397:
398: }
399:
400: if (transactions[i].serial == TRUE) {
401: cptab_rollback (i);
402: }
403:
404: }
405:
406:
407: return TRUE;
408: }
409:
410: int tp_trestart(void)
411: {
412: return 0;
413: }
414:
415: void tp_tdump(void)
416: {
417:
418: int i, j;
419:
420: char tkey[256];
421: char tdata[256];
422: char tact[256];
423:
424: set_io (UNIX);
425:
426: if (tp_level == 0) {
427: printf("No transaction is active.\n");
428:
429: return;
430: }
431:
432: for(i = 1; i <= tp_level; i++) {
433:
434: if(i == tp_level) {
435: printf(" $TLEVEL %d*\n", i);
436: }
437: else {
438: printf(" $TLEVEL %d\n", i);
439: }
440:
441: printf(" Operations for Transaction ID: %s [%s%s]\n",
442: transactions[i].tp_id,
443: ((transactions[i].restartable == TRUE) ? "RESTARTABLE" : "NON-RESTARTABLE"),
444: ((transactions[i].serial == TRUE) ? " SERIAL" : " BATCH"));
445:
446: printf ("\n %-10s%-15s%-15s\n", "OP. NO.", "ACTION", "KEY/DATA");
447: printf (" %-10s%-15s%-15s\n", "-------", "------", "--------");
448:
449:
450: for(j = 1; j <= transactions[i].opcount; j++) {
451: stcpy (tkey, transactions[i].ops[j].key);
452: stcnv_m2c (tkey);
453: stcpy (tdata, transactions[i].ops[j].data);
454: stcnv_m2c (tdata);
455:
456: tp_get_op_name (tact, transactions[i].ops[j].action);
457:
458: if (transactions[i].ops[j].action == set_sym) {
459: printf (" %-10d%-15s%s=%s\n", j, tact, tkey, tdata);
460: }
461: else {
462: printf (" %-10d%-15s%s\n", j, tact, tkey);
463: }
464:
465: }
466:
467: cptab_dump (i);
468:
469: }
470:
471:
472: set_io (MUMPS);
473: }
474:
475: void tp_get_op_name(char *buf, const short action)
476: {
477: switch (action) {
478:
479: case set_sym:
480: strcpy (buf, "SET");
481: break;
482:
483: case killone:
484: case kill_sym:
485: case kill_all:
486: case killexcl:
487: strcpy (buf, "KILL");
488: break;
489:
490: case new_sym:
491: case new_all:
492: case newexcl:
493: strcpy (buf, "NEW");
494: break;
495:
496: case get_sym:
497: strcpy (buf, "GET");
498: break;
499:
1.6 snw 500: case fra_dat:
1.1 snw 501: strcpy (buf, "$DATA");
502: break;
503:
504: case fra_order:
505: strcpy (buf, "$ORDER");
506: break;
507:
508: case fra_query:
509: case bigquery:
510: strcpy (buf, "$QUERY");
511: break;
512:
513: case getinc:
514: strcpy (buf, "$INCREMENT");
515: break;
516:
517: case getnext:
518: strcpy (buf, "$NEXT");
519: break;
520:
521: case lock_inc:
522: strcpy (buf, "LOCK (INCR)");
523: break;
524:
525: case lock_old:
526: strcpy (buf, "LOCK (TRAD)");
527: break;
528:
529: }
530:
531: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>