Files
gcc/libgm2/libm2iso/RTco.cc
T
Iain Sandoe ec6d1fc09f modula-2: Fix registration of modules via constructors [PR108183].
This reworks the mechanism used for module registration to use init-
time constructors.  The order of registration is not important, the
actual initialization dependency tree will be computed early in the
execution (all that matters is that we have registered before that).

This fixes a potential issue in which the external name known to the
m2 system is of the form _M2_XXXXXX_ctor() but the C++ code was
producing a static variable instance with the same name.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

	PR modula2/108183

gcc/m2/ChangeLog:

	* gm2-libs-ch/UnixArgs.cc (_M2_UnixArgs_ctor): Rework to use
	an extern "C" function with 'constructor' attribute.
	* gm2-libs-ch/dtoa.cc (_M2_dtoa_ctor): Likewise.
	* gm2-libs-ch/ldtoa.cc (_M2_ldtoa_ctor): Likewise.

libgm2/ChangeLog:

	* libm2cor/KeyBoardLEDs.cc (_M2_KeyBoardLEDs_ctor): Rework to use
	an extern "C" function with 'constructor' attribute.
	* libm2iso/ErrnoCategory.cc (_M2_ErrnoCategory_ctor): Likewise.
	* libm2iso/RTco.cc (_M2_RTco_ctor): Likewise.
	* libm2pim/Selective.cc (_M2_Selective_ctor): Likewise.
	* libm2pim/SysExceptions.cc (_M2_SysExceptions_ctor): Likewise.
	* libm2pim/UnixArgs.cc (_M2_UnixArgs_ctor): Likewise.
	* libm2pim/cgetopt.cc (_M2_cgetopt_ctor): Likewise.
	* libm2pim/dtoa.cc (_M2_dtoa_ctor): Likewise.
	* libm2pim/errno.cc (_M2_errno_ctor): Likewise.
	* libm2pim/ldtoa.cc (_M2_ldtoa_ctor): Likewise.
	* libm2pim/sckt.cc (_M2_sckt_ctor): Likewise.
	* libm2pim/termios.cc (_M2_termios_ctor): Likewise.
	* libm2pim/wrapc.c: Add a new line to the file end.
2023-01-04 14:54:41 +00:00

468 lines
11 KiB
C++

