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