mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-13 04:44:39 +00:00
Estimated hours taken: 15
Fix several bugs in the runtime engine to do with thread-safe execution.
Add a new flag to pragma c_code/import to allow programmers to specify
whether or not the C code is thread-safe or not, and modify code generation
to put a lock around C code that isn't thread_safe.
runtime/mercury_thread.{c,h}:
Add a global lock.
Change the handling of thread creation. create_thread now takes
a "closure" (a C struct with a fn pointer and an argument to pass
the function) which it calls in the new thread. (The same mechanism
is used in the Boehm collector), or NULL which causes the thread
to wait for work to appear in the Mercury runqueue.
runtime/mercury_context.c:
initialize the global lock.
runtime/mercury_engine.{c,h}:
Add a new field to the MercuryEngine structre which is used to
store a list of saved thread ids. These were being saved in a
local variable in call_engine_inner which was a bug because
call_engine_inner's (C) stack frame gets scribbled on by Mercury
execution. For more detail see the comments in mercury_engine.h
runtime/mercury_wrapper.c:
Use the new interface to create_thread.
compiler/prog_io_pragma.m:
Parse either a single attribute or a list of attributes instead
of just 'may_call_mercury' in pragma c code and pragma import.
These are stored in an abstract type 'pragma_c_code_attributes'
that uses a bit array (aka int) to store the attributes.
compiler/pragma_c_gen.m:
Get the code generator to emit c code to obtain and release the
global lock for pragma c code that isn't thread_safe.
compiler/<various>.m:
Change may_call_mercury to pragma_c_code_attributes.
doc/reference_manual.m:
Document the change to pragma c code.
scripts/mgnuc.in:
Pass some extra C flags for thread-safe compilation for Linux.
142 lines
4.6 KiB
C
142 lines
4.6 KiB
C
/*
|
|
** Copyright (C) 1997-1998 The University of Melbourne.
|
|
** This file may only be copied under the terms of the GNU Library General
|
|
** Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
*/
|
|
#ifndef MERCURY_THREAD_H
|
|
#define MERCURY_THREAD_H
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
|
|
#include <signal.h> /* for sigset_t on the SPARC */
|
|
#include <pthread.h>
|
|
#include "mercury_std.h"
|
|
|
|
#if defined(MR_DIGITAL_UNIX_PTHREADS)
|
|
#define MR_MUTEX_ATTR pthread_mutexattr_default
|
|
#define MR_COND_ATTR pthread_condattr_default
|
|
#define MR_THREAD_ATTR pthread_attr_default
|
|
#else
|
|
#define MR_MUTEX_ATTR NULL
|
|
#define MR_COND_ATTR NULL
|
|
#define MR_THREAD_ATTR NULL
|
|
#endif
|
|
|
|
typedef pthread_t MercuryThread;
|
|
typedef pthread_key_t MercuryThreadKey;
|
|
typedef pthread_mutex_t MercuryLock;
|
|
typedef pthread_cond_t MercuryCond;
|
|
|
|
#if 0
|
|
/*
|
|
** The following macros should be used once the
|
|
** use of locking in the generated code is considered
|
|
** stable, since the alternative versions do the
|
|
** same thing, but with debugging support.
|
|
*/
|
|
#define MR_LOCK(lck, from) pthread_mutex_lock((lck))
|
|
#define MR_UNLOCK(lck, from) pthread_mutex_unlock((lck))
|
|
|
|
#define MR_SIGNAL(cnd) pthread_cond_signal((cnd))
|
|
#define MR_WAIT(cnd, mtx) pthread_cond_wait((cnd), (mtx))
|
|
#else
|
|
void MR_mutex_lock(MercuryLock *lock, const char *from);
|
|
void MR_mutex_unlock(MercuryLock *lock, const char *from);
|
|
void MR_cond_signal(MercuryCond *cond);
|
|
void MR_cond_wait(MercuryCond *cond, MercuryLock *lock);
|
|
|
|
#define MR_LOCK(lck, from) MR_mutex_lock((lck), (from))
|
|
#define MR_UNLOCK(lck, from) MR_mutex_unlock((lck), (from))
|
|
|
|
#define MR_SIGNAL(cnd) MR_cond_signal((cnd))
|
|
#define MR_WAIT(cnd, mtx) MR_cond_wait((cnd), (mtx))
|
|
#endif
|
|
|
|
/*
|
|
** The following two macros are used to protect pragma c_code
|
|
** predicates which are not thread-safe.
|
|
** See the comments below.
|
|
*/
|
|
#define MR_OBTAIN_GLOBAL_C_LOCK() MR_mutex_lock(&MR_global_lock, \
|
|
"pragma c code");
|
|
|
|
#define MR_RELEASE_GLOBAL_C_LOCK() MR_mutex_unlock(&MR_global_lock, \
|
|
"pragma c code");
|
|
|
|
#if defined(MR_DIGITAL_UNIX_PTHREADS)
|
|
#define MR_GETSPECIFIC(key) ({ \
|
|
pthread_addr_t gstmp; \
|
|
pthread_getspecific((key), &gstmp); \
|
|
(void *) gstmp; \
|
|
})
|
|
#define MR_KEY_CREATE pthread_keycreate
|
|
#else
|
|
#define MR_GETSPECIFIC(key) pthread_getspecific((key))
|
|
#define MR_KEY_CREATE pthread_key_create
|
|
#endif
|
|
|
|
typedef struct {
|
|
void (*func)(void *);
|
|
void *arg;
|
|
} MR_ThreadGoal;
|
|
|
|
/*
|
|
** create_thread(Goal) creates a new POSIX thread, and creates and
|
|
** initializes a new Mercury engine to run in that thread. If Goal
|
|
** is a NULL pointer, that thread will suspend on the global Mercury
|
|
** runqueue. If Goal is non-NULL, it is a pointer to a MR_ThreadGoal
|
|
** structure containing a function and an argument. The function will
|
|
** be called with the given argument in the new thread.
|
|
*/
|
|
MercuryThread *create_thread(MR_ThreadGoal *);
|
|
void destroy_thread(void *eng);
|
|
extern bool MR_exit_now;
|
|
|
|
/*
|
|
** MR_global_lock is a mutex for ensuring that only one non-threadsafe
|
|
** piece of pragma c code executes at a time. If `not_threadsafe' is
|
|
** given or `threadsafe' is not given in the attributes of a pragma
|
|
** c code definition of a predicate, then the generated code will
|
|
** obtain this lock before executing the C code fragment, and then
|
|
** release it afterwards.
|
|
** XXX we should emit a warning if may_call_mercury and not_threadsafe
|
|
** (the defaults) are specified since if you obtain the lock then
|
|
** call back into Mercury deadlock could result.
|
|
*/
|
|
extern MercuryLock MR_global_lock;
|
|
|
|
#else /* not MR_THREAD_SAFE */
|
|
|
|
#define MR_LOCK(nothing, from) do { } while (0)
|
|
#define MR_UNLOCK(nothing, from) do { } while (0)
|
|
|
|
#define MR_SIGNAL(nothing) do { } while (0)
|
|
#define MR_WAIT(no, thing) do { } while (0)
|
|
|
|
#define MR_OBTAIN_GLOBAL_C_LOCK() do { } while (0)
|
|
|
|
#define MR_RELEASE_GLOBAL_C_LOCK() do { } while (0)
|
|
|
|
#endif
|
|
|
|
/*
|
|
** The following enum is used as the argument to init_thread.
|
|
** MR_use_now should be passed to init_thread to indicate that
|
|
** it has been called in a context in which it should initialize
|
|
** the current thread's environment and return.
|
|
** MR_use_later should be passed to indicate that the thread should
|
|
** be initialized, then suspend waiting for work to appear in the
|
|
** runqueue. The engine is destroyed when the execution of work from
|
|
** the runqueue returns.
|
|
*/
|
|
typedef enum { MR_use_now, MR_use_later } MR_when_to_use;
|
|
|
|
/*
|
|
** Create and initialize a new Mercury engine running in the current
|
|
** POSIX thread.
|
|
** See the comments above for the meaning of the argument.
|
|
*/
|
|
void init_thread(MR_when_to_use);
|
|
|
|
#endif
|