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