mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 13:55:07 +00:00
Branches: main Move more of the concurrency related modules from extras into the standard library. library/Mercury.options: library/library.m: library/thread.m: library/thread.channel.m: library/thread.mvar.m: library/thread.semaphore.m: Move the concurrency-related modules `channel', `mvar' and `semaphore' from extras/concurrency into the standard library. Make thread.mvar use the standard library module mutvar instead of providing its own implementation of the same thing. Replace "ME_" prefixes by "ML_". library/mutvar.m: Add predicate `new_mutvar0' which is like `new_mutvar' but does not require an initial value for the mutvar. This is needed for thread.mvar. Define `new_mutvar' in terms of `new_mutvar0' and `set_mutvar'. runtime/mercury_thread.h: Make the MR_WAIT macro expand to "(0)" when MR_THREAD_SAFE is not defined, so it can be used in an expression context. Zero is the success code for pthread_cond_wait. extras/concurrency/channel.m: extras/concurrency/mvar.m: extras/concurrency/semaphore.m: Remove these modules. extras/concurrency/Mercury.options: extras/concurrency/concurrency.m: Delete lines pertaining to removed modules. extras/concurrency/philo.m: extras/concurrency/philo2.m: extras/concurrency/philo3.m: Update to use the standard library modules. NEWS: Announce the change.
270 lines
9.1 KiB
C
270 lines
9.1 KiB
C
/*
|
|
** Copyright (C) 1997-1998, 2000, 2003, 2005-2007 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;
|
|
|
|
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);
|
|
|
|
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) pthread_cond_signal((cnd))
|
|
#define MR_BROADCAST(cnd) pthread_cond_broadcast((cnd))
|
|
#define MR_WAIT(cnd, mtx) pthread_cond_wait((cnd), (mtx))
|
|
#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) \
|
|
( MR_debug_threads ? \
|
|
MR_cond_signal((cnd)) \
|
|
: \
|
|
pthread_cond_signal((cnd)) \
|
|
)
|
|
#define MR_BROADCAST(cnd) \
|
|
( MR_debug_threads ? \
|
|
MR_cond_broadcast((cnd)) \
|
|
: \
|
|
pthread_cond_broadcast((cnd)) \
|
|
)
|
|
#define MR_WAIT(cnd, mtx) \
|
|
( MR_debug_threads ? \
|
|
MR_cond_wait((cnd), (mtx)) \
|
|
: \
|
|
pthread_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_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.
|
|
*/
|
|
|
|
MercuryThread *MR_create_thread(MR_ThreadGoal *);
|
|
void MR_destroy_thread(void *eng);
|
|
extern 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;
|
|
|
|
/*
|
|
** 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) do { } while (0)
|
|
#define MR_BROADCAST(nothing) do { } while (0)
|
|
#define MR_WAIT(no, thing) (0)
|
|
|
|
#define MR_OBTAIN_GLOBAL_LOCK(where) do { } while (0)
|
|
#define MR_RELEASE_GLOBAL_LOCK(where) 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.
|
|
** 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) \
|
|
(MR_THREAD_LOCAL_MUTABLES = tlm)
|
|
|
|
#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 */
|