/* RTco.c 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>
// #define 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(TRUE)
#define TRUE (1 == 1)
#endif
#if !defined(FALSE)
#define FALSE (1 == 0)
#endif
#if defined(TRACEON)
#define tprintf printf
#else
/* sizeof is not evaluated. */
#define tprintf (void)sizeof
#endif
typedef struct threadCB_s
{
void (*proc) (void);
int execution;
pthread_t p;
int tid;
unsigned int interruptLevel;
} threadCB;
typedef struct threadSem_s
{
__gthread_mutex_t mutex;
__gthread_cond_t counter;
int 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 threadSem lock;
static int initialized = FALSE;
extern "C" int RTco_init (void);
extern "C" void
_M2_RTco_dep (void)
{
}
extern "C" void
_M2_RTco_init (int argc, char *argv[], char *envp[])
{
}
extern "C" void
_M2_RTco_fini (int argc, char *argv[], char *envp[])
{
}
static void
initSem (threadSem *sem, int value)
{
__GTHREAD_COND_INIT_FUNCTION (&sem->counter);
__GTHREAD_MUTEX_INIT_FUNCTION (&sem->mutex);
sem->waiting = FALSE;
sem->sem_value = value;
}
static void
waitSem (threadSem *sem)
{
__gthread_mutex_lock (&sem->mutex);
if (sem->sem_value == 0)
{
sem->waiting = TRUE;
__gthread_cond_wait (&sem->counter, &sem->mutex);
sem->waiting = FALSE;
}
else
sem->sem_value--;
__gthread_mutex_unlock (&sem->mutex);
}
static void
signalSem (threadSem *sem)
{
__gthread_mutex_unlock (&sem->mutex);
if (sem->waiting)
__gthread_cond_signal (&sem->counter);
else
sem->sem_value++;
__gthread_mutex_unlock (&sem->mutex);
}
void stop (void) {}
extern "C" void
RTco_wait (int sid)
{
RTco_init ();
tprintf ("wait %d\n", sid);
waitSem (semArray[sid]);
}
extern "C" void
RTco_signal (int sid)
{
RTco_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)
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__,
"too many semaphores created");
#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
RTco_initSemaphore (int value)
{
int sid;
RTco_init ();
waitSem (&lock);
sid = initSemaphore (value);
signalSem (&lock);
return sid;
}
/* signalThread signal the semaphore associated with thread tid. */
extern "C" void
RTco_signalThread (int tid)
{
int sem;
RTco_init ();
tprintf ("signalThread %d\n", tid);
waitSem (&lock);
sem = threadArray[tid].execution;
signalSem (&lock);
RTco_signal (sem);
}
/* waitThread wait on the semaphore associated with thread tid. */
extern "C" void
RTco_waitThread (int tid)
{
RTco_init ();
tprintf ("waitThread %d\n", tid);
RTco_wait (threadArray[tid].execution);
}
extern "C" int
currentThread (void)
{
int tid;
for (tid = 0; tid < nThreads; tid++)
if (pthread_self () == threadArray[tid].p)
return tid;
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__,
"failed to find currentThread");
}
extern "C" int
RTco_currentThread (void)
{
int tid;
RTco_init ();
waitSem (&lock);
tid = currentThread ();
tprintf ("currentThread %d\n", tid);
signalSem (&lock);
return tid;
}
/* currentInterruptLevel returns the interrupt level of the current thread. */
extern "C" unsigned int
RTco_currentInterruptLevel (void)
{
RTco_init ();
tprintf ("currentInterruptLevel %d\n",
threadArray[RTco_currentThread ()].interruptLevel);
return threadArray[RTco_currentThread ()].interruptLevel;
}
/* turninterrupts returns the old interrupt level and assigns the
interrupt level to newLevel. */
extern "C" unsigned int
RTco_turnInterrupts (unsigned int newLevel)
{
int tid = RTco_currentThread ();
unsigned int old = RTco_currentInterruptLevel ();
tprintf ("turnInterrupts from %d to %d\n", old, newLevel);
waitSem (&lock);
threadArray[tid].interruptLevel = newLevel;
signalSem (&lock);
return old;
}
static void
never (void)
{
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__,
"the main thread should never call here");
}
static void *
execThread (void *t)
{
threadCB *tp = (threadCB *)t;
tprintf ("exec thread tid = %d function = 0x%p arg = 0x%p\n", tp->tid,
tp->proc, t);
RTco_waitThread (
tp->tid); /* Forcing this thread to block, waiting to be scheduled. */
tprintf (" exec thread [%d] function = 0x%p arg = 0x%p\n", tp->tid,
tp->proc, t);
tp->proc (); /* Now execute user procedure. */
#if 0
M2RTS_CoroutineException ( __FILE__, __LINE__, __COLUMN__, __FUNCTION__, "coroutine finishing");
#endif
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__, "execThread should never finish");
return NULL;
}
static int
newThread (void)
{
#if defined(POOL)
nThreads += 1;
if (nThreads == THREAD_POOL)
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__, "too many threads created");
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;
threadArray[tid].execution = initSemaphore (0);
threadArray[tid].interruptLevel = interrupt;
/* set thread creation attributes. */
result = pthread_attr_init (&attr);
if (result != 0)
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__,
"failed to create thread attribute");
if (stackSize > 0)
{
result = pthread_attr_setstacksize (&attr, stackSize);
if (result != 0)
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__,
"failed to set stack size attribute");
}
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)
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__, "thread_create failed");
tprintf (" created thread [%d] function = 0x%p 0x%p\n", tid, proc,
(void *)&threadArray[tid]);
return tid;
}
extern "C" int
RTco_initThread (void (*proc) (void), unsigned int stackSize,
unsigned int interrupt)
{
int tid;
RTco_init ();
waitSem (&lock);
tid = initThread (proc, stackSize, interrupt);
signalSem (&lock);
return tid;
}
/* transfer unlocks thread p2 and locks the current thread. p1 is
updated with the current thread id. */
extern "C" void
RTco_transfer (int *p1, int p2)
{
int tid = currentThread ();
if (!initialized)
M2RTS_Halt (
__FILE__, __LINE__, __FUNCTION__,
"cannot transfer to a process before the process has been created");
if (tid == p2)
{
/* error. */
M2RTS_Halt (__FILE__, __LINE__, __FUNCTION__,
"attempting to transfer to ourself");
}
else
{
*p1 = tid;
tprintf ("start, context switching from: %d to %d\n", tid, p2);
RTco_signalThread (p2);
RTco_waitThread (tid);
tprintf ("end, context back to %d\n", tid);
}
}
extern "C" int
RTco_select (int p1, fd_set *p2, fd_set *p3, fd_set *p4, const timespec *p5)
{
RTco_init ();
tprintf ("[%x] RTco.select (...)\n", pthread_self ());
return pselect (p1, p2, p3, p4, p5, NULL);
}
extern "C" int
RTco_init (void)
{
if (! initialized)
{
int tid;
tprintf ("RTco initialized\n");
initSem (&lock, 0);
/* Create initial thread container. */
#if defined(POOL)
threadArray = (threadCB *)malloc (sizeof (threadCB) * THREAD_POOL);
semArray = (threadSem **)malloc (sizeof (threadSem *) * SEM_POOL);
#endif
tid = newThread (); /* For the current initial thread. */
threadArray[tid].tid = tid;
threadArray[tid].execution = initSemaphore (0);
threadArray[tid].p = pthread_self ();
threadArray[tid].interruptLevel = 0;
threadArray[tid].proc
= never; /* This shouldn't happen as we are already running. */
initialized = TRUE;
tprintf ("RTco initialized completed\n");
signalSem (&lock);
}
return 0;
}
extern "C" void __attribute__((__constructor__))
_M2_RTco_ctor (void)
{
M2RTS_RegisterModule ("RTco", _M2_RTco_init, _M2_RTco_fini,
_M2_RTco_dep);
}