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)
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/>. */
30 #include <sys/select.h>
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. */
49 #define SEM_POOL 10000
50 #define THREAD_POOL 10000
52 #define _GTHREAD_USE_COND_INIT_FUNC
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__
59 #define gm2_printf gnu_printf
61 #define gm2_printf __printf__
65 #define tprintf printf
71 typedef struct threadCB_s
75 int tid
; /* The thread id. */
76 unsigned int interruptLevel
;
77 __gthread_cond_t run_counter
; /* Used to block the thread and force
79 int value
; /* Count 0 or 1. */
80 bool waiting
; /* Is this thread waiting on the run_counter? */
84 typedef struct threadSem_s
86 __gthread_cond_t counter
;
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
99 static bool initialized
= false;
101 extern "C" int EXPORT(init
) (void);
109 M2EXPORT(init
) (int argc
, char *argv
[], char *envp
[])
114 M2EXPORT(fini
) (int argc
, char *argv
[], char *envp
[])
120 initSem (threadSem
*sem
, int value
)
122 __GTHREAD_COND_INIT_FUNCTION (&sem
->counter
);
123 sem
->waiting
= false;
124 sem
->sem_value
= value
;
128 waitSem (threadSem
*sem
)
130 __gthread_mutex_lock (&lock
);
131 if (sem
->sem_value
== 0)
134 __gthread_cond_wait (&sem
->counter
, &lock
);
135 sem
->waiting
= false;
139 __gthread_mutex_unlock (&lock
);
143 signalSem (threadSem
*sem
)
145 __gthread_mutex_lock (&lock
);
147 __gthread_cond_signal (&sem
->counter
);
150 __gthread_mutex_unlock (&lock
);
154 EXPORT(wait
) (int sid
)
157 tprintf ("wait %d\n", sid
);
158 waitSem (semArray
[sid
]);
162 EXPORT(signal
) (int sid
)
165 tprintf ("signal %d\n", sid
);
166 signalSem (semArray
[sid
]);
173 semArray
[nSemaphores
]
174 = (threadSem
*)malloc (sizeof (threadSem
));
176 if (nSemaphores
== SEM_POOL
)
177 m2iso_M2RTS_HaltC ("too many semaphores created",
178 __FILE__
, __FUNCTION__
, __LINE__
);
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
186 if (nSemaphores
== 0)
188 semArray
= (threadSem
**)malloc (sizeof (sem
));
194 semArray
= (threadSem
**)realloc (semArray
,
195 sizeof (sem
) * nSemaphores
);
197 semArray
[nSemaphores
- 1] = sem
;
199 return nSemaphores
- 1;
203 initSemaphore (int value
)
207 initSem (semArray
[sid
], value
);
208 tprintf ("%d = initSemaphore (%d)\n", sid
, value
);
213 EXPORT(initSemaphore
) (int value
)
217 tprintf ("initSemaphore (%d) called\n", value
);
219 tprintf ("about to access lock\n");
220 __gthread_mutex_lock (&lock
);
221 sid
= initSemaphore (value
);
222 __gthread_mutex_unlock (&lock
);
231 for (tid
= 0; tid
< nThreads
; tid
++)
232 if (pthread_self () == threadArray
[tid
].p
)
234 m2iso_M2RTS_HaltC ("failed to find currentThread",
235 __FILE__
, __FUNCTION__
, __LINE__
);
239 EXPORT(currentThread
) (void)
244 __gthread_mutex_lock (&lock
);
245 tid
= currentThread ();
246 tprintf ("currentThread %d\n", tid
);
247 __gthread_mutex_unlock (&lock
);
251 /* currentInterruptLevel returns the interrupt level of the current thread. */
253 extern "C" unsigned int
254 EXPORT(currentInterruptLevel
) (void)
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
);
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
)
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
);
285 m2iso_M2RTS_HaltC ("the main thread should never call here",
286 __FILE__
, __FUNCTION__
, __LINE__
);
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
,
298 /* Has the thread been signalled? */
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. */
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
);
315 tprintf (" running exec thread [%d] function = 0x%p arg = 0x%p\n", tp
->tid
,
317 __gthread_mutex_unlock (&lock
);
318 tp
->proc (); /* Now execute user procedure. */
320 m2iso_M2RTS_CoroutineException ( __FILE__
, __LINE__
, __COLUMN__
, __FUNCTION__
, "coroutine finishing");
322 m2iso_M2RTS_HaltC ("execThread should never finish",
323 __FILE__
, __FUNCTION__
, __LINE__
);
332 if (nThreads
== THREAD_POOL
)
333 m2iso_M2RTS_HaltC ("too many threads created",
334 __FILE__
, __FUNCTION__
, __LINE__
);
339 threadArray
= (threadCB
*)malloc (sizeof (threadCB
));
346 = (threadCB
*)realloc (threadArray
, sizeof (threadCB
) * nThreads
);
353 initThread (void (*proc
) (void), unsigned int stackSize
,
354 unsigned int interrupt
)
356 int tid
= newThread ();
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
);
371 m2iso_M2RTS_HaltC ("failed to create thread attribute",
372 __FILE__
, __FUNCTION__
, __LINE__
);
376 result
= pthread_attr_setstacksize (&attr
, stackSize
);
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
]);
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
]);
395 EXPORT(initThread
) (void (*proc
) (void), unsigned int stackSize
,
396 unsigned int interrupt
)
401 __gthread_mutex_lock (&lock
);
402 tid
= initThread (proc
, stackSize
, interrupt
);
403 __gthread_mutex_unlock (&lock
);
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. */
412 EXPORT(transfer
) (int *p1
, int p2
)
414 __gthread_mutex_lock (&lock
);
416 int current
= currentThread ();
418 m2iso_M2RTS_HaltC ("cannot transfer to a process before the process has been created",
419 __FILE__
, __FUNCTION__
, __LINE__
);
423 m2iso_M2RTS_HaltC ("attempting to transfer to ourself",
424 __FILE__
, __FUNCTION__
, __LINE__
);
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
);
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. */
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
);
469 m2iso_M2RTS_HaltC ("wrong process id",
470 __FILE__
, __FUNCTION__
, __LINE__
);
473 __gthread_mutex_unlock (&lock
);
477 EXPORT(select
) (int p1
, fd_set
*p2
, fd_set
*p3
, fd_set
*p4
, const timespec
*p5
)
480 tprintf ("[%x] RTco.select (...)\n", pthread_self ());
481 return pselect (p1
, p2
, p3
, p4
, p5
, NULL
);
487 tprintf ("checking init\n");
492 tprintf ("RTco initialized\n");
493 __GTHREAD_MUTEX_INIT_FUNCTION (&lock
);
494 __gthread_mutex_lock (&lock
);
495 /* Create initial thread container. */
497 threadArray
= (threadCB
*)malloc (sizeof (threadCB
) * THREAD_POOL
);
498 semArray
= (threadSem
**)malloc (sizeof (threadSem
*) * SEM_POOL
);
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
);
516 extern "C" void __attribute__((__constructor__
))
517 M2EXPORT(ctor
) (void)
519 m2iso_M2RTS_RegisterModule ("RTco", M2LIBNAME
,
520 M2EXPORT(init
), M2EXPORT(fini
),