Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include "gdk.h"
15 : #include "gdk_system.h"
16 :
17 : #define NN (3)
18 :
19 : volatile int timeout = 100; // set this to 0 during interactive debugging
20 :
21 : /* global state protected by a lock.
22 : *
23 : * Every worker thread has a number of permits and a number of ticks.
24 : * It sleeps on the condition variable, whenever it wakes it will
25 : * try to convert one permit into a tick, that is, permit-- and tick++.
26 : */
27 :
28 : MT_Lock lock = MT_LOCK_INITIALIZER(lock);
29 : MT_Cond condvar = MT_COND_INITIALIZER(the_condvar);
30 : struct state {
31 : MT_Id id;
32 : int ticks;
33 : int permits;
34 : bool terminate;
35 : bool terminated;
36 : } states[NN] = { {0} };
37 :
38 : /*
39 : * The main thread holds the lock so it can manipulate and verify the permits
40 : * and ticks. It uses this function to temporarily release the lock so the
41 : * workers get a chance to do their work. We give them a 100ms, which should be
42 : * plenty.
43 : *
44 : * If we cannot retake the lock after that interval we assume a worker thread
45 : * has gone astray while holding the lock.
46 : */
47 : static void
48 7 : let_run(void)
49 : {
50 7 : MT_lock_unset(&lock);
51 :
52 7 : MT_sleep_ms(100);
53 :
54 : // try to retake the lock. Make a few attempts before giving up.
55 7 : int attempts = 0;
56 7 : while (!MT_lock_try(&lock)) {
57 0 : if (timeout > 0 && ++attempts > timeout) {
58 0 : fprintf(stderr, "Can't get hold of the lock after %d attempts\n", attempts);
59 0 : fprintf(stderr, "If this is because you're running this program in a debugger,\n");
60 0 : fprintf(stderr, "try setting the timeout variable to 0.\n");
61 0 : abort();
62 : }
63 0 : MT_sleep_ms(10);
64 : }
65 7 : }
66 :
67 :
68 : static void
69 3 : worker(void *arg)
70 : {
71 3 : struct state *st = arg;
72 3 : int id = (int)(st - &states[0]);
73 3 : fprintf(stderr, "worker %d started, waiting to acquire lock\n", id);
74 :
75 3 : MT_lock_set(&lock);
76 3 : fprintf(stderr, "worker %d acquired lock\n", id);
77 21 : while (1) {
78 12 : if (st->terminate) {
79 3 : fprintf(stderr, "worker %d terminating\n", id);
80 3 : break;
81 : }
82 9 : if (st->permits > 0) {
83 6 : fprintf(stderr, "worker %d ticking\n", id);
84 6 : st->ticks++;
85 6 : st->permits--;
86 : }
87 9 : fprintf(stderr, "worker %d waiting on condvar\n", id);
88 9 : MT_cond_wait(&condvar, &lock);
89 9 : fprintf(stderr, "worker %d woke up\n", id);
90 : }
91 3 : st->terminated = true;
92 3 : MT_lock_unset(&lock);
93 3 : }
94 :
95 :
96 : static void
97 8 : check_impl(int line, int expected_sum_ticks, int expected_max_ticks, int expected_sum_permits)
98 : {
99 8 : int sum_ticks = 0;
100 8 : int max_ticks = -1;
101 8 : int sum_permits = 0;
102 :
103 32 : for (int i = 0; i < NN; i++) {
104 24 : sum_permits += states[i].permits;
105 24 : int ticks = states[i].ticks;
106 24 : sum_ticks += ticks;
107 24 : if (ticks > max_ticks)
108 : max_ticks = ticks;
109 : }
110 :
111 8 : fprintf(stderr, "On line %d: (sum_ticks, max_ticks, sum_permits) = (%d, %d, %d)\n",
112 : line,
113 : sum_ticks, max_ticks, sum_permits);
114 :
115 8 : bool good = true;
116 8 : good &= (sum_ticks == expected_sum_ticks);
117 8 : if (expected_max_ticks >= 0)
118 5 : good &= (max_ticks == expected_max_ticks);
119 8 : good &= (sum_permits == expected_sum_permits);
120 8 : if (good)
121 8 : return;
122 :
123 0 : if (expected_max_ticks >= 0) {
124 0 : fprintf(stderr, "MISMATCH: expected (%d, %d, %d)\n",
125 : expected_sum_ticks, expected_max_ticks, expected_sum_permits);
126 : } else {
127 0 : fprintf(stderr, "MISMATCH: expected (%d, ?, %d)\n",
128 : expected_sum_ticks, expected_sum_permits);
129 : }
130 :
131 0 : for (int i = 0; i < NN; i++) {
132 0 : fprintf(stderr, "worker %d: ticks=%d permits=%d\n", i, states[i].ticks, states[i].permits);
133 : }
134 0 : abort();
135 : }
136 :
137 : #define check(expected_sum, expected_max, expected_permits) check_impl(__LINE__, expected_sum, expected_max, expected_permits)
138 :
139 : int
140 1 : main(void)
141 : {
142 1 : MT_thread_init();
143 :
144 : // All code in this function runs while we hold the lock.
145 : // From time to time we call let_run() to allow the worker threads to obtain it.
146 1 : MT_lock_set(&lock);
147 :
148 1 : fprintf(stderr, "-- Initially, everything is zero\n");
149 1 : check(0, 0, 0);
150 :
151 1 : fprintf(stderr, "\n-- Starting the worker threads\n");
152 4 : for (int i = 0; i < NN; i++) {
153 3 : struct state *st = &states[i];
154 3 : MT_create_thread(&st->id, worker, st, MT_THR_JOINABLE, "workerXXXX");
155 : }
156 1 : MT_sleep_ms(100);
157 :
158 1 : fprintf(stderr, "\n-- Now allow the workers to take the lock, they should enter their main loop\n");
159 1 : let_run();
160 1 : check(0, 0, 0);
161 :
162 1 : fprintf(stderr, "\n-- All threads get a permit but nothing happens because we haven't touched the condvar\n");
163 4 : for (int i = 0; i < NN; i++)
164 3 : states[i].permits = 1;
165 1 : let_run();
166 1 : check(0, 0, NN);
167 :
168 1 : fprintf(stderr, "\n-- Now we broadcast on the condvar. All should wake\n");
169 1 : MT_cond_broadcast(&condvar);
170 1 : let_run();
171 1 : check(NN, 1, 0);
172 :
173 1 : fprintf(stderr, "\n-- Now we give each of them %d permits\n", NN);
174 4 : for (int i = 0; i < NN; i++) {
175 3 : states[i].ticks = 0;
176 3 : states[i].permits = NN;
177 : }
178 1 : check(0, 0, NN * NN);
179 :
180 : // Note: counting from 1 instead of 0
181 4 : for (int i = 1; i <= NN; i++) {
182 3 : fprintf(stderr, "\n-- [%d] Signal one, don't know which one it will be\n", i);
183 3 : MT_cond_signal(&condvar);
184 3 : let_run();
185 3 : check(i, -1, NN * NN - i);
186 : }
187 :
188 :
189 1 : fprintf(stderr, "\n-- Telling them all to quit\n");
190 4 : for (int i = 0; i < NN; i++) {
191 3 : states[i].terminate = true;
192 : }
193 1 : MT_cond_broadcast(&condvar);
194 1 : let_run();
195 :
196 4 : for (int i = 0; i < NN; i++) {
197 3 : fprintf(stderr, "-- Joining worker %d\n", i);
198 3 : MT_join_thread(states[i].id);
199 3 : fprintf(stderr, "-- Joined worker %d\n", i);
200 : }
201 1 : fprintf(stderr, "\n-- Joined all, exiting\n");
202 :
203 1 : return 0;
204 : }
|