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