// ********************************************************************** // // Copyright (c) 2000 // Object Oriented Concepts, Inc. // Billerica, MA, USA // // All Rights Reserved // // ********************************************************************** #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_POSIX_THREADS) || defined(HAVE_DCE_THREADS) # ifdef HAVE_SCHED_H # include # else # ifdef HAVE_SYS_SCHED_H # include # endif # endif # include #endif #if defined(HAVE_POSIX_THREADS) # include # include #endif #if defined(WIN32) # include # include #endif #if defined(HAVE_EXCEPTION) # include #endif #ifdef HAVE_STD_IOSTREAM using namespace std; #endif #if defined(HAVE_EXCEPTION_H) # include #elif defined(HAVE_UNEXPECTED_H) # include #endif #ifdef HAVE_TERMINATE_H # include #endif #ifndef HAVE_NO_EXPLICIT_TEMPLATES template class JTCHandleT; #else # ifdef HAVE_PRAGMA_DEFINE # pragma define(JTCHandleT) # endif #endif #ifndef HAVE_NO_EXPLICIT_TEMPLATES template class JTCHandleT; #else # ifdef HAVE_PRAGMA_DEFINE # pragma define(JTCHandleT) # endif #endif // // This class keeps track of the number of running threads. When the // last JTCInititalize object is destroyed it waits for all running // threads to terminate. // class JTCThreadCounter : public JTCMonitor { int threads_; public: JTCThreadCounter() : threads_(0) { } // // Called when each new thread is started. // void threadStart() { JTCSynchronized guard(*this, true); ++threads_; } // // Called when each thread is terminated. If there are no more // running threads then notify any threads waiting in waitZero. // void threadEnd() { JTCSynchronized guard(*this, true); --threads_; if(threads_ == 0) { notify(); } } // // Wait for all threads to terminate. // void waitZero() { JTCSynchronized guard(*this, true); while(threads_ > 0) { try { wait(); } catch(const JTCInterruptedException&) { } } } }; // // Global instance JTCThreadCounter. // static JTCThreadCounter* threadCounter = 0; static size_t initialStackSize = 0; #if defined(__sgi) && !defined(__GNUC__) static long _jtc_threadId() { return pthread_self(); } #endif // // This is used as a hook from the WIN32 threading library, and the // POSIX threading library into JTC. It's better not to use a static // member of the JTCThread class since they are not guaranteed to be C // callable. // #ifndef WIN32 extern "C" #else static #endif void* _JTC_threadAdapter(void* arg) { // // Ensure that unexpected exceptions cause an abort. // set_terminate(abort); set_unexpected(abort); #if defined(__sgi) && !defined(__GNUC__) // // SGI needs this to handle multiple exceptions in threads. // set_thread_id_function(_jtc_threadId); #endif // // The arg is a pointer to the JTCThread class. // JTCThread* thr = (JTCThread*)arg; try { // // Invoke the _JTC_startThreadHook. // thr -> _JTC_startThreadHook(); } catch(...) { } try { // // Call the exit method of the thread. // thr -> _JTC_exit(); } catch(...) { } // // // No return value. // return 0; } // ---------------------------------------------------------------------- // JTCThread private member implementation // ---------------------------------------------------------------------- // // TSS key. This key is used to store a pointer to the JTCThread // object. // JTCThreadKey JTCThread::thrKey_; #if defined(HAVE_POSIX_THREADS) || defined(HAVE_DCE_THREADS) JTCAttrHook JTCThread::attrHook_ = 0; #endif #ifdef HAVE_JTC_STOP // // Check the running status of this thread. This determines whether // the thread needs to be suspended, or terminated. // void JTCThread::_checkRunningStatus() const { JTCThread* This = (JTCThread*)this; JTCSynchronized guard(resumeMut_); // // If this thread is the same as the current thread, and the // thread isn't dead yet then check suspended, and terminated. // if(getId() == JTCThreadId::self() && state_ != JTCThread::Dead) { // // Thread suspended? // if(This -> suspended_) { // // If so then wait for the resume condition variable to be // signalled. // This -> resumeCond_.wait(); } // // Thread terminated, but ThreadDeath exception not thrown // yet? // if(This -> terminated_ == terminated) { // // If so note that the ThreadDeath exception has been // thrown, and throw the exception. // This -> terminated_ = throw_termination; throw JTCThreadDeath(); } } } // // Set the monitor of the current thread. This is used for suspend and // stop functionality. If the thread currently has a monitor then we // need to signal it so that the thread can be terminated in a timely // fashion. // void JTCThread::setMonitor(JTCMonitor* monitor) { JTCSynchronized guard(monMutex_); monitor_ = monitor; } // // Get the monitor of the current thread. // JTCMonitor* JTCThread::getMonitor() { JTCSynchronized guard(monMutex_); return monitor_; } #endif // // IOstream operator for pointer to void function taking a pointer // to void. // /* ostream& operator<<(ostream& os, void * (fn)(void *)) { os << (void*)fn; return os; } */ #if defined(HAVE_POSIX_THREADS) #define PTHREAD_ATTR_INIT(a) pthread_attr_init(a) #define PTHREAD_ATTR_DESTROY(a) pthread_attr_destroy(a) #define PTHREAD_CREATE(a,b,c,d) \ JTC_SYSCALL_4(pthread_create, &a, &b, c, d,!= 0) #endif #if defined(HAVE_DCE_THREADS) #define PTHREAD_ATTR_INIT(a) pthread_attr_create(a) #define PTHREAD_ATTR_DESTROY(a) pthread_attr_delete(a) #define PTHREAD_CREATE(a,b,c,d) \ JTC_SYSCALL_4(pthread_create, &a, b, c, d,!= 0) #endif // // Start the thread. The entry point into the JTC library is the C // call-style function _JTC_threadAdapter. This in turn calls // _JTC_startThreadHook. // void JTCThread::spawnThread() { #if defined(HAVE_POSIX_THREADS) || defined(HAVE_DCE_THREADS) pthread_attr_t attr; try { JTC_SYSCALL_1(PTHREAD_ATTR_INIT, &attr, != 0) } catch(const JTCSystemCallException& e) { if(e.getError() == ENOMEM) { throw JTCOutOfMemoryError(e.getMessage()); } throw; } # if defined(_AIX) // // On AIX threads are created by default DETACHED. This makes // it kinda tough to join with them. Best change this. // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_UNDETACHED); # endif # ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE if(initialStackSize != 0) pthread_attr_setstacksize(&attr, initialStackSize); # endif // // Call the custom pthread attribute hook, if defined. // if(attrHook_ != 0) (*attrHook_)(&attr); pthread_t threadId; try { PTHREAD_CREATE(threadId, attr, _JTC_threadAdapter, this) } catch(const JTCSystemCallException& e) { PTHREAD_ATTR_DESTROY(&attr); if(e.getError() == EAGAIN || e.getError() == ENOMEM) { throw JTCOutOfMemoryError(e.getMessage()); } throw; } PTHREAD_ATTR_DESTROY(&attr); thrId_ = JTCThreadId(threadId); #endif #if defined(HAVE_WIN32_THREADS) DWORD threadId; HANDLE handle; try { JTC_SYSCALL_6( handle = (HANDLE)::_beginthreadex, NULL, initialStackSize, (unsigned (__stdcall*)(void*))_JTC_threadAdapter, (LPVOID)this, 0, (unsigned int*)&threadId, == NULL) } catch(const JTCSystemCallException& e) { if(e.getError() == ERROR_NOT_ENOUGH_MEMORY || e.getError() == ERROR_OUTOFMEMORY) { throw JTCOutOfMemoryError(e.getMessage()); } throw; } thrId_ = JTCThreadId(threadId, handle); #endif // // Wait for the TSS data to be set by the running thread. // setInitialData_.wait(); } // // Helper for the constructor. // void JTCThread::init(const JTCThreadGroupHandle& group, JTCRunnableHandle target, const char* name) { // // Set default values for the thread state flags. // terminated_ = not_terminated; #ifdef HAVE_JTC_STOP monitor_ = 0; suspended_ = false; #endif name_ = 0; target_ = target; state_ = NewThread; thrId_ = JTCThreadId(); ++_jtc_refCount_; // We're referencing outself. Boost the reference count. adopted_ = false; // The thread was not adopted // // Group of 0 means that we inherit the thread group from the creating // thread. // group_ = group; if(group_.get() == 0) { group_ = currentThread() -> getThreadGroup(); } // // We create the thread on construction. Note that the thread // doesn't invoke run until JTCThread::start is called. We cannot // start the thread until a later point since we need the thread // id one the constructor is called. // spawnThread(); // // Set the threads name. // setName(name); // // Add this thread to the group. // group_ -> add(this); } // ---------------------------------------------------------------------- // JTCThread private constructor // ---------------------------------------------------------------------- // // This is called to initialize the JTC thread system. // JTCThread::JTCThread(JTCThreadId id, bool main) : name_(0), #ifdef HAVE_JTC_STOP resumeCond_(resumeMut_), #endif joinCond_(joinMut_) { // // We need to create the key that contains the current thread // data. // if(main) { thrKey_ = JTCTSS::allocate(); adopted_ = false; } else adopted_ = true; // // Initialize to default state. Remember that this thread doesn't // need to be spawned since it represents the main thread of the // application. // // // Set default values for the thread state variables. // #ifdef HAVE_JTC_STOP suspended_ = false; monitor_ = 0; #endif terminated_ = not_terminated; thrId_ = id; state_ = JTCThread::Runnable; // // Set the TSS data for this thread. // JTCTSS::set(thrKey_, this); setName(0); ++_jtc_refCount_; // We're referencing outself. Boost the reference count. // // Create the default `system' thread group. // group_ = new JTCThreadGroup(main); group_ -> add(this); // // The main thread runs at normal priority. This may or may not be // the default for the OS. // setPriority(JTC_NORM_PRIORITY); } // ---------------------------------------------------------------------- // JTCThread constructor and destructor // ---------------------------------------------------------------------- JTCThread::JTCThread(const char* name) : #ifdef HAVE_JTC_STOP resumeCond_(resumeMut_), #endif joinCond_(joinMut_) { init(JTCThreadGroupHandle(0), JTCRunnableHandle(0), name); } JTCThread::JTCThread(JTCRunnableHandle target, const char* name) : #ifdef HAVE_JTC_STOP resumeCond_(resumeMut_), #endif joinCond_(joinMut_) { init(JTCThreadGroupHandle(0), target, name); } JTCThread::JTCThread(JTCThreadGroupHandle& group, const char* name) : #ifdef HAVE_JTC_STOP resumeCond_(resumeMut_), #endif joinCond_(joinMut_) { init(group, JTCRunnableHandle(0), name); } JTCThread::JTCThread(JTCThreadGroupHandle& group, JTCRunnableHandle target, const char* name) : #ifdef HAVE_JTC_STOP resumeCond_(resumeMut_), #endif joinCond_(joinMut_) { init(group, target, name); } JTCThread::~JTCThread() { try { // // The group needs to be defererences *before* the Thread // object is destructed since if the ThreadGroup is // destroyed a synchronization primitive is called // which uses _JTC_checkRunningStatus. The bad news // is at this point the thread has been destructed. // group_._jtc_deref(); delete[] name_; } catch(...) { } } // ---------------------------------------------------------------------- // JTCThread public member implementation // ---------------------------------------------------------------------- // // Get a handle to this threads ThreadGroup. // JTCThreadGroupHandle JTCThread::getThreadGroup() { return group_; } // // Set the name of the current thread. // void JTCThread::setName(const char* name) { char buf[1024]; // // 0 means use the default name of the thread. That is // Thread- // if(name == 0) { ostrstream os(buf, sizeof(buf)); os << "Thread-" << thrId_ << ends; name = buf; } JTCSynchronized guard(resumeMut_); char* newName = new char[strlen(name) + 1]; strcpy(newName, name); delete[] name_; name_ = newName; } // // Return the name of the thread. // const char* JTCThread::getName() const { JTCSynchronized guard(resumeMut_); return name_; } #ifdef HAVE_JTC_STOP // // Suspend execution of this thread. // void JTCThread::suspend() { JTCSynchronized guard(resumeMut_); _checkRunningStatus(); if(!suspended_) { suspended_ = true; if(getId() == JTCThreadId::self()) { resumeCond_.wait(); _checkRunningStatus(); } } } // // Resume execution of this thread. // void JTCThread::resume() { JTCSynchronized guard(resumeMut_); if(suspended_) { resumeCond_.broadcast(); suspended_ = false; } } // // Stop execution of this thread. // void JTCThread::stop() { JTCMonitor* currentMonitor = 0; { JTCSynchronized guard(resumeMut_); // // First check if the thread hasn't already been terminated. // if(terminated_ == not_terminated) { // // Mark state as terminated, but ThreadDeath exception not // yet thrown. // terminated_ = terminated; // // Terminate immediately if the calling thread is stopping // itself. // if(getId() == JTCThreadId::self() && terminated_ == not_terminated) { terminated_ = throw_termination; throw JTCThreadDeath(); } // // these conditions are all mutually exclusive. the // thread cannot be stopping itself if it hasn't been // started. The thread cannot be suspended if it's // stopping itself, or if it hasn't yet been started. // else if(state_ == JTCThread::NewThread) { // // If the thread hasn't been started yet, then start // it. // startRequest_.post(); } else if (suspended_) { resume(); } else { currentMonitor = getMonitor(); } } } // // If the thread has an associated monitor, then notify to allow // termination in a timely fashion. Note that this needs to be // notified outside of the resumeMut_ mutex lock. // if(currentMonitor != 0) { JTCSynchronized monGuard(*currentMonitor, true); currentMonitor -> notifyAll(); } } #endif // // Start execution of the thread. // void JTCThread::start() { if(state_ != JTCThread::NewThread) { throw JTCIllegalThreadStateException("state is not NewThread"); } // // State is runnable. // state_ = JTCThread::Runnable; // // Tell the thread counter that a new thread has started. // We don't up the running thread count until *after* // start() has been called since we don't want unstarted // threads to block JTCInitialize::~JTCInitialize() // threadCounter -> threadStart(); startRequest_.post(); } // // The run method of the thread. If the thread has an associated // target (that is the thread has a Runnable object), then invoke it's // run method. // void JTCThread::run() { if(target_) { target_ -> run(); } } // // The thread is alive if runnable or not runnable. // bool JTCThread::isAlive() const { JTCSynchronized guard(resumeMut_); return state_ == JTCThread::Runnable || state_ == JTCThread::NotRunnable; } // // Wait for termination of this thread. // void JTCThread::join() { join(0,0); } static unsigned long currentTimeMillis() { unsigned long t; #ifdef WIN32 struct _timeb timebuffer; _ftime(&timebuffer); t = timebuffer.time * 1000; t += timebuffer.millitm; #else struct timeval tv; gettimeofday(&tv, 0); t = tv.tv_sec * 1000; t += tv.tv_usec/1000; #endif return t; } // // Wait at most millis milliseconds for termination of this thread. // void JTCThread::join(long millis) { if(millis < 0) { throw JTCIllegalArgumentException ("timeout value is negative"); } #ifdef HAVE_JTC_STOP _checkRunningStatus(); #endif unsigned long base = currentTimeMillis(); unsigned long now = 0; JTCSynchronized guard(joinMut_); if(millis == 0) { while(isAlive()) { joinCond_.wait(); } } else { while(isAlive()) { long delay = millis - now; if(delay <= 0) { break; } joinCond_.wait(delay); now = currentTimeMillis() - base; } } #ifdef HAVE_JTC_STOP _checkRunningStatus(); #endif } // // Wait at most millis milliseconds plus nanos nanoseconds for termination // of this thread. // void JTCThread::join(long millis, int nanos) { if(millis < 0) { throw JTCIllegalArgumentException ("timeout value is negative"); } if(nanos > 999999) { throw JTCIllegalArgumentException ("nanosecond timeout out of range"); } if(nanos >= 500000 || (nanos != 0 && millis == 0)) { ++millis; } join(millis); } static int levelMap[] = { #if defined(HAVE_DCE_THREADS) PRI_OTHER_MIN, ( PRI_OTHER_MIN + PRI_OTHER_MAX + 1 ) / 2, PRI_OTHER_MAX #elif defined(HAVE_WIN32_THREADS) THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_HIGHEST #else // defined(HAVE_POSIX_THREADS) # if defined(__sun) && defined(__SVR4) // // Solaris. // 5, 3, 1 # elif defined(HAVE_FSU_THREADS) 0, 1, 2 # elif defined(__linux__) // // Linux has no priorities under LinuxThreads. // 0, 0, 0 # elif defined(__sgi) -20, 0, 20 # elif defined(_AIX) // // AIX doesn't appear to have priorities either. // //PTHREAD_PRIO_MIN, //( PTHREAD_PRIO_MIN + PTHREAD_PRIO_MAX ) / 2, //PTHREAD_PRIO_MAX 1, 1, 1 # elif defined(__osf__) PRI_OTHER_MIN, ( PRI_OTHER_MIN + PRI_OTHER_MAX + 1 ) / 2, PRI_OTHER_MAX #elif defined(__hpux) && defined(HAVE_POSIX_THREADS) // // No thread priorities under HPUX 11.x. // 0, 0, 0 # else # error "No known priority levels!" # endif #endif }; #if defined(HAVE_FSU_THREADS) ostream& operator<<(ostream& os, const pthread_attr_t& attr) { os << "sched: " << attr.sched << " prio: " << attr.prio; return os; } #endif // // Set the threads priority. // void JTCThread::setPriority(int newPri) { if(newPri < JTC_MIN_PRIORITY || newPri > JTC_MAX_PRIORITY) { throw JTCIllegalArgumentException("priority out of range"); } if(newPri > group_ -> getMaxPriority()) { newPri = group_ -> getMaxPriority(); } #if defined(HAVE_POSIX_THREADS) # if defined(HAVE_FSU_THREADS) pthread_attr_t attr; JTC_SYSCALL_2(pthread_getschedattr, thrId_, &attr, != 0); attr.prio = levelMap[newPri]; JTC_SYSCALL_2(pthread_setschedattr, thrId_, attr, != 0); # elif !defined(__hpux) sched_param param; int policy = 0; JTC_SYSCALL_3(pthread_getschedparam, thrId_, &policy, ¶m, != 0) param.sched_priority = levelMap[newPri]; JTC_SYSCALL_3(pthread_setschedparam, thrId_, policy, ¶m, != 0) # endif #endif #if defined(HAVE_DCE_THREADS) JTC_SYSCALL_2(pthread_setprio, thrId_, levelMap[newPri], == -1) #endif #if defined(HAVE_WIN32_THREADS) JTC_SYSCALL_2(SetThreadPriority, thrId_.handle_, levelMap[newPri], == 0) #endif } // // Get the threads priority. // int JTCThread::getPriority() const { #if defined(HAVE_POSIX_THREADS) || defined(HAVE_DCE_THREADS) int prio; # if defined(HAVE_FSU_THREADS) pthread_attr_t attr; JTC_SYSCALL_2(pthread_getschedattr, thrId_, &attr, != 0) prio = attr.prio; # elif defined(HAVE_POSIX_THREADS) sched_param param; int policy; JTC_SYSCALL_3(pthread_getschedparam, thrId_, &policy, ¶m, != 0) prio = param.sched_priority; # elif defined(HAVE_DCE_THREADS) JTC_SYSCALL_1(prio = pthread_getprio, thrId_, != 0) # endif if(prio <= 0) { return JTC_MIN_PRIORITY; } else if(prio == 1) { return JTC_NORM_PRIORITY; } return JTC_MAX_PRIORITY; #endif #if defined(HAVE_WIN32_THREADS) DWORD rc; JTC_SYSCALL_1(rc = GetThreadPriority, thrId_.handle_, == THREAD_PRIORITY_ERROR_RETURN); switch(rc) { case THREAD_PRIORITY_HIGHEST: case THREAD_PRIORITY_ABOVE_NORMAL: return JTC_MAX_PRIORITY; case THREAD_PRIORITY_NORMAL: return JTC_NORM_PRIORITY; case THREAD_PRIORITY_BELOW_NORMAL: case THREAD_PRIORITY_LOWEST: return JTC_MIN_PRIORITY; } return JTC_NORM_PRIORITY; #endif } // // Enumerate the set of threads in this group. // int JTCThread::enumerate(JTCThreadHandle* list, int len) { return currentThread() -> getThreadGroup() -> enumerate(list, len); } // // Get a pointer to the current thread. // JTCThread* JTCThread::currentThread() { JTCThread* current = (JTCThread*)JTCTSS::get(JTCThread::thrKey_); if(current == 0) throw JTCUnknownThreadException(); return current; } #if defined(HAVE_MIT_THREADS) extern "C" { void usleep(unsigned); }; int nanosleep(struct timespec* tv, int) { unsigned nsecs = tv -> tv_sec * 1000000; nsecs += tv -> tv_nsec / 1000; usleep(nsecs); return 0; } #endif // // Put the current thread to sleep for millis millseconds and nano // nanoseconds. // void JTCThread::sleep(long millis, int nano) { #ifdef HAVE_JTC_STOP _JTC_checkRunningStatus(); #endif if(millis < 0) { throw JTCIllegalArgumentException ("timeout value is negative"); } if(nano > 999999) { throw JTCIllegalArgumentException ("nanosecond timeout out of range"); } //state_ = JTCThread::NotRunnable; #if defined(HAVE_PTHREAD_DELAY_NP) struct timespec tv; tv.tv_sec = millis/1000; tv.tv_nsec = (millis% 1000)*1000000 + nano; JTC_SYSCALL_1(pthread_delay_np, &tv, != 0); #elif defined(HAVE_POSIX_THREADS) struct timespec tv; tv.tv_sec = millis/1000; tv.tv_nsec = (millis% 1000)*1000000 + nano; if(nanosleep(&tv, 0) < 0 && errno == EINTR) { throw JTCInterruptedException(); } #endif #if defined(HAVE_WIN32_THREADS) Sleep(millis); #endif //state_ = JTCThread::Runnable; #ifdef HAVE_JTC_STOP _JTC_checkRunningStatus(); #endif } // // Provide definition for sched_yield under SGI. // #ifdef __sgi extern "C" { int sched_yield(void); }; #endif // // Yield the current threads timeslice. // void JTCThread::yield() { #ifdef HAVE_JTC_STOP _JTC_checkRunningStatus(); #endif #if defined(HAVE_POSIX_THREADS) || defined(HAVE_DCE_THREADS) # if defined(HAVE_SCHED_YIELD) sched_yield(); # elif defined(HAVE_PTHREAD_YIELD) pthread_yield(); # endif #endif #if defined(HAVE_WIN32_THREADS) Yield(); #endif #ifdef HAVE_JTC_STOP _JTC_checkRunningStatus(); #endif } // // Return the number of active threads in the current thread group. // int JTCThread::activeCount() { return currentThread() -> getThreadGroup() -> activeCount(); } // // Get the id of this thread. thrId_ is immutable so this method // doesn't need to be mutex protected. // JTCThreadId JTCThread::getId() const { return thrId_; } #if defined(HAVE_POSIX_THREADS) || defined(HAVE_DCE_THREADS) void JTCThread::setAttrHook(JTCAttrHook attrHook) { attrHook_ = attrHook; } #endif // // This method is called by the external "C" calling function used by // the native thread package. This is the entrance point from the // native thread library into the sane world of JTC. // void JTCThread::_JTC_startThreadHook() { // // Flag to indicate that we want to lower the thread counter // at thread termination. // bool lowerCounter = false; try { // // Set the TSS data for this thread. This should a pointer to // this thread data. // JTCTSS::set(thrKey_, this); setInitialData_.post(); //cout << "Start: " << name_ << endl; // // Wait for the user to actually `start' the thread. That is // call the start method for this thread object. // startRequest_.wait(); // // We want to lower the threadCounter if JTCThread::start() // has been called. // lowerCounter = (state_ != JTCThread::NewThread); // // Make sure that the thread didn't `start' because the user // stopped the thread. // if(terminated_ == not_terminated) { // // It's not possible to set the priority until after the // start request has been sent, since under WIN32 the handle // may not have been set yet. // setPriority(JTC_NORM_PRIORITY); // // Call the run method of the thread. // run(); } } catch(const JTCThreadDeath&) { // // This exception is thrown when stop() is called. // } catch(const JTCException& e) { group_ -> uncaughtException(this, e); } catch(...) { group_ -> uncaughtException(this); } #if defined(HAVE_WIN32_THREADS) // // Under WIN32 we must now close the thread handle, or suffer // the consequences. // CloseHandle(thrId_.handle_); thrId_.handle_ = 0; #endif // // Run the TSS cleanup hooks. The TSSManager should always // be present at this point. Note that the thrKey_ is cleared // out below and not at this point. However, this shouldn't make // any difference since the thrKey_ doesn't have an associated // data destructor. // JTCTSSManager* manager = JTCTSSManager::instance(); assert(manager != 0); manager -> cleanup(); if (lowerCounter) { // // Inform the counter that this thread died. This has to be // done before _JTC_exit is called since the current thread // object could be deleted if the reference count drops to // zero. // threadCounter -> threadEnd(); } } void JTCThread::_JTC_exit() { { // // Set the state to Dead. This needs to be done before the // removal from the ThreadGroup since this eventually ends up // calling _checkRunningStatus, which if the state isn't dead // may end up throwing a JTCThreadDeath exception. We'll keep // the join and internal mutex locked to stop a join from // proceeding until _JTC_exit is completed. Ensure that // joinMut_ is aquired before resumeMut_ else a race can occur // between isAlive() and join(). // JTCSynchronized joinGuard(joinMut_); JTCSynchronized guard(resumeMut_); state_ = JTCThread::Dead; joinCond_.broadcast(); // // Detach the thread, assuming it was not adopted. That is // ensures that the thread resources are freed once the thread // terminates. // if(!adopted_) { #if defined(HAVE_DCE_THREADS) pthread_t id = thrId_; JTC_SYSCALL_1(pthread_detach, &id, != 0) #elif defined(HAVE_POSIX_THREADS) JTC_SYSCALL_1(pthread_detach, thrId_, != 0) #endif } // // Remove this thread from its ThreadGroup. // group_ -> remove(this); } // // Lower the reference count. // if (--_jtc_refCount_ == 0) delete this; // // Clear the current value after the reference count has been // lowered. This ensures that the currentThread is available in // the thread objects destructor. // JTCTSS::set(thrKey_, 0); } #ifdef HAVE_JTC_STOP // // Check the running status of the currently active thread. // void JTCThread::_JTC_checkRunningStatus() { JTCThreadHandle current((JTCThread*)JTCTSS::get(JTCThread::thrKey_)); if (current) current -> _checkRunningStatus(); } // // Set a monitor for the currently running thread. // void JTCThread::_JTC_setMonitor(JTCMonitor* monitor) { JTCThreadHandle current((JTCThread*)JTCTSS::get(JTCThread::thrKey_)); if (current) current -> setMonitor(monitor); } #endif // // IOstream operator for the thread class. // ostream& operator<<(ostream& os, const JTCThread& thr) { os << "Thread: " << thr.getName(); return os; } // // This represents the number of times that the thread has been // initialized. // int JTCInitialize::init_ = 0; // ---------------------------------------------------------------------- // JTCThread private member implementation // ---------------------------------------------------------------------- void JTCInitialize::initialize() { manager_ = new JTCTSSManager; // // Create ThreadCounter. // threadCounter = new JTCThreadCounter; try { // // Create entry for main thread. // mainThread_ = new JTCThread(JTCThreadId::self(), true); } catch(const JTCSystemCallException& e) { cerr << "JTC failed initialization:\n" << "\t" << e.getMessage() << endl; abort(); } catch(...) { cerr << "Caught unexpected exception in initialization" << endl; abort(); } } // ---------------------------------------------------------------------- // JTCInitialize constructor and destructor // ---------------------------------------------------------------------- JTCInitialize::JTCInitialize() { if(init_++ != 0) { return; } initialize(); } JTCInitialize::JTCInitialize(int& argc, char** argv) { // // Only initialize on first call. Subsequent attempts do nothing. // if(init_++ != 0) { return; } int i = 0; while(i < argc) { // // Initial stack size in kilobytes. // if(strcmp(argv[i], "-JTCss") == 0) { int ssTmp; if(i + 1 < argc) ssTmp = atoi(argv[i + 1]); else { cerr << "JTCInitialize:: argument expected for -JTCss" << endl; exit(1); } if(ssTmp <= 0) { cout << "JTCInitialize:: stack size must be positive." << endl; exit(1); } initialStackSize = ssTmp * 1024; for(int j = i ; j + 2 < argc ; j++) argv[j] = argv[j + 2]; argc -= 2; } else if(strcmp(argv[i], "-JTCversion") == 0) { extern const char* JTCVersion; cout << JTCVersion << endl; for(int j = i ; j + 1 < argc ; j++) argv[j] = argv[j + 1]; argc -= 1; } else if(strcmp(argv[i], "-JTClicense") == 0) { extern const char* JTCLicense; cout << JTCLicense << endl; for(int j = i ; j + 1 < argc ; j++) argv[j] = argv[j + 1]; argc -= 1; } else if(strncmp(argv[i], "-JTC", strlen("-JTC")) == 0) { cout << "JTCInitialize:: unknown option: " << argv[i] << endl; for(int j = i ; j + 1 < argc ; j++) argv[j] = argv[j + 1]; argc -= 1; } else { i++; } } initialize(); } JTCInitialize::~JTCInitialize() { // // On terminate on last call. // if(--init_ > 0) return; // // Wait for all threads to disappear. // threadCounter -> waitZero(); // // Clean up main thread, and the ThreadCounter. // mainThread_ -> _JTC_exit(); delete threadCounter; delete manager_; } // ---------------------------------------------------------------------- // JTCInitialize public member implementation // ---------------------------------------------------------------------- // // Wait for all running threads to terminate. // void JTCInitialize::waitTermination() { threadCounter -> waitZero(); } // // Determine if the JTC library has been initialized. // bool JTCInitialize::initialized() { return init_ > 0; } // ---------------------------------------------------------------------- // JTCAdoptCurrentThread constructor and destructor // ---------------------------------------------------------------------- JTCAdoptCurrentThread::JTCAdoptCurrentThread() { try { // // Create entry for adopted thread. // adoptedThread_ = new JTCThread(JTCThreadId::self(), false); } catch(const JTCSystemCallException& e) { cerr << "JTC failed initialization:\n" << "\t" << e.getMessage() << endl; abort(); } catch(...) { cerr << "Caught unexpected exception in initialization" << endl; abort(); } } JTCAdoptCurrentThread::~JTCAdoptCurrentThread() { // // Clean up main thread, and the ThreadCounter. // adoptedThread_ -> _JTC_exit(); }