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