// -*- c++ -*- /* * Jakelib2 - General purpose C++ library * Copyright (C) 2001 Florian Wolff (florian@donuz.de) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: Thread.jlc,v 1.16 2003/10/07 17:42:10 florian Exp $ */ #include "jakelib2.h" #include "jakelib2/lang/Thread.h" #include "jakelib2/lang/Synchronizer.h" #include "jakelib2/lang/Integer.h" #include "jakelib2/lang/System.h" #include "jakelib2/util/ArrayList.h" #include "jakelib2/lang/StackTrace.h" using namespace jakelib::lang; using namespace jakelib::io; using namespace jakelib::util; JAKELIB_IMPLEMENT_CLASS_1("jakelib.lang.Thread", Thread, Object, Runnable); #ifdef HAVE_UNISTD_H # include #endif #ifdef JAKELIB_THREADS Mutex* Thread::globalThreadLock = null; #endif Thread* Thread::mainThread = null; int Thread::highestThreadNumber = 0; ArrayList *Thread::allThreads = null; #ifdef HAVE_PTHREADS pthread_key_t Thread::currentThreadKey; #endif #ifdef HAVE_WINTHREADS DWORD Thread::currentThreadKey; #endif /*****************************************************************************\ * Thread | *****************************************************************************/ #ifdef JAKELIB_THREADS Thread::Thread(jakelib::lang::String* name) { //stackTrace = new StackTrace(); state = THREAD_STATE_INIT; this->target = this; this->interruptFlag = false; #ifdef HAVE_WINTHREADS DWORD id; handle = CreateThread(null, 0, (LPTHREAD_START_ROUTINE)Thread::intThreadRun, this, CREATE_SUSPENDED, &id); if (handle == null) throw new IOException(`"Cannot create thread - error code "` .. (jlong)GetLastError() .. JAKELIB_AT2("jakelib.lang.Thread.Thread")); #endif Sentry sentry(globalThreadLock); threadNumber = highestThreadNumber++; if (name == NULL) this->name = `"Thread-"` .. threadNumber; else this->name = name; } Thread::Thread(Runnable* target, jakelib::lang::String* name) { //stackTrace = new StackTrace(); state = THREAD_STATE_INIT; this->target = target; this->interruptFlag = false; #ifdef HAVE_WINTHREADS handle = INVALID_HANDLE_VALUE; DWORD id; handle = CreateThread(null, 0, (LPTHREAD_START_ROUTINE)Thread::intThreadRun, target, CREATE_SUSPENDED, &id); if (handle == null) throw new IOException(`"Cannot create thread - error code "` .. (jlong)GetLastError() .. JAKELIB_AT2("jakelib.lang.Thread.Thread")); #endif Sentry sentry(globalThreadLock); threadNumber = highestThreadNumber++; if (name == NULL) this->name = `"Thread-"` .. threadNumber; else this->name = name; } #endif Thread::Thread(jboolean) { allThreads->add(this); //stackTrace = new StackTrace(); this->interruptFlag = false; #if defined(HAVE_PTHREADS) pthread_key_create(¤tThreadKey, null); pthread_setspecific(currentThreadKey, this); #endif #ifdef HAVE_WINTHREADS currentThreadKey = TlsAlloc(); TlsSetValue(currentThreadKey, this); #endif threadNumber = -1; name = `"main"`; } /*****************************************************************************\ * run | *****************************************************************************/ void Thread::run() {} #ifdef JAKELIB_THREADS /*****************************************************************************\ * start | *****************************************************************************/ void Thread::start() { if (state != THREAD_STATE_INIT && state != THREAD_STATE_STOPPED) { throw new IllegalThreadStateException(); } state = THREAD_STATE_RUNNING; #if defined(HAVE_WINTHREADS) if (ResumeThread(handle) == 0xFFFFFFFF) throw new IOException(`"Cannot start thread - error code "` .. (jlong)GetLastError() .. JAKELIB_AT2(`"jakelib.lang.Thread.start"`)); #elif defined(HAVE_PTHREADS) int error = pthread_create(&threadInfo, null, Thread::voidThreadRun, this); if (error != 0) throw new IOException(`"Cannot create/start thread - error code "` .. error .. JAKELIB_AT2(`"jakelib.lang.Thread.start"`)); #else # warning "jakelib.lang.Thread.start not supported => Threads not supported!" throw new UnsupportedOperationException(JAKELIB_AT2(`"jakelib.lang.Thread.start"`)); #endif } /*****************************************************************************\ * threadRun | *****************************************************************************/ #ifdef HAVE_WINTHREADS int Thread::intThreadRun (Thread* thread) { TlsSetValue(currentThreadKey, thread); thread->beforeRun(); thread->target->run(); thread->afterRun(); LocalFree((HLOCAL) thread); return 0; } #endif #ifdef HAVE_PTHREADS void* Thread::voidThreadRun(void* _thread) { int error; Thread* thread = (Thread*) _thread; error = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, null); if (error) throw new IOException(`"Unable to set cancellation state for thread - error code "` .. error .. JAKELIB_AT2("jakelib.lang.Thread.threadRun")); error = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, null); if (error) throw new IOException(`"Unable to set cancellation type for thread - error code "` .. error .. JAKELIB_AT2("jakelib.lang.Thread.threadRun")); pthread_setspecific(currentThreadKey, thread); thread->beforeRun(); thread->target->run(); thread->afterRun(); pthread_setspecific(currentThreadKey, null); return null; } #endif /*****************************************************************************\ * yield | *****************************************************************************/ void Thread::yield() { #if defined(HAVE_WINTHREADS) // FIXME: MSVC! //SwitchToThread(); ::Sleep(0); #elif defined(HAVE_PTHREADS) && defined(__USE_GNU) pthread_yield(); #elif defined(_POSIX_PRIORITY_SCHEDULING) sched_yield(); #else # warning "jakelib.lang.Thread.yield not supported." throw new UnsupportedOperationException(`""` .. JAKELIB_AT2(`"jakelib.lang.Thread.yield"`)); #endif } /*****************************************************************************\ * join | *****************************************************************************/ void Thread::join() { if (interrupted()) { throw new InterruptedException(); } #if defined(HAVE_PTHREADS) pthread_join(threadInfo, null); #elif defined(HAVE_WINTHREADS) WaitForSingleObject(handle, INFINITE); #else # warning "jakelib.lang.Thread.join not supported." throw new UnsupportedOperationException(`""` .. JAKELIB_AT2(`"jakelib.lang.Thread.join"`)); #endif } #endif /*****************************************************************************\ * currentThread | *****************************************************************************/ Thread* Thread::currentThread() { #ifdef JAKELIB_THREADS Thread *thread; # if defined(HAVE_PTHREADS) thread = (Thread*)pthread_getspecific(currentThreadKey); # elif defined(HAVE_WINTHREADS) thread = (Thread*) TlsGetValue(currentThreadKey); # else # warning "jakelib.lang.Thread.currentThread not supported." throw new UnsupportedOperationException(JAKELIB_AT2(`"jakelib.lang.Thread.currentThread"`)); # endif if (thread != null) return thread; else throw new IllegalThreadStateException(`"Cannot determine current thread"` .. JAKELIB_AT2(`"jakelib.lang.Thread.currentThread"`)); #else return mainThread; #endif } /*****************************************************************************\ * getName | *****************************************************************************/ String* Thread::getName() { return name; } /*****************************************************************************\ * toString | *****************************************************************************/ String* Thread::toString() { StringBuffer buf; buf.append('['); buf.append(name); buf.append(']'); return buf.toString(); } /*****************************************************************************\ * hashCode | *****************************************************************************/ int Thread::hashCode() { return threadNumber + 1; } /*****************************************************************************\ * initThreadClass | *****************************************************************************/ void Thread::initThreadClass() { #ifdef JAKELIB_THREADS globalThreadLock = new Mutex(); #endif allThreads = new ArrayList(50); mainThread = new Thread(true); } // /*****************************************************************************\ // * getStackTrace | // *****************************************************************************/ // StackTrace* Thread::getStackTrace() // { // return stackTrace; // } // /*****************************************************************************\ // * dumpStack | // *****************************************************************************/ // void Thread::dumpStack() // { // System::out->println(stackTrace); // } /*****************************************************************************\ * sleep | *****************************************************************************/ void Thread::sleep(jlong millis) { if (interrupted()) { throw new InterruptedException(); } #if defined(JAKELIB_WIN32API) Sleep((DWORD) millis); #else usleep(millis * 1000); #endif } #ifdef JAKELIB_THREADS /*****************************************************************************\ * afterRun | *****************************************************************************/ void Thread::afterRun() { Sentry sentry(globalThreadLock); allThreads->remove(this); //stackTrace->destroy(); //stackTrace = null; //printf("Thread::afterRun: %s\n", JAKELIB_LATIN1(allThreads->toString())); } /*****************************************************************************\ * beforeRun | *****************************************************************************/ void Thread::beforeRun() { Sentry sentry(globalThreadLock); allThreads->add(this); //printf("Thread::beforeRun: %s\n", JAKELIB_LATIN1(allThreads->toString())); } /*****************************************************************************\ * interrupt | *****************************************************************************/ void Thread::interrupt() { interruptFlag = true; } /*****************************************************************************\ * isInterrupted | *****************************************************************************/ jboolean Thread::isInterrupted() { return interruptFlag; } /*****************************************************************************\ * interrupted | *****************************************************************************/ jboolean Thread::interrupted() { Thread *t = currentThread(); jboolean flag = t->interruptFlag; t->interruptFlag = false; return flag; } #endif