Files
mercury/runtime/mercury_thread.h
Paul Bone a071eaba53 Improve thread pinning:
+ Now pins threads intelligently on SMT systems by balancing threads among
      cores.
    + performs fewer migrations when pinning threads (If a thread's current
      CPU is a valid CPU for pinning, then it is not migrated).
    + Handle cases where the user requests more threads than available CPUs.
    + Handle cases where the process is restricted to a subset of CPUs by its
      environment.  (for instance, Linux cpuset(7))

This is largely made possible by the hwloc library
http://www.open-mpi.org/projects/hwloc/  However, hwloc is not required and the
runtime system will fall back to sched_setaffinity(), it will simply be less
intelligent WRT SMT.

runtime/mercury_context.h:
runtime/mercury_context.c:
    Do thread pinning either via hwloc or sched_setaffinity.  Previously only
    sched_setaffinity was used.

    Update thread-pinning algorithm, this:

    Include the general thread pinning code only if MR_HAVE_THREAD_PINNING is
    defined.

    Use a combination of sysconf and sched_getaffinity to detect the number of
    processors when hwloc isn't available.  This makes the runtime compatible
    with Linux cpuset(7) when hwloc isn't available.

configure.in:
Mmake.common.in:
    Detect presence of the hwloc library.

configure.in:
    Detect sched_getaffinity()

aclocal.m4:
acinclude.m4:
    Move aclocal.m4 to acinclude.m4, the aclocal program will build aclocal.m4
    and retrieve macros from the system and the contents of acinclude.m4.

Mmakefile:
    Create a make target for aclocal.m4.

runtime/Mmakefile:
    Link the runtime with libhwloc in low-level C parallel grades.

    Include CFLAGS for libhwloc.

scripts/ml.in:
    Link programs and libraries with libhwloc in low-level C parallel grades.

runtime/mercury_conf.h.in:
    Define MR_HAVE_HWLOC when it is available.

    Define MR_HAVE_SCHED_GETAFFINITY when it is available.

runtime/mercury_conf_param.h:
    Define MR_HAVE_THREAD_PINNING if either hwloc or [sched_setaffinity and
    sched_getaffinity] are available.

runtime/mercury_thread.c:
runtime/mercury_wrapper.c:
    Only call MR_pin_thread and MR_pin_primordial_thread if
    MR_HAVE_THREAD_PINNING is defined.

runtime/mercury_thread.h:
runtime/mercury_context.h:
    Move the declaration of MR_pin_primordial_thread to mercury_context.h from
    mercury_thead.h since it's definition is in mercury_context.c.

    Require MR_HAVE_THREAD_PINNING for the declaration of
    MR_pin_primordial_thread.

runtime/mercury_wrapper.c:
    Conform to changes in mercury_context.h

INSTALL_CVS:
tools/test_mercury
    Run aclocal at the right times while testing Mercury.
2011-10-13 02:42:21 +00:00

358 lines
13 KiB
C

/*
** vim: ts=4 sw=4 expandtab
*/
/*
** Copyright (C) 1997-1998, 2000, 2003, 2005-2007, 2009-2011 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>
#include <semaphore.h> /* POSIX semaphores */
#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;
typedef sem_t MercurySem;
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);
extern int
MR_sem_wait(MercurySem *sem, const char *from);
extern int
MR_sem_post(MercurySem *sem, 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))
#if defined(MR_PTHREADS_WIN32)
#define MR_SELF_THREAD_ID ((long) pthread_self().p)
#else
#define MR_SELF_THREAD_ID ((long) pthread_self())
#endif
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))
#define MR_SEM_POST(sem, from) sem_post((sem))
#define MR_SEM_WAIT(sem, from) sem_wait((sem))
#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)) \
)
#define MR_SEM_WAIT(sem, from) \
( MR_debug_threads ? \
MR_sem_wait((sem), (from)) \
: \
sem_wait((sem)) \
)
#define MR_SEM_POST(sem, from) \
( MR_debug_threads ? \
MR_sem_post((sem), (from)) \
: \
sem_post((sem)) \
)
#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);
/*
** 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)
/*
** Initialise some static structures in mercury_thread.c
*/
void
MR_init_thread_stuff(void);
#endif /* MERCURY_THREAD_H */