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