Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | Copyright 2025 Joel Svensson svenssonjoel@yahoo.se | ||
3 | |||
4 | This program is free software: you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation, either version 3 of the License, or | ||
7 | (at your option) any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include "extensions/mutex_extensions.h" | ||
19 | |||
20 | #include "extensions.h" | ||
21 | #include "eval_cps.h" | ||
22 | |||
23 | #ifdef LBM_OPT_MUTEX_EXTENSIONS_SIZE | ||
24 | #pragma GCC optimize ("-Os") | ||
25 | #endif | ||
26 | #ifdef LBM_OPT_MUTEX_EXTENSIONS_SIZE_AGGRESSIVE | ||
27 | #pragma GCC optimize ("-Oz") | ||
28 | #endif | ||
29 | |||
30 | |||
31 | // This file provides a mutual exclusion feature based on the block_from_extension | ||
32 | // mechanism present in LBM. | ||
33 | // | ||
34 | // Three operations are provided: | ||
35 | // - mutex-create | ||
36 | // - mutex-lock | ||
37 | // - mutex-unlock | ||
38 | // | ||
39 | // It is strongly adviced to use these as a low-level interface and then to wrap | ||
40 | // them up in a (with-mutex-do mutex expr) that implements the locking and unlocking | ||
41 | // so that no mutex is left dangling. | ||
42 | // | ||
43 | // with-mutex-do can be implemented as | ||
44 | // | ||
45 | // (define with-mutex-do (lambda (mutex quoted-expr) | ||
46 | // (progn (mutex-lock mutex) | ||
47 | // (eval quoted-expr) | ||
48 | // (mutex-unlock mutex) | ||
49 | // | ||
50 | // | ||
51 | // The mutex object is a dotted pair (ls . last) which contains | ||
52 | // two references into a single list, implementing a O(1)-insert-last O(1)-remove-first | ||
53 | // queue. At the surface though, it is a regular lisp dotted pair that | ||
54 | // can be destroyed with standard lisp functionality, no protection! | ||
55 | // When a good replacement for "Custom types" is invented this will be improved. | ||
56 | |||
57 | 306 | bool is_mutex(lbm_value v) { | |
58 | // true if it is somewhat likely that v is a mutex. | ||
59 |
2/2✓ Branch 0 taken 299 times.
✓ Branch 1 taken 7 times.
|
605 | bool res = (lbm_is_cons(v) && |
60 |
3/4✓ Branch 0 taken 152 times.
✓ Branch 1 taken 147 times.
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
|
299 | (!lbm_is_symbol_nil(lbm_car(v)) || lbm_is_symbol_nil(lbm_cdr(v)))); // car == nil -> cdr == nil |
61 | // potentially add a clause | ||
62 | // car == (cons a b) -> cdr == (cons c nil) | ||
63 | 306 | return res; | |
64 | } | ||
65 | |||
66 | 299 | bool is_mutex_unlocked(lbm_value v) { | |
67 | 299 | return (lbm_is_symbol_nil(lbm_car(v))); | |
68 | } | ||
69 | |||
70 | 148 | void enqueue_cid(lbm_value mutex, lbm_value cid_pair) { | |
71 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | if (lbm_is_symbol_nil(lbm_car(mutex))) { |
72 | 148 | lbm_set_car(mutex, cid_pair); | |
73 | 148 | lbm_set_cdr(mutex, cid_pair); | |
74 | } else { | ||
75 | ✗ | lbm_value last = lbm_cdr(mutex); | |
76 | ✗ | lbm_set_cdr(last, cid_pair); | |
77 | ✗ | lbm_set_cdr(mutex, cid_pair); | |
78 | } | ||
79 | 148 | } | |
80 | |||
81 | 147 | bool dequeue_cid(lbm_value mutex, lbm_value cid) { | |
82 | 147 | bool res = false; | |
83 |
1/2✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
|
147 | if (lbm_is_cons(lbm_car(mutex))) { |
84 | 147 | lbm_value locked_cid = lbm_car(lbm_car(mutex)); | |
85 |
1/2✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
|
147 | if (locked_cid == cid) { // no decoding |
86 | 147 | res = true; | |
87 | 147 | lbm_value head = lbm_car(mutex); | |
88 | 147 | lbm_value last = lbm_cdr(mutex); | |
89 |
1/2✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
|
147 | if (head == last) { // one element |
90 | 147 | lbm_set_car(mutex, ENC_SYM_NIL); | |
91 | 147 | lbm_set_cdr(mutex, ENC_SYM_NIL); | |
92 | } else { | ||
93 | ✗ | lbm_set_car(mutex, lbm_cdr(head)); | |
94 | } | ||
95 | } | ||
96 | } | ||
97 | 147 | return res; | |
98 | } | ||
99 | |||
100 | 147 | lbm_value head_of_queue(lbm_value mutex) { | |
101 | 147 | lbm_value res = ENC_SYM_NIL; | |
102 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 147 times.
|
147 | if (lbm_is_cons(lbm_car(mutex))) { |
103 | ✗ | res = lbm_car(lbm_car(mutex)); | |
104 | } | ||
105 | 147 | return res; | |
106 | } | ||
107 | |||
108 | 102 | static lbm_value ext_mutex_create(lbm_value *args, lbm_uint argn) { | |
109 | (void) args; | ||
110 | (void) argn; | ||
111 | 102 | return lbm_cons(ENC_SYM_NIL, ENC_SYM_NIL); | |
112 | } | ||
113 | |||
114 | 154 | static lbm_value ext_mutex_lock(lbm_value *args, lbm_uint argn) { | |
115 | 154 | lbm_value res = ENC_SYM_TERROR; | |
116 |
4/4✓ Branch 0 taken 152 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 148 times.
✓ Branch 3 taken 4 times.
|
154 | if (argn == 1 && is_mutex(args[0])) { |
117 | 148 | lbm_cid cid = lbm_get_current_cid(); | |
118 | 148 | lbm_value cid_pair = lbm_cons(lbm_enc_i(cid), ENC_SYM_NIL); | |
119 | 148 | res = cid_pair; // Return the error from cons if failed. | |
120 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | if (lbm_is_cons(cid_pair)) { |
121 | 148 | res = ENC_SYM_TRUE; | |
122 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | if (is_mutex_unlocked(args[0])) { |
123 | 148 | enqueue_cid(args[0], cid_pair); | |
124 | } else { | ||
125 | ✗ | enqueue_cid(args[0], cid_pair); | |
126 | ✗ | lbm_block_ctx_from_extension(); | |
127 | } | ||
128 | } | ||
129 | } | ||
130 | 154 | return res; | |
131 | } | ||
132 | |||
133 | 156 | static lbm_value ext_mutex_unlock(lbm_value *args, lbm_uint argn) { | |
134 | 156 | lbm_value res = ENC_SYM_TERROR; | |
135 |
4/4✓ Branch 0 taken 154 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 151 times.
✓ Branch 3 taken 3 times.
|
156 | if (argn == 1 && is_mutex(args[0])) { |
136 | 151 | res = ENC_SYM_EERROR; // no mutex is locked! | |
137 |
2/2✓ Branch 0 taken 147 times.
✓ Branch 1 taken 4 times.
|
151 | if (!is_mutex_unlocked(args[0])) { |
138 | 147 | lbm_cid cid = lbm_get_current_cid(); // this cid should be top of queue! | |
139 | // otherwise error | ||
140 |
1/2✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
|
147 | if (dequeue_cid(args[0], lbm_enc_i(cid))) { |
141 | 147 | lbm_value h = head_of_queue(args[0]); | |
142 | 147 | res = ENC_SYM_TRUE; | |
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 147 times.
|
147 | if (!lbm_is_symbol_nil(h)) { |
144 | ✗ | lbm_cid unblock = lbm_dec_i(h); | |
145 | ✗ | lbm_unblock_ctx_unboxed(unblock, ENC_SYM_TRUE); | |
146 | } | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | 156 | return res; | |
151 | } | ||
152 | |||
153 | |||
154 | 44260 | void lbm_mutex_extensions_init(void) { | |
155 | 44260 | lbm_add_extension("mutex-create", ext_mutex_create); | |
156 | 44260 | lbm_add_extension("mutex-lock", ext_mutex_lock); | |
157 | 44260 | lbm_add_extension("mutex-unlock", ext_mutex_unlock); | |
158 | 44260 | } | |
159 |