mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-09 02:43:21 +00:00
The majority of calls to MR_fatal_error do not follow an operation that sets errno, so printing out an error message unrelated to the reason for the fatal error will lead to confusion. It can also cause test failures if errno happens to be set to non-zero some time prior to an expected call to MR_fatal_error. Fixes bug #464. runtime/mercury_misc.c: Don't print value of errno in MR_fatal_error. runtime/mercury_context.c: runtime/mercury_thread.c: Pass strerror strings to MR_fatal_error where appropriate. runtime/mercury_memory_zones.c: runtime/mercury_memory_zones.h: Pass strerror strings to MR_fatal_error following failures of MR_protect_pages. Document that this assumes MR_protect_pages sets errno on error. Skip unnecessary call to sprintf before MR_fatal_error. runtime/mercury_deep_profiling.c: Skip unnecessary call to sprintf before MR_fatal_error. Reduce size of some buffers. runtime/mercury_overflow.c: runtime/mercury_stack_trace.c: Pass a fixed format string to MR_fatal_error just in case the message string may contain percentage signs. runtime/mercury_tabling.c: Skip unnecessary call to sprintf before MR_fatal_error. deep_profiler/timeout.m: library/thread.m: mdbcomp/shared_utilities.m: Pass strerror strings to MR_fatal_error where appropriate. trace/mercury_trace.c: Skip unnecessary call to sprintf before MR_fatal_error. trace/mercury_trace_external.c: Pass a fixed format string to MR_fatal_error just in case.
598 lines
15 KiB
C
598 lines
15 KiB
C
// vim: ts=4 sw=4 expandtab ft=c
|
|
|
|
// Copyright (C) 1997-2001, 2003, 2005-2007, 2009-2011 The University of Melbourne.
|
|
// Copyright (C) 2014, 2016, 2018 The Mercury team.
|
|
// This file is distributed under the terms specified in COPYING.LIB.
|
|
|
|
#include "mercury_imp.h"
|
|
#include "mercury_regs.h"
|
|
#include "mercury_engine.h"
|
|
#include "mercury_memory.h"
|
|
#include "mercury_context.h" // for MR_do_runnext
|
|
#include "mercury_runtime_util.h"
|
|
#include "mercury_thread.h"
|
|
#include "mercury_threadscope.h"
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryThread MR_primordial_thread;
|
|
|
|
MercuryThreadKey MR_exception_handler_key;
|
|
#ifdef MR_THREAD_LOCAL_STORAGE
|
|
__thread MercuryEngine *MR_thread_engine_base;
|
|
#else
|
|
MercuryThreadKey MR_engine_base_key;
|
|
#endif
|
|
MercuryLock MR_global_lock;
|
|
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
static MercuryLock MR_all_engine_bases_lock;
|
|
MercuryEngine **MR_all_engine_bases = NULL;
|
|
static MR_EngineId MR_highest_engine_id;
|
|
#endif
|
|
#endif
|
|
|
|
MR_bool MR_debug_threads = MR_FALSE;
|
|
|
|
MR_Unsigned MR_num_thread_local_mutables = 0;
|
|
|
|
MR_Integer MR_thread_barrier_count;
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryLock MR_thread_barrier_lock;
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
MercuryCond MR_thread_barrier_cond;
|
|
#endif
|
|
#endif
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
MR_Context *MR_thread_barrier_context;
|
|
#endif
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
static MR_bool
|
|
MR_setup_engine_for_threads(MercuryEngine *eng, MR_EngineType engine_type);
|
|
static void
|
|
MR_shutdown_engine_for_threads(MercuryEngine *eng);
|
|
#endif
|
|
|
|
#ifdef MR_LL_PARALLEL_CONJ
|
|
static void *
|
|
MR_create_worksteal_thread_2(void *goal);
|
|
|
|
MercuryThread *
|
|
MR_create_worksteal_thread(void)
|
|
{
|
|
MercuryThread *thread;
|
|
pthread_attr_t attrs;
|
|
int err;
|
|
char errbuf[MR_STRERROR_BUF_SIZE];
|
|
|
|
assert(!MR_thread_equal(MR_primordial_thread, MR_null_thread()));
|
|
|
|
// Create threads in the detached state so that resources will be
|
|
// automatically freed when threads terminate (we don't call
|
|
// pthread_join() anywhere).
|
|
|
|
thread = MR_GC_NEW_ATTRIB(MercuryThread, MR_ALLOC_SITE_RUNTIME);
|
|
pthread_attr_init(&attrs);
|
|
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
|
|
err = pthread_create(thread, &attrs, MR_create_worksteal_thread_2, NULL);
|
|
pthread_attr_destroy(&attrs);
|
|
|
|
#if 0
|
|
fprintf(stderr, "pthread_create returned %d (errno = %d)\n", err, errno);
|
|
#endif
|
|
|
|
if (err != 0) {
|
|
MR_fatal_error("error creating thread: %s",
|
|
MR_strerror(err, errbuf, sizeof(errbuf)));
|
|
}
|
|
|
|
return thread;
|
|
}
|
|
|
|
static void *
|
|
MR_create_worksteal_thread_2(void *arg)
|
|
{
|
|
#ifdef MR_HAVE_THREAD_PINNING
|
|
// TODO: We may use the cpu value returned to determine which CPUs
|
|
// which engines are on. This can help with some interesting work
|
|
// stealing algorithms.
|
|
|
|
MR_pin_thread();
|
|
#endif
|
|
if (! MR_init_thread_inner(MR_use_later, MR_ENGINE_TYPE_SHARED)) {
|
|
MR_fatal_error("Unable to init shared engine thread.");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#endif // MR_LL_PARALLEL_CONJ
|
|
|
|
// This interface is used by generated code and thread.m.
|
|
// Internal code should call MR_init_thread_inner.
|
|
|
|
MR_bool
|
|
MR_init_thread(MR_when_to_use when_to_use)
|
|
{
|
|
#ifdef MR_THREAD_SAFE
|
|
// Check to see whether there is already an engine that is initialized
|
|
// in this thread. If so we just return, there is nothing for us to do.
|
|
|
|
if (MR_thread_engine_base != NULL) {
|
|
return MR_FALSE;
|
|
}
|
|
#endif // MR_THREAD_SAFE
|
|
assert(when_to_use == MR_use_now);
|
|
return MR_init_thread_inner(when_to_use, MR_ENGINE_TYPE_EXCLUSIVE);
|
|
}
|
|
|
|
// Set up a Mercury engine in the current thread.
|
|
|
|
MR_bool
|
|
MR_init_thread_inner(MR_when_to_use when_to_use, MR_EngineType engine_type)
|
|
{
|
|
MercuryEngine *eng;
|
|
|
|
eng = MR_create_engine();
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
if (MR_setup_engine_for_threads(eng, engine_type) == MR_FALSE) {
|
|
MR_destroy_engine(eng);
|
|
return MR_FALSE;
|
|
}
|
|
assert(MR_thread_engine_base == NULL);
|
|
MR_set_thread_engine_base(eng);
|
|
MR_restore_registers();
|
|
#ifdef MR_ENGINE_BASE_REGISTER
|
|
MR_engine_base_word = (MR_Word) eng;
|
|
#endif
|
|
#else
|
|
MR_memcpy(&MR_engine_base, eng, sizeof(MercuryEngine));
|
|
MR_restore_registers();
|
|
#endif
|
|
MR_load_engine_regs(MR_cur_engine());
|
|
|
|
#if defined(MR_LL_PARALLEL_CONJ) && defined(MR_THREADSCOPE)
|
|
// TSC Synchronization is not used, support is commented out.
|
|
// See runtime/mercury_threadscope.h for an explanation.
|
|
//
|
|
if (when_to_use == MR_use_later) {
|
|
MR_threadscope_sync_tsc_slave();
|
|
}
|
|
|
|
#endif
|
|
|
|
switch (when_to_use) {
|
|
case MR_use_later :
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
MR_fatal_error("Sorry, not implemented: "
|
|
"--high-level-code and multiple engines");
|
|
#else
|
|
// This call never returns.
|
|
(void) MR_call_engine(MR_ENTRY(MR_do_idle), MR_FALSE);
|
|
#endif
|
|
return MR_FALSE;
|
|
|
|
case MR_use_now :
|
|
// The following is documented in mercury_engine.h, so any
|
|
// changes here may need changes there as well.
|
|
|
|
if (MR_ENGINE(MR_eng_this_context) == NULL) {
|
|
MR_ENGINE(MR_eng_this_context) =
|
|
MR_create_context("init_thread",
|
|
MR_CONTEXT_SIZE_REGULAR, NULL);
|
|
}
|
|
MR_load_context(MR_ENGINE(MR_eng_this_context));
|
|
#ifdef MR_THREADSCOPE
|
|
MR_threadscope_post_run_context();
|
|
#endif
|
|
MR_save_registers();
|
|
return MR_TRUE;
|
|
|
|
default:
|
|
MR_fatal_error("init_thread was passed a bad value");
|
|
}
|
|
}
|
|
|
|
// Release resources associated with the Mercury engine for this thread.
|
|
|
|
void
|
|
MR_finalize_thread_engine(void)
|
|
{
|
|
#ifdef MR_THREAD_SAFE
|
|
MercuryEngine *eng;
|
|
|
|
eng = MR_thread_engine_base;
|
|
MR_set_thread_engine_base(NULL);
|
|
MR_shutdown_engine_for_threads(eng);
|
|
MR_destroy_engine(eng);
|
|
#endif
|
|
}
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
// Additional setup/shutdown of the engine for threads support.
|
|
|
|
static MR_bool
|
|
MR_setup_engine_for_threads(MercuryEngine *eng, MR_EngineType engine_type)
|
|
{
|
|
MR_bool ok = MR_TRUE;
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
MR_EngineId min;
|
|
MR_EngineId max;
|
|
MR_EngineId id;
|
|
|
|
MR_LOCK(&MR_all_engine_bases_lock, "MR_setup_engine_for_threads");
|
|
|
|
// Allocate an engine id.
|
|
if (engine_type == MR_ENGINE_TYPE_SHARED) {
|
|
min = 0;
|
|
max = MR_num_ws_engines;
|
|
} else {
|
|
min = MR_num_ws_engines;
|
|
max = MR_max_engines;
|
|
}
|
|
for (id = min; id < max; id++) {
|
|
if (MR_all_engine_bases[id] == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (id < max) {
|
|
if (MR_highest_engine_id < id) {
|
|
MR_highest_engine_id = id;
|
|
}
|
|
|
|
eng->MR_eng_id = id;
|
|
eng->MR_eng_type = engine_type;
|
|
eng->MR_eng_victim_counter = (id + 1) % MR_num_ws_engines;
|
|
|
|
MR_all_engine_bases[id] = eng;
|
|
MR_spark_deques[id] = eng->MR_eng_spark_deque;
|
|
|
|
MR_verify_initial_engine_sleep_sync(id);
|
|
|
|
#ifdef MR_THREADSCOPE
|
|
MR_threadscope_setup_engine(eng);
|
|
#endif
|
|
} else {
|
|
#ifdef MR_DEBUG_THREADS
|
|
if (MR_debug_threads) {
|
|
fprintf(stderr, "Exhausted engine ids.\n");
|
|
}
|
|
#endif
|
|
ok = MR_FALSE;
|
|
}
|
|
|
|
MR_UNLOCK(&MR_all_engine_bases_lock, "MR_setup_engine_for_threads");
|
|
#endif
|
|
|
|
return ok;
|
|
}
|
|
|
|
static void
|
|
MR_shutdown_engine_for_threads(MercuryEngine *eng)
|
|
{
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
MR_EngineId id = eng->MR_eng_id;
|
|
|
|
MR_LOCK(&MR_all_engine_bases_lock, "MR_shutdown_engine_for_threads");
|
|
|
|
MR_verify_final_engine_sleep_sync(eng->MR_eng_id, eng->MR_eng_type);
|
|
|
|
assert(MR_all_engine_bases[id] == eng);
|
|
MR_all_engine_bases[id] = NULL;
|
|
|
|
if (MR_highest_engine_id == id) {
|
|
int i;
|
|
for (i = id - 1; i >= 0; i--) {
|
|
if (MR_all_engine_bases[i] != NULL) {
|
|
MR_highest_engine_id = (MR_EngineId) i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(MR_spark_deques[id] == eng->MR_eng_spark_deque);
|
|
MR_spark_deques[id] = NULL;
|
|
|
|
MR_UNLOCK(&MR_all_engine_bases_lock, "MR_shutdown_engine_for_threads");
|
|
#endif
|
|
}
|
|
#endif // MR_THREAD_SAFE
|
|
|
|
#if defined(MR_THREAD_SAFE)
|
|
// XXX: maybe these should only be conditionally compiled when MR_DEBUG_THREADS
|
|
// is also set. - pbone
|
|
|
|
int
|
|
MR_mutex_lock(MercuryLock *lock, const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d locking on %p (%s)\n",
|
|
MR_SELF_THREAD_ID, lock, from);
|
|
fflush(stderr);
|
|
err = pthread_mutex_lock(lock);
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d lock returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
assert(err == 0);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
MR_mutex_unlock(MercuryLock *lock, const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d unlocking on %p (%s)\n",
|
|
MR_SELF_THREAD_ID, lock, from);
|
|
fflush(stderr);
|
|
err = pthread_mutex_unlock(lock);
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d unlock returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
assert(err == 0);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
MR_cond_signal(MercuryCond *cond, const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d signaling %p (%s)\n",
|
|
MR_SELF_THREAD_ID, cond, from);
|
|
fflush(stderr);
|
|
err = pthread_cond_signal(cond);
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d signal returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
assert(err == 0);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
MR_cond_broadcast(MercuryCond *cond, const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d broadcasting %p (%s)\n",
|
|
MR_SELF_THREAD_ID, cond, from);
|
|
fflush(stderr);
|
|
err = pthread_cond_broadcast(cond);
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d broadcast returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
assert(err == 0);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
MR_cond_wait(MercuryCond *cond, MercuryLock *lock, const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d waiting on cond: %p lock: %p (%s)\n",
|
|
MR_SELF_THREAD_ID, cond, lock, from);
|
|
fflush(stderr);
|
|
err = pthread_cond_wait(cond, lock);
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d wait returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
assert(err == 0);
|
|
return err;
|
|
}
|
|
|
|
int
|
|
MR_cond_timed_wait(MercuryCond *cond, MercuryLock *lock,
|
|
const struct timespec *abstime, const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d timed-waiting on cond: %p lock: %p (%s)\n",
|
|
MR_SELF_THREAD_ID, cond, lock, from);
|
|
fflush(stderr);
|
|
err = pthread_cond_timedwait(cond, lock, abstime);
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d timed-wait returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
return err;
|
|
}
|
|
|
|
void
|
|
MR_sem_init(MercurySem *sem, unsigned int value)
|
|
{
|
|
#if defined(MR_USE_LIBDISPATCH)
|
|
*sem = dispatch_semaphore_create(value);
|
|
if (*sem == NULL) {
|
|
MR_perror("cannot initialize semaphore");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#else // !MR_USE_LIBDISPATCH
|
|
// XXX we should check errno and say *why* we could not initialize
|
|
// the semaphore.
|
|
if (sem_init(sem, 0, value) == -1) {
|
|
MR_perror("cannot initialize semaphore");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int
|
|
MR_sem_wait(MercurySem *sem, const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d waiting on sem: %p (%s)\n",
|
|
MR_SELF_THREAD_ID, sem, from);
|
|
fflush(stderr);
|
|
#if defined(MR_USE_LIBDISPATCH)
|
|
err = dispatch_semaphore_wait(*sem, DISPATCH_TIME_FOREVER);
|
|
#else
|
|
err = sem_wait(sem);
|
|
#endif
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d wait returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
MR_sem_timed_wait(MercurySem *sem, const struct timespec *abstime,
|
|
const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d timed wait on sem: %p (%s)\n",
|
|
MR_SELF_THREAD_ID, sem, from);
|
|
fflush(stderr);
|
|
#if defined(MR_USE_LIBDISPATCH)
|
|
err = dispatch_semaphore_wait(*sem, dispatch_walltime(abstime, 0));
|
|
#else
|
|
err = sem_timedwait(sem, abstime);
|
|
#endif
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d timed wait returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
|
|
return err;
|
|
}
|
|
|
|
int
|
|
MR_sem_post(MercurySem *sem, const char *from)
|
|
{
|
|
int err;
|
|
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d posting to sem: %p (%s)\n",
|
|
MR_SELF_THREAD_ID, sem, from);
|
|
fflush(stderr);
|
|
#if defined(MR_USE_LIBDISPATCH)
|
|
err = dispatch_semaphore_signal(*sem);
|
|
#else
|
|
err = sem_post(sem);
|
|
#endif
|
|
fprintf(stderr,
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d post returned %d\n",
|
|
MR_SELF_THREAD_ID, err);
|
|
fflush(stderr);
|
|
|
|
return err;
|
|
}
|
|
|
|
void
|
|
MR_sem_destroy(MercurySem *sem)
|
|
{
|
|
#if defined(MR_USE_LIBDISPATCH)
|
|
dispatch_release(*sem);
|
|
#else
|
|
if (sem_destroy(sem) == -1) {
|
|
MR_perror("cannot destroy semaphore");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// For pthreads-win32 MR_null_thread() is defined as follows. For other
|
|
// pthread implementations it is defined as a macro in mercury_thread.h.
|
|
|
|
#if defined(MR_PTHREADS_WIN32)
|
|
MercuryThread
|
|
MR_null_thread(void)
|
|
{
|
|
const MercuryThread null_thread = {NULL, 0};
|
|
return null_thread;
|
|
}
|
|
#endif // MR_PTHREADS_WIN32
|
|
|
|
#endif // MR_THREAD_SAFE
|
|
|
|
MR_Unsigned
|
|
MR_new_thread_local_mutable_index(void)
|
|
{
|
|
if (MR_num_thread_local_mutables >= MR_MAX_THREAD_LOCAL_MUTABLES-1) {
|
|
MR_fatal_error("too many thread-local mutables");
|
|
}
|
|
return MR_num_thread_local_mutables++;
|
|
}
|
|
|
|
MR_ThreadLocalMuts *
|
|
MR_create_thread_local_mutables(MR_Unsigned numslots)
|
|
{
|
|
MR_ThreadLocalMuts *muts;
|
|
|
|
muts = MR_GC_NEW_ATTRIB(MR_ThreadLocalMuts, MR_ALLOC_SITE_RUNTIME);
|
|
#ifdef MR_THREAD_SAFE
|
|
pthread_mutex_init(&muts->MR_tlm_lock, MR_MUTEX_ATTR);
|
|
#endif
|
|
muts->MR_tlm_values = MR_GC_NEW_ARRAY_ATTRIB(MR_Word, numslots,
|
|
MR_ALLOC_SITE_RUNTIME);
|
|
|
|
return muts;
|
|
}
|
|
|
|
MR_ThreadLocalMuts *
|
|
MR_clone_thread_local_mutables(const MR_ThreadLocalMuts *old_muts)
|
|
{
|
|
MR_ThreadLocalMuts *new_muts;
|
|
MR_Unsigned i;
|
|
|
|
new_muts = MR_create_thread_local_mutables(MR_num_thread_local_mutables);
|
|
|
|
MR_LOCK(&new_muts->MR_tlm_lock, "MR_clone_thread_local_mutables");
|
|
for (i = 0; i < MR_num_thread_local_mutables; i++) {
|
|
new_muts->MR_tlm_values[i] = old_muts->MR_tlm_values[i];
|
|
}
|
|
MR_UNLOCK(&new_muts->MR_tlm_lock, "MR_clone_thread_local_mutables");
|
|
|
|
return new_muts;
|
|
}
|
|
|
|
#ifdef MR_THREAD_SAFE
|
|
void
|
|
MR_init_thread_stuff(void)
|
|
{
|
|
int i;
|
|
|
|
pthread_mutex_init(&MR_global_lock, MR_MUTEX_ATTR);
|
|
#ifndef MR_THREAD_LOCAL_STORAGE
|
|
MR_KEY_CREATE(&MR_engine_base_key, NULL);
|
|
#endif
|
|
MR_KEY_CREATE(&MR_exception_handler_key, NULL);
|
|
pthread_mutex_init(&MR_thread_barrier_lock, MR_MUTEX_ATTR);
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
pthread_cond_init(&MR_thread_barrier_cond, MR_COND_ATTR);
|
|
#endif
|
|
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
pthread_mutex_init(&MR_all_engine_bases_lock, MR_MUTEX_ATTR);
|
|
MR_all_engine_bases =
|
|
MR_GC_malloc(sizeof(MercuryEngine *) * MR_max_engines);
|
|
for (i = 0; i < MR_max_engines; i++) {
|
|
MR_all_engine_bases[i] = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|