6bab028494
2009-11-17 Andrew Haley <aph@redhat.com> * posix-threads.cc (park): Rewrite code to handle time. Move mutex lock before the call to compare_and_swap to avoid a race condition. Add some assertions. (unpark): Add an assertion. (init): Move here from posix-threads.h. * include/posix-threads.h (destroy): removed. From-SVN: r154265
385 lines
8.9 KiB
C++
385 lines
8.9 KiB
C++
// -*- c++ -*-
|
|
// posix-threads.h - Defines for using POSIX threads.
|
|
|
|
/* Copyright (C) 1998, 1999, 2001, 2003, 2006 Free Software Foundation
|
|
|
|
This file is part of libgcj.
|
|
|
|
This software is copyrighted work licensed under the terms of the
|
|
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
|
|
details. */
|
|
|
|
#ifndef __JV_POSIX_THREADS__
|
|
#define __JV_POSIX_THREADS__
|
|
|
|
// NOTE: This file may only reference those pthread functions which
|
|
// are known not to be overridden by the Boehm GC. If in doubt, scan
|
|
// boehm-gc/gc.h. This is yucky but lets us avoid including gc.h
|
|
// everywhere (which would be truly yucky).
|
|
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
#include <sysdep/locks.h>
|
|
|
|
//
|
|
// Typedefs.
|
|
//
|
|
|
|
typedef struct _Jv_Thread_t
|
|
{
|
|
// Flag values are defined in implementation.
|
|
int flags;
|
|
|
|
// Actual thread id.
|
|
pthread_t thread;
|
|
|
|
// Java Thread object.
|
|
java::lang::Thread *thread_obj;
|
|
|
|
// Condition variable and corresponding mutex, used to implement the
|
|
// interruptable wait/notify mechanism.
|
|
pthread_cond_t wait_cond;
|
|
pthread_mutex_t wait_mutex;
|
|
|
|
// Next thread for Condition Variable wait-list chain.
|
|
_Jv_Thread_t *next;
|
|
|
|
} _Jv_Thread_t;
|
|
|
|
typedef void _Jv_ThreadStartFunc (java::lang::Thread *);
|
|
|
|
// Condition Variables used to implement wait/notify/sleep/interrupt.
|
|
typedef struct
|
|
{
|
|
// Linked list of Threads that are waiting to be notified.
|
|
_Jv_Thread_t *first;
|
|
|
|
} _Jv_ConditionVariable_t;
|
|
|
|
typedef struct
|
|
{
|
|
// For compatibility, simplicity, and correctness, we do not use the native
|
|
// pthreads recursive mutex implementation, but simulate them instead.
|
|
|
|
// Mutex the thread holds the entire time this mutex is held.
|
|
pthread_mutex_t mutex;
|
|
|
|
// Thread holding this mutex.
|
|
pthread_t owner;
|
|
|
|
// Number of times mutex is held (lock depth). If 0, the lock is not held.
|
|
int count;
|
|
} _Jv_Mutex_t;
|
|
|
|
// This is a convenience function used only by the pthreads thread
|
|
// implementation. This is slow, but that's too bad -- we need to do
|
|
// the checks for correctness. It might be nice to be able to compile
|
|
// this out. Returns 0 if the lock is held by the current thread, and
|
|
// 1 otherwise.
|
|
inline int
|
|
_Jv_MutexCheckMonitor (_Jv_Mutex_t *mu)
|
|
{
|
|
return (mu->owner != pthread_self());
|
|
}
|
|
|
|
// Type identifying a POSIX thread.
|
|
typedef pthread_t _Jv_ThreadDesc_t;
|
|
|
|
inline _Jv_ThreadDesc_t
|
|
_Jv_GetPlatformThreadID(_Jv_Thread_t *t)
|
|
{
|
|
return t->thread;
|
|
}
|
|
|
|
//
|
|
// Signal helpers.
|
|
//
|
|
|
|
void _Jv_BlockSigchld();
|
|
void _Jv_UnBlockSigchld();
|
|
|
|
|
|
//
|
|
// Condition variables.
|
|
//
|
|
|
|
int _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu,
|
|
jlong millis, jint nanos);
|
|
|
|
int _Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu);
|
|
|
|
int _Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu);
|
|
|
|
inline void
|
|
_Jv_CondInit (_Jv_ConditionVariable_t *cv)
|
|
{
|
|
cv->first = 0;
|
|
}
|
|
|
|
//
|
|
// Mutexes.
|
|
//
|
|
|
|
#ifdef LOCK_DEBUG
|
|
# include <stdio.h>
|
|
#endif
|
|
|
|
inline void
|
|
_Jv_MutexInit (_Jv_Mutex_t *mu)
|
|
{
|
|
# ifdef LOCK_DEBUG /* Assumes Linuxthreads */
|
|
pthread_mutexattr_t attr;
|
|
pthread_mutexattr_init(&attr);
|
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
|
|
pthread_mutex_init (&mu->mutex, &attr);
|
|
# else
|
|
pthread_mutex_init (&mu->mutex, 0);
|
|
# endif
|
|
|
|
mu->count = 0;
|
|
mu->owner = 0;
|
|
}
|
|
|
|
extern int _Jv_MutexLock (_Jv_Mutex_t *);
|
|
|
|
inline int
|
|
_Jv_MutexUnlock (_Jv_Mutex_t *mu)
|
|
{
|
|
if (_Jv_MutexCheckMonitor (mu))
|
|
{
|
|
# ifdef LOCK_DEBUG
|
|
fprintf(stderr, "_Jv_MutexUnlock: Not owner\n");
|
|
for (;;) {}
|
|
# endif
|
|
return 1;
|
|
}
|
|
|
|
mu->count--;
|
|
|
|
if (mu->count == 0)
|
|
{
|
|
mu->owner = 0;
|
|
# ifdef LOCK_DEBUG
|
|
int result = pthread_mutex_unlock (&mu->mutex);
|
|
if (0 != result)
|
|
{
|
|
fprintf(stderr, "Pthread_mutex_unlock returned %d\n", result);
|
|
for (;;) {}
|
|
}
|
|
# else
|
|
pthread_mutex_unlock (&mu->mutex);
|
|
# endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifndef LINUX_THREADS
|
|
|
|
// pthread_mutex_destroy does nothing on Linux and it is a win to avoid
|
|
// defining this macro.
|
|
|
|
#define _Jv_HaveMutexDestroy
|
|
|
|
inline void
|
|
_Jv_MutexDestroy (_Jv_Mutex_t *mu)
|
|
{
|
|
pthread_mutex_destroy (&mu->mutex);
|
|
}
|
|
|
|
#endif /* LINUX_THREADS */
|
|
|
|
//
|
|
// Thread creation and manipulation.
|
|
//
|
|
|
|
void _Jv_InitThreads (void);
|
|
|
|
_Jv_Thread_t *_Jv_ThreadInitData (java::lang::Thread *thread);
|
|
void _Jv_ThreadDestroyData (_Jv_Thread_t *data);
|
|
|
|
inline java::lang::Thread *
|
|
_Jv_ThreadCurrent (void)
|
|
{
|
|
extern pthread_key_t _Jv_ThreadKey;
|
|
return (java::lang::Thread *) pthread_getspecific (_Jv_ThreadKey);
|
|
}
|
|
|
|
#ifdef JV_HASH_SYNCHRONIZATION
|
|
// Should be specialized to just load the "current thread" register
|
|
// on platforms that support it. Speed is of the essence. The value
|
|
// of the descriptor is not, so long as there is a one-to-one correspondence
|
|
// to threads.
|
|
|
|
|
|
#ifdef __ia64__
|
|
|
|
typedef size_t _Jv_ThreadId_t;
|
|
|
|
register size_t _Jv_self __asm__("r13");
|
|
// For linux_threads this is really a pointer to its thread data
|
|
// structure. We treat it as opaque. That should also work
|
|
// on other operating systems that follow the ABI standard.
|
|
|
|
// This should become the prototype for machines that maintain a thread
|
|
// pointer in a register.
|
|
inline _Jv_ThreadId_t
|
|
_Jv_ThreadSelf (void)
|
|
{
|
|
return _Jv_self;
|
|
}
|
|
|
|
#define JV_SELF_DEFINED
|
|
|
|
#endif /* __ia64__ */
|
|
|
|
#ifdef __alpha__
|
|
|
|
typedef void *_Jv_ThreadId_t;
|
|
|
|
inline _Jv_ThreadId_t
|
|
_Jv_ThreadSelf (void)
|
|
{
|
|
return __builtin_thread_pointer ();
|
|
}
|
|
|
|
#define JV_SELF_DEFINED
|
|
|
|
#endif /* __alpha__ */
|
|
|
|
#if defined(SLOW_PTHREAD_SELF)
|
|
|
|
#include "sysdep/locks.h"
|
|
|
|
typedef pthread_t _Jv_ThreadId_t;
|
|
|
|
// E.g. on X86 Linux, pthread_self() is too slow for our purpose.
|
|
// Instead we maintain a cache based on the current sp value.
|
|
// This is similar to what's done for thread local allocation in the
|
|
// GC, only far simpler.
|
|
// This code should probably go away when Linux/X86 starts using a
|
|
// segment register to hold the thread id.
|
|
# define LOG_THREAD_SPACING 12
|
|
// If two thread pointer values are closer than
|
|
// 1 << LOG_THREAD_SPACING, we assume they belong
|
|
// to the same thread.
|
|
# define SELF_CACHE_SIZE 1024
|
|
# define SC_INDEX(sp) (((unsigned long)(sp) >> 19) & (SELF_CACHE_SIZE-1))
|
|
// Mapping from sp value to cache index.
|
|
// Note that this is not in any real sense a hash
|
|
// function, since we need to be able to clear
|
|
// all possibly matching slots on thread startup.
|
|
// Thus all entries that might correspond to
|
|
// a given thread are intentionally contiguous.
|
|
// Works well with anything that allocates at least
|
|
// 512KB stacks.
|
|
# define SC_CLEAR_MIN (-16) // When starting a new thread, we clear
|
|
# define SC_CLEAR_MAX 0 // all self cache entries between
|
|
// SC_INDEX(sp)+SC_CLEAR_MIN and
|
|
// SC_INDEX(sp)+SC_CLEAR_MAX to ensure
|
|
// we never see stale values. The
|
|
// current values assume a downward
|
|
// growing stack of size <= 7.5 MB.
|
|
# define BAD_HIGH_SP_VALUE ((size_t)(-1))
|
|
|
|
extern volatile
|
|
struct self_cache_entry {
|
|
size_t high_sp_bits; // sp value >> LOG_THREAD_SPACING
|
|
pthread_t self; // Corresponding thread
|
|
} _Jv_self_cache[];
|
|
|
|
void _Jv_Self_Cache_Init();
|
|
|
|
_Jv_ThreadId_t
|
|
_Jv_ThreadSelf_out_of_line(volatile self_cache_entry *sce,
|
|
size_t high_sp_bits);
|
|
|
|
inline _Jv_ThreadId_t
|
|
_Jv_ThreadSelf (void)
|
|
{
|
|
int dummy;
|
|
size_t sp = (size_t)(&dummy);
|
|
unsigned h = SC_INDEX(sp);
|
|
volatile self_cache_entry *sce = _Jv_self_cache + h;
|
|
pthread_t candidate_self = sce -> self; // Read must precede following one.
|
|
read_barrier();
|
|
if (sce -> high_sp_bits == sp >> LOG_THREAD_SPACING)
|
|
{
|
|
// The sce -> self value we read must be valid. An intervening
|
|
// cache replacement by another thread would have first replaced
|
|
// high_sp_bits by something else, and it can't possibly change
|
|
// back without our intervention.
|
|
return candidate_self;
|
|
}
|
|
else
|
|
return _Jv_ThreadSelf_out_of_line(sce, sp >> LOG_THREAD_SPACING);
|
|
}
|
|
|
|
#define JV_SELF_DEFINED
|
|
|
|
#endif /* SLOW_PTHREAD_SELF */
|
|
|
|
#ifndef JV_SELF_DEFINED /* If all else fails, call pthread_self directly */
|
|
|
|
typedef pthread_t _Jv_ThreadId_t;
|
|
|
|
inline _Jv_ThreadId_t
|
|
_Jv_ThreadSelf (void)
|
|
{
|
|
return pthread_self();
|
|
}
|
|
|
|
#endif /* !JV_SELF_DEFINED */
|
|
|
|
#endif /* JV_HASH_SYNCHRONIZATION */
|
|
|
|
inline _Jv_Thread_t *
|
|
_Jv_ThreadCurrentData (void)
|
|
{
|
|
extern pthread_key_t _Jv_ThreadDataKey;
|
|
return (_Jv_Thread_t *) pthread_getspecific (_Jv_ThreadDataKey);
|
|
}
|
|
|
|
inline void
|
|
_Jv_ThreadYield (void)
|
|
{
|
|
#ifdef HAVE_SCHED_YIELD
|
|
sched_yield ();
|
|
#endif /* HAVE_SCHED_YIELD */
|
|
}
|
|
|
|
void _Jv_ThreadRegister (_Jv_Thread_t *data);
|
|
void _Jv_ThreadUnRegister ();
|
|
|
|
void _Jv_ThreadSetPriority (_Jv_Thread_t *data, jint prio);
|
|
|
|
void _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data,
|
|
_Jv_ThreadStartFunc *meth);
|
|
|
|
void _Jv_ThreadWait (void);
|
|
|
|
void _Jv_ThreadInterrupt (_Jv_Thread_t *data);
|
|
|
|
// park() / unpark() support
|
|
|
|
struct ParkHelper
|
|
{
|
|
volatile obj_addr_t permit;
|
|
pthread_mutex_t mutex;
|
|
pthread_cond_t cond;
|
|
|
|
void init ();
|
|
void deactivate ();
|
|
void destroy ();
|
|
void park (jboolean isAbsolute, jlong time);
|
|
void unpark ();
|
|
};
|
|
|
|
inline void
|
|
ParkHelper::destroy ()
|
|
{
|
|
pthread_mutex_destroy (&mutex);
|
|
pthread_cond_destroy (&cond);
|
|
}
|
|
|
|
#endif /* __JV_POSIX_THREADS__ */
|