blob: e0e57979ca483016232b331a5ce092b09a078b9b [file] [log] [blame]
//===---------------- Threads.h - Micro-vm threads ------------------------===//
//
// The VMKit project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MVM_THREAD_H
#define MVM_THREAD_H
#include <cassert>
#include <cstdio>
#include <stdlib.h>
#include "debug.h"
#include "types.h"
#ifdef RUNTIME_DWARF_EXCEPTIONS
#define TRY try
#define CATCH catch(...)
#define IGNORE catch(...) { mvm::Thread::get()->clearPendingException(); }
#define END_CATCH
#else
#include <csetjmp>
#if defined(__MACH__)
#define TRY { mvm::ExceptionBuffer __buffer__; if (!_setjmp(__buffer__.buffer))
#else
#define TRY { mvm::ExceptionBuffer __buffer__; if (!setjmp(__buffer__.buffer))
#endif
#define CATCH else
#define IGNORE else { mvm::Thread::get()->clearPendingException(); }}
#define END_CATCH }
#endif
namespace mvm {
class gc;
class MethodInfo;
class VirtualMachine;
/// CircularBase - This class represents a circular list. Classes that extend
/// this class automatically place their instances in a circular list.
///
// WARNING: if you modify this class, you must also change mvm-runtime.ll
template<typename T>
class CircularBase {
/// _next - The next object in the list.
///
T *_next;
/// _prev - The previous object in the list.
///
T *_prev;
public:
/// ~CircularBase - Give the class a home.
///
virtual ~CircularBase() {}
/// next - Get the next object in the list.
///
inline T *next() { return _next; }
/// prev - Get the previous object in the list.
///
inline T *prev() { return _prev; }
/// CricularBase - Creates the object as a single element in the list.
///
inline CircularBase() { _prev = _next = (T*)this; }
/// CircularBase - Creates the object and place it in the given list.
///
inline explicit CircularBase(CircularBase<T> *p) { appendTo(p); }
/// remove - Remove the object from its list.
///
inline void remove() {
_prev->_next = _next;
_next->_prev = _prev;
_prev = _next = (T*)this;
}
/// append - Add the object in the list.
///
inline void appendTo(CircularBase<T> *p) {
_prev = (T*)p;
_next = p->_next;
_next->_prev = (T*)this;
_prev->_next = (T*)this;
}
/// print - Print the list for debug purposes.
void print() {
T* temp = (T*)this;
do {
fprintf(stderr, "%p -> ", (void*)temp);
temp = temp->next0();
} while (temp != this);
fprintf(stderr, "\n");
}
};
#if defined(__MACH__) && (defined(__PPC__) || defined(__ppc__))
#define FRAME_IP(fp) (fp[2])
#else
#define FRAME_IP(fp) (fp[1])
#endif
// Apparently gcc for i386 and family considers __builtin_frame_address(0) to
// return the caller, not the current function.
#if defined(__i386__) || defined(i386) || defined(_M_IX86) || \
defined(__x86_64__) || defined(_M_AMD64)
#define FRAME_PTR() __builtin_frame_address(0)
#else
#define FRAME_PTR() (((void**)__builtin_frame_address(0))[0])
#endif
class KnownFrame {
public:
void* currentFP;
void* currentIP;
KnownFrame* previousFrame;
};
class ExceptionBuffer;
class Thread;
class VMKit;
// WARNING: if you modify this class, you must also change mvm-runtime.ll
// WARNING: when a VMThreadData is in a thread (in allVmsData), you must never delete it yourself.
class VMThreadData {
public:
/// mut - The associated thread mutator
Thread* mut;
/// vm - The associated virtual machine
VirtualMachine* vm;
VMThreadData(VirtualMachine *v, Thread* m) {
this->mut = m;
this->vm = v;
}
virtual void tracer(uintptr_t closure) {};
virtual ~VMThreadData() {} // force the construction of a VT
};
#define THREAD_RUNNING 1
#define THREAD_DAEMON 2
/// Thread - This class is the base of custom virtual machines' Thread classes.
/// It provides static functions to manage threads. An instance of this class
/// contains all thread-specific informations.
// WARNING: if you modify this class, you must also change mvm-runtime.ll
class Thread : public CircularBase<Thread> {
public:
/// doYield - Flag to tell the thread to yield for GC reasons.
bool doYield; // 1 - intrinsic
#ifdef RUNTIME_DWARF_EXCEPTIONS
void* internalPendingException;
#else
/// lastExceptionBuffer - The last exception buffer on this thread's stack.
ExceptionBuffer* lastExceptionBuffer; // 2 - intrinsic
#endif
/// vmData - vm specific data - notice that vmkit do not consider that this field has a value
VMThreadData* vmData; // 3 - intrinsic
/// pendingException - the pending exception
gc* pendingException; // 4 - intrinsic
/// vmkit - a (shortcut) pointer to vmkit that contains information on all the vms
mvm::VMKit* vmkit; // 5
/// baseSP - The base stack pointer.
void* baseSP; // 6
/// inRV - Flag to tell that the thread is being part of a rendezvous.
bool inRV; // 7
/// joinedRV - Flag to tell that the thread has joined a rendezvous.
bool joinedRV; // 8
private:
/// lastSP - If the thread is running native code that can not be
/// interrupted, lastSP is not null and contains the value of the
/// stack pointer before entering native.
void* lastSP; // 9
/// internalThreadID - The implementation specific thread id.
void* internalThreadID; // 10
public:
/// routine - The function to invoke when the thread starts.
void (*routine)(mvm::Thread*); // 11
/// lastKnownFrame - The last frame that we know of, before resuming to JNI.
KnownFrame* lastKnownFrame; // 12
/// allVmsData - the array of thread specific data.
VMThreadData** allVmsData; // 13
private:
/// state - daemon, running
uint32 state; // 14
private:
friend class MutatorThread;
Thread(VMKit* vmk);
public:
/// setDaemon - the thread is a daemon
void setDaemon();
/// setDaemon - the thread is not a daemon
void setNonDaemon();
/// getDaemon - get the daemon flag of the thread
uint32 getState() { return state; }
/// yield - Yield the processor to another thread.
///
static void yield(void);
/// kill - Kill the thread with the given pid by sending it a signal.
///
static int kill(void* tid, int signo);
/// kill - Kill the given thread by sending it a signal.
///
int kill(int signo);
/// exit - Exit the current thread.
///
static void exit(int value);
/// start - Start the execution of a thread.
///
virtual int start(void (*fct)(mvm::Thread*));
uint64_t getThreadID() {
return (uint64_t)this;
}
/// get - Get the thread specific data of the current thread.
///
static Thread* get() {
return (Thread*)((uintptr_t)__builtin_frame_address(0) & IDMask);
}
private:
/// internalThreadStart - The implementation sepcific thread starter
/// function.
///
static void internalThreadStart(mvm::Thread* th);
public:
/// tracer - trace the pendingException and the vmData
///
virtual void tracer(uintptr_t closure);
void scanStack(uintptr_t closure);
void* getLastSP() { return lastSP; }
void setLastSP(void* V) { lastSP = V; }
void joinRVBeforeEnter();
void joinRVAfterLeave(void* savedSP);
void enterUncooperativeCode(unsigned level = 0) __attribute__ ((noinline));
void enterUncooperativeCode(void* SP);
void leaveUncooperativeCode();
void* waitOnSP();
/// clearPendingException - Clear any pending exception of the current thread.
void clearPendingException() {
pendingException = 0;
#ifdef RUNTIME_DWARF_EXCEPTIONS
internalPendingException = 0;
#endif
}
/// setException - only set the pending exception
///
Thread* setPendingException(gc *obj);
/// throwIt - Throw a pending exception.
///
void throwIt();
/// getPendingException - Return the pending exception.
///
gc* getPendingException() {
return pendingException;
}
bool isMvmThread() {
if (!baseAddr) return false;
else return (((uintptr_t)this) & MvmThreadMask) == baseAddr;
}
/// baseAddr - The base address for all threads.
static uintptr_t baseAddr;
/// IDMask - Apply this mask to the stack pointer to get the Thread object.
///
#if (__WORDSIZE == 64)
static const uint64_t IDMask = 0xF7FF00000;
#else
static const uint64_t IDMask = 0x7FF00000;
#endif
/// MvmThreadMask - Apply this mask to verify that the current thread was
/// created by Mvm.
///
static const uint64_t MvmThreadMask = 0xF0000000;
/// OverflowMask - Apply this mask to implement overflow checks. For
/// efficiency, we lower the available size of the stack: it can never go
/// under 0xC0000
///
static const uint64_t StackOverflowMask = 0xC0000;
/// stackOverflow - Returns if there is a stack overflow in Java land.
///
bool stackOverflow() {
return ((uintptr_t)__builtin_frame_address(0) & StackOverflowMask) == 0;
}
/// operator new - Allocate the Thread object as well as the stack for this
/// Thread. The thread object is inlined in the stack.
///
void* operator new(size_t sz);
void operator delete(void* th);
virtual ~Thread();
/// printBacktrace - Print the backtrace.
///
void printBacktrace();
/// getFrameContext - Fill the buffer with frames currently on the stack.
///
void getFrameContext(void** buffer);
/// getFrameContextLength - Get the length of the frame context.
///
uint32_t getFrameContextLength();
void startKnownFrame(KnownFrame& F) __attribute__ ((noinline));
void endKnownFrame();
void startUnknownFrame(KnownFrame& F) __attribute__ ((noinline));
void endUnknownFrame();
/// reallocAllVmsData - realloc the allVmsData from old to n or 0 to n if allVmsData=0
/// must be protected by rendezvous.threadLock
///
void reallocAllVmsData(int old, int n);
/// attach - attach the vm specific data of the given virtual machine
///
void attach(VirtualMachine* vm);
};
#ifndef RUNTIME_DWARF_EXCEPTIONS
class ExceptionBuffer {
public:
ExceptionBuffer() {
Thread* th = Thread::get();
previousBuffer = th->lastExceptionBuffer;
th->lastExceptionBuffer = this;
}
~ExceptionBuffer() {
Thread* th = Thread::get();
assert(th->lastExceptionBuffer == this && "Wrong exception buffer");
th->lastExceptionBuffer = previousBuffer;
}
ExceptionBuffer* previousBuffer;
jmp_buf buffer;
};
#endif
/// StackWalker - This class walks the stack of threads, returning a MethodInfo
/// object at each iteration.
///
class StackWalker {
public:
void** addr;
void* ip;
KnownFrame* frame;
mvm::Thread* thread;
StackWalker(mvm::Thread* th) __attribute__ ((noinline));
void operator++();
void* operator*();
MethodInfo* get();
};
} // end namespace mvm
#endif // MVM_THREAD_H