c++: accepts-invalid with =delete("") [PR111840]
[official-gcc.git] / libgm2 / libm2iso / RTco.cc
blob17e8010a496a31c0d0091e2c071cd4edb0517229
1 /* RTco.cc provides minimal access to thread primitives.
3 Copyright (C) 2019-2022 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
11 any later version.
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. */
27 #include "config.h"
28 #include <unistd.h>
29 #include <pthread.h>
30 #include <sys/select.h>
31 #include <stdlib.h>
32 #include <m2rts.h>
33 #include <cstdio>
35 #define EXPORT(FUNC) m2iso ## _RTco_ ## FUNC
36 #define M2EXPORT(FUNC) m2iso ## _M2_RTco_ ## FUNC
37 #define M2LIBNAME "m2iso"
39 /* This implementation of RTco.cc uses a single lock for mutex across
40 the whole module. It also forces context switching between threads
41 in transfer by combining an implementation of wait and signal.
43 All semaphores are implemented using the same mutex lock and
44 separate condition variables. */
46 #undef TRACEON
48 #define POOL
49 #define SEM_POOL 10000
50 #define THREAD_POOL 10000
52 #define _GTHREAD_USE_COND_INIT_FUNC
53 #include "gthr.h"
55 /* Ensure that ANSI conform stdio is used. This needs to be set
56 before any system header file is included. */
57 #if defined __MINGW32__
58 #define _POSIX 1
59 #define gm2_printf gnu_printf
60 #else
61 #define gm2_printf __printf__
62 #endif
64 #if defined(TRACEON)
65 #define tprintf printf
66 #else
67 #define tprintf(...)
68 #endif
71 typedef struct threadCB_s
73 void (*proc) (void);
74 pthread_t p;
75 int tid; /* The thread id. */
76 unsigned int interruptLevel;
77 __gthread_cond_t run_counter; /* Used to block the thread and force
78 a context switch. */
79 int value; /* Count 0 or 1. */
80 bool waiting; /* Is this thread waiting on the run_counter? */
81 } threadCB;
84 typedef struct threadSem_s
86 __gthread_cond_t counter;
87 bool waiting;
88 int sem_value;
89 } threadSem;
91 static unsigned int nThreads = 0;
92 static threadCB *threadArray = NULL;
93 static unsigned int nSemaphores = 0;
94 static threadSem **semArray = NULL;
96 /* These are used to lock the above module data structures. */
97 static __gthread_mutex_t lock; /* This is the only mutex for
98 the whole module. */
99 static int initialized = false;
101 extern "C" int EXPORT(init) (void);
103 extern "C" void
104 M2EXPORT(dep) (void)
108 extern "C" void
109 M2EXPORT(init) (int argc, char *argv[], char *envp[])
113 extern "C" void
114 M2EXPORT(fini) (int argc, char *argv[], char *envp[])
119 static void
120 initSem (threadSem *sem, int value)
122 __GTHREAD_COND_INIT_FUNCTION (&sem->counter);
123 sem->waiting = false;
124 sem->sem_value = value;
127 static void
128 waitSem (threadSem *sem)
130 __gthread_mutex_lock (&lock);
131 if (sem->sem_value == 0)
133 sem->waiting = true;
134 __gthread_cond_wait (&sem->counter, &lock);
135 sem->waiting = false;
137 else
138 sem->sem_value--;
139 __gthread_mutex_unlock (&lock);
142 static void
143 signalSem (threadSem *sem)
145 __gthread_mutex_lock (&lock);
146 if (sem->waiting)
147 __gthread_cond_signal (&sem->counter);
148 else
149 sem->sem_value++;
150 __gthread_mutex_unlock (&lock);
153 extern "C" void
154 EXPORT(wait) (int sid)
156 EXPORT(init) ();
157 tprintf ("wait %d\n", sid);
158 waitSem (semArray[sid]);
161 extern "C" void
162 EXPORT(signal) (int sid)
164 EXPORT(init) ();
165 tprintf ("signal %d\n", sid);
166 signalSem (semArray[sid]);
169 static int
170 newSem (void)
172 #if defined(POOL)
173 semArray[nSemaphores]
174 = (threadSem *)malloc (sizeof (threadSem));
175 nSemaphores += 1;
176 if (nSemaphores == SEM_POOL)
177 m2iso_M2RTS_HaltC ("too many semaphores created",
178 __FILE__, __FUNCTION__, __LINE__);
179 #else
180 threadSem *sem
181 = (threadSem *)malloc (sizeof (threadSem));
183 /* We need to be careful when using realloc as the lock (semaphore)
184 operators use the semaphore address. So we keep an array of pointer
185 to semaphores. */
186 if (nSemaphores == 0)
188 semArray = (threadSem **)malloc (sizeof (sem));
189 nSemaphores = 1;
191 else
193 nSemaphores += 1;
194 semArray = (threadSem **)realloc (semArray,
195 sizeof (sem) * nSemaphores);
197 semArray[nSemaphores - 1] = sem;
198 #endif
199 return nSemaphores - 1;
202 static int
203 initSemaphore (int value)
205 int sid = newSem ();
207 initSem (semArray[sid], value);
208 tprintf ("%d = initSemaphore (%d)\n", sid, value);
209 return sid;
212 extern "C" int
213 EXPORT(initSemaphore) (int value)
215 int sid;
217 tprintf ("initSemaphore (%d) called\n", value);
218 EXPORT(init) ();
219 tprintf ("about to access lock\n");
220 __gthread_mutex_lock (&lock);
221 sid = initSemaphore (value);
222 __gthread_mutex_unlock (&lock);
223 return sid;
226 static int
227 currentThread (void)
229 int tid;
231 for (tid = 0; tid < nThreads; tid++)
232 if (pthread_self () == threadArray[tid].p)
233 return tid;
234 m2iso_M2RTS_HaltC ("failed to find currentThread",
235 __FILE__, __FUNCTION__, __LINE__);
238 extern "C" int
239 EXPORT(currentThread) (void)
241 int tid;
243 EXPORT(init) ();
244 __gthread_mutex_lock (&lock);
245 tid = currentThread ();
246 tprintf ("currentThread %d\n", tid);
247 __gthread_mutex_unlock (&lock);
248 return tid;
251 /* currentInterruptLevel returns the interrupt level of the current thread. */
253 extern "C" unsigned int
254 EXPORT(currentInterruptLevel) (void)
256 EXPORT(init) ();
257 __gthread_mutex_lock (&lock);
258 int current = currentThread ();
259 tprintf ("currentInterruptLevel %d\n",
260 threadArray[current].interruptLevel);
261 int level = threadArray[current].interruptLevel;
262 __gthread_mutex_unlock (&lock);
263 return level;
266 /* turninterrupts returns the old interrupt level and assigns the
267 interrupt level to newLevel. */
269 extern "C" unsigned int
270 EXPORT(turnInterrupts) (unsigned int newLevel)
272 EXPORT(init) ();
273 __gthread_mutex_lock (&lock);
274 int current = currentThread ();
275 unsigned int old = threadArray[current].interruptLevel;
276 tprintf ("turnInterrupts from %d to %d\n", old, newLevel);
277 threadArray[current].interruptLevel = newLevel;
278 __gthread_mutex_unlock (&lock);
279 return old;
282 static void
283 never (void)
285 m2iso_M2RTS_HaltC ("the main thread should never call here",
286 __FILE__, __FUNCTION__, __LINE__);
289 static void *
290 execThread (void *t)
292 threadCB *tp = (threadCB *)t;
294 tprintf ("exec thread tid = %d coming to life\n", tp->tid);
295 __gthread_mutex_lock (&lock);
296 tprintf ("exec thread tid = %d function = 0x%p arg = 0x%p\n", tp->tid,
297 tp->proc, t);
298 /* Has the thread been signalled? */
299 if (tp->value == 0)
301 /* Not been signalled therefore we force ourselves to block. */
302 tprintf ("%s: forcing thread tid = %d to wait\n",
303 __FUNCTION__, tp->tid);
304 tp->waiting = true; /* We are waiting. */
305 __gthread_cond_wait (&tp->run_counter, &lock);
306 tp->waiting = false; /* Running again. */
308 else
310 /* Yes signalled, therefore just take the recorded signal and continue. */
311 tprintf ("%s: no need for thread tid = %d to wait\n",
312 __FUNCTION__, tp->tid);
313 tp->value--;
315 tprintf (" running exec thread [%d] function = 0x%p arg = 0x%p\n", tp->tid,
316 tp->proc, t);
317 __gthread_mutex_unlock (&lock);
318 tp->proc (); /* Now execute user procedure. */
319 #if 0
320 m2iso_M2RTS_CoroutineException ( __FILE__, __LINE__, __COLUMN__, __FUNCTION__, "coroutine finishing");
321 #endif
322 m2iso_M2RTS_HaltC ("execThread should never finish",
323 __FILE__, __FUNCTION__, __LINE__);
324 return NULL;
327 static int
328 newThread (void)
330 #if defined(POOL)
331 nThreads += 1;
332 if (nThreads == THREAD_POOL)
333 m2iso_M2RTS_HaltC ("too many threads created",
334 __FILE__, __FUNCTION__, __LINE__);
335 return nThreads - 1;
336 #else
337 if (nThreads == 0)
339 threadArray = (threadCB *)malloc (sizeof (threadCB));
340 nThreads = 1;
342 else
344 nThreads += 1;
345 threadArray
346 = (threadCB *)realloc (threadArray, sizeof (threadCB) * nThreads);
348 return nThreads - 1;
349 #endif
352 static int
353 initThread (void (*proc) (void), unsigned int stackSize,
354 unsigned int interrupt)
356 int tid = newThread ();
357 pthread_attr_t attr;
358 int result;
360 threadArray[tid].proc = proc;
361 threadArray[tid].tid = tid;
362 /* Initialize the thread run_counter used to force a context switch. */
363 __GTHREAD_COND_INIT_FUNCTION (&threadArray[tid].run_counter);
364 threadArray[tid].interruptLevel = interrupt;
365 threadArray[tid].waiting = false; /* The thread is running. */
366 threadArray[tid].value = 0; /* No signal has been seen yet. */
368 /* Set thread creation attributes. */
369 result = pthread_attr_init (&attr);
370 if (result != 0)
371 m2iso_M2RTS_HaltC ("failed to create thread attribute",
372 __FILE__, __FUNCTION__, __LINE__);
374 if (stackSize > 0)
376 result = pthread_attr_setstacksize (&attr, stackSize);
377 if (result != 0)
378 m2iso_M2RTS_HaltC ("failed to set stack size attribute",
379 __FILE__, __FUNCTION__, __LINE__);
382 tprintf ("initThread [%d] function = 0x%p (arg = 0x%p)\n", tid, proc,
383 (void *)&threadArray[tid]);
384 result = pthread_create (&threadArray[tid].p, &attr, execThread,
385 (void *)&threadArray[tid]);
386 if (result != 0)
387 m2iso_M2RTS_HaltC ("thread_create failed",
388 __FILE__, __FUNCTION__, __LINE__);
389 tprintf (" created thread [%d] function = 0x%p 0x%p\n", tid, proc,
390 (void *)&threadArray[tid]);
391 return tid;
394 extern "C" int
395 EXPORT(initThread) (void (*proc) (void), unsigned int stackSize,
396 unsigned int interrupt)
398 int tid;
400 EXPORT(init) ();
401 __gthread_mutex_lock (&lock);
402 tid = initThread (proc, stackSize, interrupt);
403 __gthread_mutex_unlock (&lock);
404 return tid;
407 /* transfer unlocks thread p2 and locks the current thread. p1 is
408 updated with the current thread id.
409 The implementation of transfer uses a combined wait/signal. */
411 extern "C" void
412 EXPORT(transfer) (int *p1, int p2)
414 __gthread_mutex_lock (&lock);
416 int current = currentThread ();
417 if (!initialized)
418 m2iso_M2RTS_HaltC ("cannot transfer to a process before the process has been created",
419 __FILE__, __FUNCTION__, __LINE__);
420 if (current == p2)
422 /* Error. */
423 m2iso_M2RTS_HaltC ("attempting to transfer to ourself",
424 __FILE__, __FUNCTION__, __LINE__);
426 else
428 *p1 = current;
429 int old = current;
430 tprintf ("start, context switching from: %d to %d\n", current, p2);
431 /* Perform signal (p2 sem). Without the mutex lock as we have
432 already obtained it above. */
433 if (threadArray[p2].waiting)
435 /* p2 is blocked on the condition variable, release it. */
436 tprintf ("p1 = %d cond_signal to p2 (%d)\n", current, p2);
437 __gthread_cond_signal (&threadArray[p2].run_counter);
438 tprintf ("after p1 = %d cond_signal to p2 (%d)\n", current, p2);
440 else
442 /* p2 hasn't reached the condition variable, so bump value
443 ready for p2 to test. */
444 tprintf ("no need for thread %d to cond_signal - bump %d value (pre) = %d\n",
445 current, p2, threadArray[p2].value);
446 threadArray[p2].value++;
448 /* Perform wait (old sem). Again without obtaining mutex as
449 we've already claimed it. */
450 if (threadArray[old].value == 0)
452 /* Record we are about to wait on the condition variable. */
453 threadArray[old].waiting = true;
454 __gthread_cond_wait (&threadArray[old].run_counter, &lock);
455 threadArray[old].waiting = false;
456 /* We are running again. */
458 else
460 tprintf ("(currentThread = %d) no need for thread %d to cond_wait - taking value (pre) = %d\n",
461 current, old, threadArray[old].value);
462 /* No need to block as we have been told a signal has
463 effectively already been recorded. We remove the signal
464 notification without blocking. */
465 threadArray[old].value--;
467 tprintf ("end, context back to %d\n", current);
468 if (current != old)
469 m2iso_M2RTS_HaltC ("wrong process id",
470 __FILE__, __FUNCTION__, __LINE__);
473 __gthread_mutex_unlock (&lock);
476 extern "C" int
477 EXPORT(select) (int p1, fd_set *p2, fd_set *p3, fd_set *p4, const timespec *p5)
479 EXPORT(init) ();
480 tprintf ("[%x] RTco.select (...)\n", pthread_self ());
481 return pselect (p1, p2, p3, p4, p5, NULL);
484 extern "C" int
485 EXPORT(init) (void)
487 tprintf ("checking init\n");
488 if (! initialized)
490 initialized = true;
492 tprintf ("RTco initialized\n");
493 __GTHREAD_MUTEX_INIT_FUNCTION (&lock);
494 __gthread_mutex_lock (&lock);
495 /* Create initial thread container. */
496 #if defined(POOL)
497 threadArray = (threadCB *)malloc (sizeof (threadCB) * THREAD_POOL);
498 semArray = (threadSem **)malloc (sizeof (threadSem *) * SEM_POOL);
499 #endif
500 /* Create a thread control block for the main program (or process). */
501 int tid = newThread (); /* For the current initial thread. */
502 threadArray[tid].p = pthread_self ();
503 threadArray[tid].tid = tid;
504 __GTHREAD_COND_INIT_FUNCTION (&threadArray[tid].run_counter);
505 threadArray[tid].interruptLevel = 0;
506 /* The line below shouldn't be necessary as we are already running. */
507 threadArray[tid].proc = never;
508 threadArray[tid].waiting = false; /* We are running. */
509 threadArray[tid].value = 0; /* No signal from anyone yet. */
510 tprintf ("RTco initialized completed\n");
511 __gthread_mutex_unlock (&lock);
513 return 0;
516 extern "C" void __attribute__((__constructor__))
517 M2EXPORT(ctor) (void)
519 m2iso_M2RTS_RegisterModule ("RTco", M2LIBNAME,
520 M2EXPORT(init), M2EXPORT(fini),
521 M2EXPORT(dep));