mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-13 12:53:53 +00:00
Branches: main, 10.04 Support the use of the pthreads-win32 library on MinGW systems. (This is based on the patch provided by Sergey Khorev.) The main change is to remove the assumption in the runtime code that POSIX thread handles are integers; in the pthreads-win32 library they are not. With this change the hlc.par.gc grade will work on Windows / MinGW. (The low-level C parallel grades will require further work.) configure.in: Configure the Boehm GC to use pthreads-win32 if that is being used to provide threads for the runtime on MinGW. Delete the --with-pthreads-win32 option; it is no longer needed. Add a new option --with-gc-pthreads-win32 that forces the Boehm GC to use pthreads-win32. This is the default for MinGW anyway, the option is intended for use by developers using pthreads-win32 in other ways, e.g. with MSVC. runtime/mercury_thread.h: Add a new macro / function (depending on the implementation of pthreads) that returns the "null" thread. Add a new macro MR_thread_equal() that tests two thread handles for equality. runtime/mercury_thread.c: Provide implementations of MR_null_thread(). Add a macro, for use within this module, that returns the id of a thread in a form suitable for use in debugging messages. runtime/mercury_engine.c: runtime/mercury_context.c: runtime/mercury_wrapper.c: runtime/mercury_thread.c: Use MR_null_thread() instead of NULL or 0. Use MR_thread_equal() instead of directly comparing thread handles.
321 lines
12 KiB
C
321 lines
12 KiB
C
/*
|
|
** vim: ts=4 sw=4 expandtab
|
|
*/
|
|
/*
|
|
** Copyright (C) 1997-1998, 2000, 2003, 2005-2007, 2009-2010 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
|
|
|
|
#include "mercury_std.h"
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
|
|
#include <signal.h> /* for sigset_t on the SPARC */
|
|
#include <pthread.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;
|
|
|
|
extern int
|
|
MR_mutex_lock(MercuryLock *lock, const char *from);
|
|
extern int
|
|
MR_mutex_unlock(MercuryLock *lock, const char *from);
|
|
extern int
|
|
MR_cond_signal(MercuryCond *cond, const char *from);
|
|
extern int
|
|
MR_cond_broadcast(MercuryCond *cond, const char *from);
|
|
extern int
|
|
MR_cond_wait(MercuryCond *cond, MercuryLock *lock, const char *from);
|
|
extern int
|
|
MR_cond_timed_wait(MercuryCond *cond, MercuryLock *lock,
|
|
const struct timespec *abstime, const char *from);
|
|
|
|
#if defined(MR_PTHREADS_WIN32)
|
|
extern MercuryThread
|
|
MR_null_thread(void);
|
|
#else
|
|
#define MR_null_thread() ((MercuryThread) 0)
|
|
#endif
|
|
|
|
#define MR_thread_equal(a, b) pthread_equal((a), (b))
|
|
|
|
extern MR_bool MR_debug_threads;
|
|
|
|
#ifndef MR_DEBUG_THREADS
|
|
/*
|
|
** 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, from) pthread_cond_signal((cnd))
|
|
#define MR_BROADCAST(cnd, from) pthread_cond_broadcast((cnd))
|
|
#define MR_WAIT(cnd, mtx, from) pthread_cond_wait((cnd), (mtx))
|
|
#define MR_TIMED_WAIT(cond, mtx, abstime, from) \
|
|
pthread_cond_timedwait((cond), (mtx), (abstime))
|
|
#else
|
|
#define MR_LOCK(lck, from) \
|
|
( MR_debug_threads ? \
|
|
MR_mutex_lock((lck), (from)) \
|
|
: \
|
|
pthread_mutex_lock((lck)) \
|
|
)
|
|
#define MR_UNLOCK(lck, from) \
|
|
( MR_debug_threads ? \
|
|
MR_mutex_unlock((lck), (from)) \
|
|
: \
|
|
pthread_mutex_unlock((lck)) \
|
|
)
|
|
|
|
#define MR_SIGNAL(cnd, from) \
|
|
( MR_debug_threads ? \
|
|
MR_cond_signal((cnd), (from)) \
|
|
: \
|
|
pthread_cond_signal((cnd)) \
|
|
)
|
|
#define MR_BROADCAST(cnd, from) \
|
|
( MR_debug_threads ? \
|
|
MR_cond_broadcast((cnd), (from)) \
|
|
: \
|
|
pthread_cond_broadcast((cnd)) \
|
|
)
|
|
#define MR_WAIT(cnd, mtx, from) \
|
|
( MR_debug_threads ? \
|
|
MR_cond_wait((cnd), (mtx), (from)) \
|
|
: \
|
|
pthread_cond_wait((cnd), (mtx)) \
|
|
)
|
|
#define MR_TIMED_WAIT(cond, mtx, abstime, from) \
|
|
( MR_debug_threads ? \
|
|
MR_cond_timed_wait((cond), (mtx), (abstime), (from)) \
|
|
: \
|
|
pthread_cond_timedwait((cond), (mtx), (abstime)) \
|
|
)
|
|
#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_LOCK(where) MR_LOCK(&MR_global_lock, (where))
|
|
#define MR_RELEASE_GLOBAL_LOCK(where) MR_UNLOCK(&MR_global_lock, (where))
|
|
|
|
#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.
|
|
*/
|
|
|
|
extern MercuryThread *MR_create_thread(MR_ThreadGoal *);
|
|
extern void MR_destroy_thread(void *eng);
|
|
extern volatile MR_bool MR_exit_now;
|
|
|
|
/*
|
|
** The primordial thread. Currently used for debugging.
|
|
*/
|
|
extern MercuryThread MR_primordial_thread;
|
|
|
|
/*
|
|
** 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;
|
|
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
/*
|
|
** This lock protects writes to the MR_all_engine_bases structure.
|
|
*/
|
|
extern MercuryLock MR_init_engine_array_lock;
|
|
#endif
|
|
|
|
/*
|
|
** MR_exception_handler_key stores a key which can be used to get
|
|
** the current exception handler for the current thread.
|
|
*/
|
|
extern MercuryThreadKey MR_exception_handler_key;
|
|
|
|
#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, from) do { } while (0)
|
|
#define MR_BROADCAST(nothing, from) do { } while (0)
|
|
#define MR_WAIT(no, thing, from) (0)
|
|
|
|
#define MR_OBTAIN_GLOBAL_LOCK(where) do { } while (0)
|
|
#define MR_RELEASE_GLOBAL_LOCK(where) do { } while (0)
|
|
|
|
#endif
|
|
|
|
/*
|
|
** These are used to prevent the process terminating as soon as the original
|
|
** Mercury thread terminates.
|
|
*/
|
|
extern MR_Integer MR_thread_barrier_count;
|
|
#ifdef MR_THREAD_SAFE
|
|
extern MercuryLock MR_thread_barrier_lock;
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
extern MercuryCond MR_thread_barrier_cond;
|
|
#endif
|
|
#endif
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
extern struct MR_Context_Struct *MR_thread_barrier_context;
|
|
#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.
|
|
** If there is already a Mercury engine running in the current POSIX
|
|
** thread then init_thread is just a no-op.
|
|
**
|
|
** Returns MR_TRUE if a Mercury engine was created as a result of this
|
|
** call *and* it is the caller's responsibility to finalize it (it is
|
|
** intended that the caller can store the return value and call
|
|
** finalize_thread_engine if it is true).
|
|
*/
|
|
extern MR_bool
|
|
MR_init_thread(MR_when_to_use);
|
|
|
|
/*
|
|
** Finalize the thread engine running in the current POSIX thread.
|
|
** This will release the resources used by this thread -- this is very
|
|
** important because the memory used for the det stack for each thread
|
|
** can be re-used by the next init_thread.
|
|
*/
|
|
extern void
|
|
MR_finalize_thread_engine(void);
|
|
|
|
/*
|
|
** The values of thread-local mutables are stored in an array per Mercury
|
|
** thread. This makes it easy for a newly spawned thread to inherit (copy)
|
|
** all the thread-local mutables of its parent thread.
|
|
** Accesses to the array are protected by a mutex, in case a parallel
|
|
** conjunctions tries to read a thread-local value while another parallel
|
|
** conjunction (in the same Mercury thread) is writing to it.
|
|
**
|
|
** Each thread-local mutable has an associated index into the array, which is
|
|
** allocated to it during initialisation. For ease of implementation there is
|
|
** an arbitrary limit to the number of thread-local mutables that are allowed.
|
|
*/
|
|
typedef struct MR_ThreadLocalMuts MR_ThreadLocalMuts;
|
|
|
|
struct MR_ThreadLocalMuts {
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryLock MR_tlm_lock;
|
|
#endif
|
|
MR_Word *MR_tlm_values;
|
|
};
|
|
|
|
#define MR_MAX_THREAD_LOCAL_MUTABLES 128
|
|
extern MR_Unsigned MR_num_thread_local_mutables;
|
|
|
|
/*
|
|
** Allocate an index into the thread-local mutable array for a mutable.
|
|
*/
|
|
extern MR_Unsigned
|
|
MR_new_thread_local_mutable_index(void);
|
|
|
|
/*
|
|
** Allocate a thread-local mutable array.
|
|
*/
|
|
extern MR_ThreadLocalMuts *
|
|
MR_create_thread_local_mutables(MR_Unsigned numslots);
|
|
|
|
/*
|
|
** Make a copy of a thread-local mutable array.
|
|
*/
|
|
extern MR_ThreadLocalMuts *
|
|
MR_clone_thread_local_mutables(const MR_ThreadLocalMuts *old_muts);
|
|
|
|
#define MR_THREAD_LOCAL_MUTABLES \
|
|
(MR_ENGINE(MR_eng_this_context)->MR_ctxt_thread_local_mutables)
|
|
|
|
#define MR_SET_THREAD_LOCAL_MUTABLES(tlm) \
|
|
do { \
|
|
MR_THREAD_LOCAL_MUTABLES = (tlm); \
|
|
} while (0)
|
|
|
|
#define MR_get_thread_local_mutable(type, var, mut_index) \
|
|
do { \
|
|
MR_ThreadLocalMuts *tlm; \
|
|
\
|
|
tlm = MR_THREAD_LOCAL_MUTABLES; \
|
|
MR_LOCK(&tlm->MR_tlm_lock, "MR_get_thread_local_mutable"); \
|
|
var = * ((type *) &tlm->MR_tlm_values[(mut_index)]); \
|
|
MR_UNLOCK(&tlm->MR_tlm_lock, "MR_get_thread_local_mutable"); \
|
|
} while (0)
|
|
|
|
#define MR_set_thread_local_mutable(type, var, mut_index) \
|
|
do { \
|
|
MR_ThreadLocalMuts *tlm; \
|
|
\
|
|
tlm = MR_THREAD_LOCAL_MUTABLES; \
|
|
MR_LOCK(&tlm->MR_tlm_lock, "MR_set_thread_local_mutable"); \
|
|
* ((type *) &tlm->MR_tlm_values[(mut_index)]) = (var); \
|
|
MR_UNLOCK(&tlm->MR_tlm_lock, "MR_set_thread_local_mutable"); \
|
|
} while (0)
|
|
|
|
#endif /* MERCURY_THREAD_H */
|