mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
bae3b7919e
A missing bool/int detected in the global variable initialized. The majority of ints were replaced by bool but this one was missed. libgm2/ChangeLog: * libm2iso/RTco.cc (initialized): Use bool instead of int. Signed-off-by: Gaius Mulley <gaiusmod2@gmail.com>
523 lines
14 KiB
C++
523 lines
14 KiB
C++
/* RTco.cc provides minimal access to thread primitives.
|
|
|
|
Copyright (C) 2019-2022 Free Software Foundation, Inc.
|
|
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
|
|
|
|
This file is part of GNU Modula-2.
|
|
|
|
GNU Modula-2 is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GNU Modula-2 is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
Under Section 7 of GPL version 3, you are granted additional
|
|
permissions described in the GCC Runtime Library Exception, version
|
|
3.1, as published by the Free Software Foundation.
|
|
|
|
You should have received a copy of the GNU General Public License and
|
|
a copy of the GCC Runtime Library Exception along with this program;
|
|
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
#include <sys/select.h>
|
|
#include <stdlib.h>
|
|
#include <m2rts.h>
|
|
#include <cstdio>
|
|
|
|
#define EXPORT(FUNC) m2iso ## _RTco_ ## FUNC
|
|
#define M2EXPORT(FUNC) m2iso ## _M2_RTco_ ## FUNC
|
|
#define M2LIBNAME "m2iso"
|
|
|
|
/* This implementation of RTco.cc uses a single lock for mutex across
|
|
the whole module. It also forces context switching between threads
|
|
in transfer by combining an implementation of wait and signal.
|
|
|
|
All semaphores are implemented using the same mutex lock and
|
|
separate condition variables. */
|
|
|
|
#undef TRACEON
|
|
|
|
#define POOL
|
|
#define SEM_POOL 10000
|
|
#define THREAD_POOL 10000
|
|
|
|
#define _GTHREAD_USE_COND_INIT_FUNC
|
|
#include "gthr.h"
|
|
|
|
/* Ensure that ANSI conform stdio is used. This needs to be set
|
|
before any system header file is included. */
|
|
#if defined __MINGW32__
|
|
#define _POSIX 1
|
|
#define gm2_printf gnu_printf
|
|
#else
|
|
#define gm2_printf __printf__
|
|
#endif
|
|
|
|
#if defined(TRACEON)
|
|
#define tprintf printf
|
|
#else
|
|
#define tprintf(...)
|
|
#endif
|
|
|
|
|
|
typedef struct threadCB_s
|
|
{
|
|
void (*proc) (void);
|
|
pthread_t p;
|
|
int tid; /* The thread id. */
|
|
unsigned int interruptLevel;
|
|
__gthread_cond_t run_counter; /* Used to block the thread and force
|
|
a context switch. */
|
|
int value; /* Count 0 or 1. */
|
|
bool waiting; /* Is this thread waiting on the run_counter? */
|
|
} threadCB;
|
|
|
|
|
|
typedef struct threadSem_s
|
|
{
|
|
__gthread_cond_t counter;
|
|
bool waiting;
|
|
int sem_value;
|
|
} threadSem;
|
|
|
|
static unsigned int nThreads = 0;
|
|
static threadCB *threadArray = NULL;
|
|
static unsigned int nSemaphores = 0;
|
|
static threadSem **semArray = NULL;
|
|
|
|
/* These are used to lock the above module data structures. */
|
|
static __gthread_mutex_t lock; /* This is the only mutex for
|
|
the whole module. */
|
|
static bool initialized = false;
|
|
|
|
extern "C" int EXPORT(init) (void);
|
|
|
|
extern "C" void
|
|
M2EXPORT(dep) (void)
|
|
{
|
|
}
|
|
|
|
extern "C" void
|
|
M2EXPORT(init) (int argc, char *argv[], char *envp[])
|
|
{
|
|
}
|
|
|
|
extern "C" void
|
|
M2EXPORT(fini) (int argc, char *argv[], char *envp[])
|
|
{
|
|
}
|
|
|
|
|
|
static void
|
|
initSem (threadSem *sem, int value)
|
|
{
|
|
__GTHREAD_COND_INIT_FUNCTION (&sem->counter);
|
|
sem->waiting = false;
|
|
sem->sem_value = value;
|
|
}
|
|
|
|
static void
|
|
waitSem (threadSem *sem)
|
|
{
|
|
__gthread_mutex_lock (&lock);
|
|
if (sem->sem_value == 0)
|
|
{
|
|
sem->waiting = true;
|
|
__gthread_cond_wait (&sem->counter, &lock);
|
|
sem->waiting = false;
|
|
}
|
|
else
|
|
sem->sem_value--;
|
|
__gthread_mutex_unlock (&lock);
|
|
}
|
|
|
|
static void
|
|
signalSem (threadSem *sem)
|
|
{
|
|
__gthread_mutex_lock (&lock);
|
|
if (sem->waiting)
|
|
__gthread_cond_signal (&sem->counter);
|
|
else
|
|
sem->sem_value++;
|
|
__gthread_mutex_unlock (&lock);
|
|
}
|
|
|
|
extern "C" void
|
|
EXPORT(wait) (int sid)
|
|
{
|
|
EXPORT(init) ();
|
|
tprintf ("wait %d\n", sid);
|
|
waitSem (semArray[sid]);
|
|
}
|
|
|
|
extern "C" void
|
|
EXPORT(signal) (int sid)
|
|
{
|
|
EXPORT(init) ();
|
|
tprintf ("signal %d\n", sid);
|
|
signalSem (semArray[sid]);
|
|
}
|
|
|
|
static int
|
|
newSem (void)
|
|
{
|
|
#if defined(POOL)
|
|
semArray[nSemaphores]
|
|
= (threadSem *)malloc (sizeof (threadSem));
|
|
nSemaphores += 1;
|
|
if (nSemaphores == SEM_POOL)
|
|
m2iso_M2RTS_HaltC ("too many semaphores created",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
#else
|
|
threadSem *sem
|
|
= (threadSem *)malloc (sizeof (threadSem));
|
|
|
|
/* We need to be careful when using realloc as the lock (semaphore)
|
|
operators use the semaphore address. So we keep an array of pointer
|
|
to semaphores. */
|
|
if (nSemaphores == 0)
|
|
{
|
|
semArray = (threadSem **)malloc (sizeof (sem));
|
|
nSemaphores = 1;
|
|
}
|
|
else
|
|
{
|
|
nSemaphores += 1;
|
|
semArray = (threadSem **)realloc (semArray,
|
|
sizeof (sem) * nSemaphores);
|
|
}
|
|
semArray[nSemaphores - 1] = sem;
|
|
#endif
|
|
return nSemaphores - 1;
|
|
}
|
|
|
|
static int
|
|
initSemaphore (int value)
|
|
{
|
|
int sid = newSem ();
|
|
|
|
initSem (semArray[sid], value);
|
|
tprintf ("%d = initSemaphore (%d)\n", sid, value);
|
|
return sid;
|
|
}
|
|
|
|
extern "C" int
|
|
EXPORT(initSemaphore) (int value)
|
|
{
|
|
int sid;
|
|
|
|
tprintf ("initSemaphore (%d) called\n", value);
|
|
EXPORT(init) ();
|
|
tprintf ("about to access lock\n");
|
|
__gthread_mutex_lock (&lock);
|
|
sid = initSemaphore (value);
|
|
__gthread_mutex_unlock (&lock);
|
|
return sid;
|
|
}
|
|
|
|
static int
|
|
currentThread (void)
|
|
{
|
|
int tid;
|
|
|
|
for (tid = 0; tid < nThreads; tid++)
|
|
if (pthread_self () == threadArray[tid].p)
|
|
return tid;
|
|
m2iso_M2RTS_HaltC ("failed to find currentThread",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
}
|
|
|
|
extern "C" int
|
|
EXPORT(currentThread) (void)
|
|
{
|
|
int tid;
|
|
|
|
EXPORT(init) ();
|
|
__gthread_mutex_lock (&lock);
|
|
tid = currentThread ();
|
|
tprintf ("currentThread %d\n", tid);
|
|
__gthread_mutex_unlock (&lock);
|
|
return tid;
|
|
}
|
|
|
|
/* currentInterruptLevel returns the interrupt level of the current thread. */
|
|
|
|
extern "C" unsigned int
|
|
EXPORT(currentInterruptLevel) (void)
|
|
{
|
|
EXPORT(init) ();
|
|
__gthread_mutex_lock (&lock);
|
|
int current = currentThread ();
|
|
tprintf ("currentInterruptLevel %d\n",
|
|
threadArray[current].interruptLevel);
|
|
int level = threadArray[current].interruptLevel;
|
|
__gthread_mutex_unlock (&lock);
|
|
return level;
|
|
}
|
|
|
|
/* turninterrupts returns the old interrupt level and assigns the
|
|
interrupt level to newLevel. */
|
|
|
|
extern "C" unsigned int
|
|
EXPORT(turnInterrupts) (unsigned int newLevel)
|
|
{
|
|
EXPORT(init) ();
|
|
__gthread_mutex_lock (&lock);
|
|
int current = currentThread ();
|
|
unsigned int old = threadArray[current].interruptLevel;
|
|
tprintf ("turnInterrupts from %d to %d\n", old, newLevel);
|
|
threadArray[current].interruptLevel = newLevel;
|
|
__gthread_mutex_unlock (&lock);
|
|
return old;
|
|
}
|
|
|
|
static void
|
|
never (void)
|
|
{
|
|
m2iso_M2RTS_HaltC ("the main thread should never call here",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
}
|
|
|
|
static void *
|
|
execThread (void *t)
|
|
{
|
|
threadCB *tp = (threadCB *)t;
|
|
|
|
tprintf ("exec thread tid = %d coming to life\n", tp->tid);
|
|
__gthread_mutex_lock (&lock);
|
|
tprintf ("exec thread tid = %d function = 0x%p arg = 0x%p\n", tp->tid,
|
|
tp->proc, t);
|
|
/* Has the thread been signalled? */
|
|
if (tp->value == 0)
|
|
{
|
|
/* Not been signalled therefore we force ourselves to block. */
|
|
tprintf ("%s: forcing thread tid = %d to wait\n",
|
|
__FUNCTION__, tp->tid);
|
|
tp->waiting = true; /* We are waiting. */
|
|
__gthread_cond_wait (&tp->run_counter, &lock);
|
|
tp->waiting = false; /* Running again. */
|
|
}
|
|
else
|
|
{
|
|
/* Yes signalled, therefore just take the recorded signal and continue. */
|
|
tprintf ("%s: no need for thread tid = %d to wait\n",
|
|
__FUNCTION__, tp->tid);
|
|
tp->value--;
|
|
}
|
|
tprintf (" running exec thread [%d] function = 0x%p arg = 0x%p\n", tp->tid,
|
|
tp->proc, t);
|
|
__gthread_mutex_unlock (&lock);
|
|
tp->proc (); /* Now execute user procedure. */
|
|
#if 0
|
|
m2iso_M2RTS_CoroutineException ( __FILE__, __LINE__, __COLUMN__, __FUNCTION__, "coroutine finishing");
|
|
#endif
|
|
m2iso_M2RTS_HaltC ("execThread should never finish",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
newThread (void)
|
|
{
|
|
#if defined(POOL)
|
|
nThreads += 1;
|
|
if (nThreads == THREAD_POOL)
|
|
m2iso_M2RTS_HaltC ("too many threads created",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
return nThreads - 1;
|
|
#else
|
|
if (nThreads == 0)
|
|
{
|
|
threadArray = (threadCB *)malloc (sizeof (threadCB));
|
|
nThreads = 1;
|
|
}
|
|
else
|
|
{
|
|
nThreads += 1;
|
|
threadArray
|
|
= (threadCB *)realloc (threadArray, sizeof (threadCB) * nThreads);
|
|
}
|
|
return nThreads - 1;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
initThread (void (*proc) (void), unsigned int stackSize,
|
|
unsigned int interrupt)
|
|
{
|
|
int tid = newThread ();
|
|
pthread_attr_t attr;
|
|
int result;
|
|
|
|
threadArray[tid].proc = proc;
|
|
threadArray[tid].tid = tid;
|
|
/* Initialize the thread run_counter used to force a context switch. */
|
|
__GTHREAD_COND_INIT_FUNCTION (&threadArray[tid].run_counter);
|
|
threadArray[tid].interruptLevel = interrupt;
|
|
threadArray[tid].waiting = false; /* The thread is running. */
|
|
threadArray[tid].value = 0; /* No signal has been seen yet. */
|
|
|
|
/* Set thread creation attributes. */
|
|
result = pthread_attr_init (&attr);
|
|
if (result != 0)
|
|
m2iso_M2RTS_HaltC ("failed to create thread attribute",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
|
|
if (stackSize > 0)
|
|
{
|
|
result = pthread_attr_setstacksize (&attr, stackSize);
|
|
if (result != 0)
|
|
m2iso_M2RTS_HaltC ("failed to set stack size attribute",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
}
|
|
|
|
tprintf ("initThread [%d] function = 0x%p (arg = 0x%p)\n", tid, proc,
|
|
(void *)&threadArray[tid]);
|
|
result = pthread_create (&threadArray[tid].p, &attr, execThread,
|
|
(void *)&threadArray[tid]);
|
|
if (result != 0)
|
|
m2iso_M2RTS_HaltC ("thread_create failed",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
tprintf (" created thread [%d] function = 0x%p 0x%p\n", tid, proc,
|
|
(void *)&threadArray[tid]);
|
|
return tid;
|
|
}
|
|
|
|
extern "C" int
|
|
EXPORT(initThread) (void (*proc) (void), unsigned int stackSize,
|
|
unsigned int interrupt)
|
|
{
|
|
int tid;
|
|
|
|
EXPORT(init) ();
|
|
__gthread_mutex_lock (&lock);
|
|
tid = initThread (proc, stackSize, interrupt);
|
|
__gthread_mutex_unlock (&lock);
|
|
return tid;
|
|
}
|
|
|
|
/* transfer unlocks thread p2 and locks the current thread. p1 is
|
|
updated with the current thread id.
|
|
The implementation of transfer uses a combined wait/signal. */
|
|
|
|
extern "C" void
|
|
EXPORT(transfer) (int *p1, int p2)
|
|
{
|
|
__gthread_mutex_lock (&lock);
|
|
{
|
|
int current = currentThread ();
|
|
if (!initialized)
|
|
m2iso_M2RTS_HaltC ("cannot transfer to a process before the process has been created",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
if (current == p2)
|
|
{
|
|
/* Error. */
|
|
m2iso_M2RTS_HaltC ("attempting to transfer to ourself",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
}
|
|
else
|
|
{
|
|
*p1 = current;
|
|
int old = current;
|
|
tprintf ("start, context switching from: %d to %d\n", current, p2);
|
|
/* Perform signal (p2 sem). Without the mutex lock as we have
|
|
already obtained it above. */
|
|
if (threadArray[p2].waiting)
|
|
{
|
|
/* p2 is blocked on the condition variable, release it. */
|
|
tprintf ("p1 = %d cond_signal to p2 (%d)\n", current, p2);
|
|
__gthread_cond_signal (&threadArray[p2].run_counter);
|
|
tprintf ("after p1 = %d cond_signal to p2 (%d)\n", current, p2);
|
|
}
|
|
else
|
|
{
|
|
/* p2 hasn't reached the condition variable, so bump value
|
|
ready for p2 to test. */
|
|
tprintf ("no need for thread %d to cond_signal - bump %d value (pre) = %d\n",
|
|
current, p2, threadArray[p2].value);
|
|
threadArray[p2].value++;
|
|
}
|
|
/* Perform wait (old sem). Again without obtaining mutex as
|
|
we've already claimed it. */
|
|
if (threadArray[old].value == 0)
|
|
{
|
|
/* Record we are about to wait on the condition variable. */
|
|
threadArray[old].waiting = true;
|
|
__gthread_cond_wait (&threadArray[old].run_counter, &lock);
|
|
threadArray[old].waiting = false;
|
|
/* We are running again. */
|
|
}
|
|
else
|
|
{
|
|
tprintf ("(currentThread = %d) no need for thread %d to cond_wait - taking value (pre) = %d\n",
|
|
current, old, threadArray[old].value);
|
|
/* No need to block as we have been told a signal has
|
|
effectively already been recorded. We remove the signal
|
|
notification without blocking. */
|
|
threadArray[old].value--;
|
|
}
|
|
tprintf ("end, context back to %d\n", current);
|
|
if (current != old)
|
|
m2iso_M2RTS_HaltC ("wrong process id",
|
|
__FILE__, __FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
__gthread_mutex_unlock (&lock);
|
|
}
|
|
|
|
extern "C" int
|
|
EXPORT(select) (int p1, fd_set *p2, fd_set *p3, fd_set *p4, const timespec *p5)
|
|
{
|
|
EXPORT(init) ();
|
|
tprintf ("[%x] RTco.select (...)\n", pthread_self ());
|
|
return pselect (p1, p2, p3, p4, p5, NULL);
|
|
}
|
|
|
|
extern "C" int
|
|
EXPORT(init) (void)
|
|
{
|
|
tprintf ("checking init\n");
|
|
if (! initialized)
|
|
{
|
|
initialized = true;
|
|
|
|
tprintf ("RTco initialized\n");
|
|
__GTHREAD_MUTEX_INIT_FUNCTION (&lock);
|
|
__gthread_mutex_lock (&lock);
|
|
/* Create initial thread container. */
|
|
#if defined(POOL)
|
|
threadArray = (threadCB *)malloc (sizeof (threadCB) * THREAD_POOL);
|
|
semArray = (threadSem **)malloc (sizeof (threadSem *) * SEM_POOL);
|
|
#endif
|
|
/* Create a thread control block for the main program (or process). */
|
|
int tid = newThread (); /* For the current initial thread. */
|
|
threadArray[tid].p = pthread_self ();
|
|
threadArray[tid].tid = tid;
|
|
__GTHREAD_COND_INIT_FUNCTION (&threadArray[tid].run_counter);
|
|
threadArray[tid].interruptLevel = 0;
|
|
/* The line below shouldn't be necessary as we are already running. */
|
|
threadArray[tid].proc = never;
|
|
threadArray[tid].waiting = false; /* We are running. */
|
|
threadArray[tid].value = 0; /* No signal from anyone yet. */
|
|
tprintf ("RTco initialized completed\n");
|
|
__gthread_mutex_unlock (&lock);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
extern "C" void __attribute__((__constructor__))
|
|
M2EXPORT(ctor) (void)
|
|
{
|
|
m2iso_M2RTS_RegisterModule ("RTco", M2LIBNAME,
|
|
M2EXPORT(init), M2EXPORT(fini),
|
|
M2EXPORT(dep));
|
|
}
|