diff options
Diffstat (limited to 'cpp/src/Ice/Thread.cpp')
-rw-r--r-- | cpp/src/Ice/Thread.cpp | 824 |
1 files changed, 824 insertions, 0 deletions
diff --git a/cpp/src/Ice/Thread.cpp b/cpp/src/Ice/Thread.cpp new file mode 100644 index 00000000000..12c5fc1738c --- /dev/null +++ b/cpp/src/Ice/Thread.cpp @@ -0,0 +1,824 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifdef __sun +// +// Solaris 10 bug: it's supposed to be defined in pthread.h +// +#ifndef __EXTENSIONS__ +#define __EXTENSIONS__ +#endif +#endif + +#include <IceUtil/Thread.h> +#include <IceUtil/Time.h> +#include <IceUtil/ThreadException.h> +#include <climits> +#include <exception> + +#ifdef ICE_OS_WINRT +# include <thread> +#endif + +#ifndef _WIN32 + #include <sys/time.h> + #include <sys/resource.h> +#endif + +using namespace std; + +#ifdef ICE_OS_WINRT + +IceUtil::ThreadControl::ThreadControl() : + _id(this_thread::get_id()) +{ +} + +IceUtil::ThreadControl::ThreadControl(const shared_ptr<thread>& thread) : + _thread(thread), + _id(_thread->get_id()) +{ +} + +bool +IceUtil::ThreadControl::operator==(const ThreadControl& rhs) const +{ + return id() == rhs.id(); +} + +bool +IceUtil::ThreadControl::operator!=(const ThreadControl& rhs) const +{ + return id() != rhs.id(); +} + +void +IceUtil::ThreadControl::join() +{ + if(!_thread) + { + throw BadThreadControlException(__FILE__, __LINE__); + } + + try + { + _thread->join(); + } + catch(const system_error& ex) + { + throw ThreadSyscallException(__FILE__, __LINE__, ex.code().value()); + } +} + +void +IceUtil::ThreadControl::detach() +{ + if(!_thread) + { + throw BadThreadControlException(__FILE__, __LINE__); + } + + try + { + _thread->detach(); + } + catch(const system_error& ex) + { + throw ThreadSyscallException(__FILE__, __LINE__, ex.code().value()); + } +} + +IceUtil::ThreadControl::ID +IceUtil::ThreadControl::id() const +{ + return _id; +} + +void +IceUtil::ThreadControl::sleep(const Time& timeout) +{ + IceUtil::Int64 msTimeout = timeout.toMilliSeconds(); + if(msTimeout < 0 || msTimeout > 0x7FFFFFFF) + { + throw IceUtil::InvalidTimeoutException(__FILE__, __LINE__, timeout); + } + this_thread::sleep_for(chrono::microseconds(timeout.toMicroSeconds())); +} + +void +IceUtil::ThreadControl::yield() +{ + this_thread::yield(); +} + +IceUtil::Thread::Thread() : + _started(false), + _running(false) +{ +} + +IceUtil::Thread::Thread(const string& name) : + _name(name), + _started(false), + _running(false) +{ +} + +IceUtil::Thread::~Thread() +{ +} + +static unsigned int +WINAPI startHook(void* arg) +{ + // Ensure that the thread doesn't go away until run() has + // completed. + // + IceUtil::ThreadPtr thread; + + try + { + IceUtil::Thread* rawThread = static_cast<IceUtil::Thread*>(arg); + + // + // Ensure that the thread doesn't go away until run() has + // completed. + // + thread = rawThread; + + // + // Initialize the random number generator in each thread on + // Windows (the rand() seed is thread specific). + // + unsigned int seed = static_cast<unsigned int>(IceUtil::Time::now().toMicroSeconds()); + srand(seed ^ static_cast<unsigned int>(hash<thread::id>()(thread->getThreadControl().id()))); + + // + // See the comment in IceUtil::Thread::start() for details. + // + rawThread->__decRef(); + thread->run(); + } + catch(...) + { + if(!thread->name().empty()) + { + cerr << thread->name() << " terminating" << endl; + } + std::terminate(); + } + + thread->_done(); + + return 0; +} + +#include <process.h> + +IceUtil::ThreadControl +IceUtil::Thread::start(size_t) +{ + return start(0, 0); +} + +IceUtil::ThreadControl +IceUtil::Thread::start(size_t, int) +{ + // + // Keep this alive for the duration of start + // + IceUtil::ThreadPtr keepMe = this; + + IceUtil::Mutex::Lock lock(_stateMutex); + + if(_started) + { + throw ThreadStartedException(__FILE__, __LINE__); + } + + // + // It's necessary to increment the reference count since + // pthread_create won't necessarily call the thread function until + // later. If the user does (new MyThread)->start() then the thread + // object could be deleted before the thread object takes + // ownership. It's also necessary to increment the reference count + // prior to calling pthread_create since the thread itself calls + // __decRef(). + // + __incRef(); + _thread.reset(new thread(startHook, this)); + + _started = true; + _running = true; + + return ThreadControl(_thread); +} + +IceUtil::ThreadControl +IceUtil::Thread::getThreadControl() const +{ + IceUtil::Mutex::Lock lock(_stateMutex); + if(!_started) + { + throw ThreadNotStartedException(__FILE__, __LINE__); + } + return ThreadControl(_thread); +} + +bool +IceUtil::Thread::operator==(const Thread& rhs) const +{ + return this == &rhs; +} + +bool +IceUtil::Thread::operator!=(const Thread& rhs) const +{ + return this != &rhs; +} + +bool +IceUtil::Thread::operator<(const Thread& rhs) const +{ + return this < &rhs; +} + +bool +IceUtil::Thread::isAlive() const +{ + IceUtil::Mutex::Lock lock(_stateMutex); + return _running; +} + +void +IceUtil::Thread::_done() +{ + IceUtil::Mutex::Lock lock(_stateMutex); + _running = false; +} + +const string& +IceUtil::Thread::name() const +{ + return _name; +} + +#elif defined(_WIN32) + +IceUtil::ThreadControl::ThreadControl() : + _handle(0), + _id(GetCurrentThreadId()) +{ +} + +IceUtil::ThreadControl::ThreadControl(HANDLE handle, IceUtil::ThreadControl::ID id) : + _handle(handle), + _id(id) +{ +} + +bool +IceUtil::ThreadControl::operator==(const ThreadControl& rhs) const +{ + return _id == rhs._id; +} + +bool +IceUtil::ThreadControl::operator!=(const ThreadControl& rhs) const +{ + return _id != rhs._id; +} + +void +IceUtil::ThreadControl::join() +{ + if(_handle == 0) + { + throw BadThreadControlException(__FILE__, __LINE__); + } + + DWORD rc = WaitForSingleObjectEx(_handle, INFINITE, true); + if(rc != WAIT_OBJECT_0) + { + throw ThreadSyscallException(__FILE__, __LINE__, GetLastError()); + } + + detach(); +} + +void +IceUtil::ThreadControl::detach() +{ + if(_handle == 0) + { + throw BadThreadControlException(__FILE__, __LINE__); + } + + if(CloseHandle(_handle) == 0) + { + throw ThreadSyscallException(__FILE__, __LINE__, GetLastError()); + } +} + +IceUtil::ThreadControl::ID +IceUtil::ThreadControl::id() const +{ + return _id; +} + +void +IceUtil::ThreadControl::sleep(const Time& timeout) +{ + IceUtil::Int64 msTimeout = timeout.toMilliSeconds(); + if(msTimeout < 0 || msTimeout > 0x7FFFFFFF) + { + throw IceUtil::InvalidTimeoutException(__FILE__, __LINE__, timeout); + } + Sleep(static_cast<long>(timeout.toMilliSeconds())); +} + +void +IceUtil::ThreadControl::yield() +{ + // + // A value of zero causes the thread to relinquish the remainder + // of its time slice to any other thread of equal priority that is + // ready to run. + // + Sleep(0); +} + +IceUtil::Thread::Thread() : + _started(false), + _running(false), + _handle(0), + _id(0) +{ +} + +IceUtil::Thread::Thread(const string& name) : + _name(name), + _started(false), + _running(false), + _handle(0), + _id(0) +{ +} + +IceUtil::Thread::~Thread() +{ +} + +static unsigned int +WINAPI startHook(void* arg) +{ + // Ensure that the thread doesn't go away until run() has + // completed. + // + IceUtil::ThreadPtr thread; + + try + { + IceUtil::Thread* rawThread = static_cast<IceUtil::Thread*>(arg); + + // + // Ensure that the thread doesn't go away until run() has + // completed. + // + thread = rawThread; + + // + // Initialize the random number generator in each thread on + // Windows (the rand() seed is thread specific). + // + unsigned int seed = static_cast<unsigned int>(IceUtil::Time::now().toMicroSeconds()); + srand(seed ^ thread->getThreadControl().id()); + + // + // See the comment in IceUtil::Thread::start() for details. + // + rawThread->__decRef(); + thread->run(); + } + catch(...) + { + if(!thread->name().empty()) + { + cerr << thread->name() << " terminating" << endl; + } + std::terminate(); + } + + thread->_done(); + + return 0; +} + +#include <process.h> + +IceUtil::ThreadControl +IceUtil::Thread::start(size_t stackSize) +{ + return start(stackSize, THREAD_PRIORITY_NORMAL); +} + +IceUtil::ThreadControl +IceUtil::Thread::start(size_t stackSize, int priority) +{ + // + // Keep this alive for the duration of start + // + IceUtil::ThreadPtr keepMe = this; + + IceUtil::Mutex::Lock lock(_stateMutex); + + if(_started) + { + throw ThreadStartedException(__FILE__, __LINE__); + } + + // + // It's necessary to increment the reference count since + // pthread_create won't necessarily call the thread function until + // later. If the user does (new MyThread)->start() then the thread + // object could be deleted before the thread object takes + // ownership. It's also necessary to increment the reference count + // prior to calling pthread_create since the thread itself calls + // __decRef(). + // + __incRef(); + + unsigned int id; + _handle = + reinterpret_cast<HANDLE>( + _beginthreadex(0, + static_cast<unsigned int>(stackSize), + startHook, this, + CREATE_SUSPENDED, + &id)); + _id = id; + assert(_handle != (HANDLE)-1L); + if(_handle == 0) + { + __decRef(); + throw ThreadSyscallException(__FILE__, __LINE__, GetLastError()); + } + if(SetThreadPriority(_handle, priority) == 0) + { + throw ThreadSyscallException(__FILE__, __LINE__, GetLastError()); + } + if(static_cast<int>(ResumeThread(_handle)) == -1) + { + __decRef(); + throw ThreadSyscallException(__FILE__, __LINE__, GetLastError()); + } + + _started = true; + _running = true; + + return ThreadControl(_handle, _id); +} + +IceUtil::ThreadControl +IceUtil::Thread::getThreadControl() const +{ + IceUtil::Mutex::Lock lock(_stateMutex); + if(!_started) + { + throw ThreadNotStartedException(__FILE__, __LINE__); + } + return ThreadControl(_handle, _id); +} + +bool +IceUtil::Thread::operator==(const Thread& rhs) const +{ + return this == &rhs; +} + +bool +IceUtil::Thread::operator!=(const Thread& rhs) const +{ + return this != &rhs; +} + +bool +IceUtil::Thread::operator<(const Thread& rhs) const +{ + return this < &rhs; +} + +bool +IceUtil::Thread::isAlive() const +{ + IceUtil::Mutex::Lock lock(_stateMutex); + return _running; +} + +void +IceUtil::Thread::_done() +{ + IceUtil::Mutex::Lock lock(_stateMutex); + _running = false; +} + +const string& +IceUtil::Thread::name() const +{ + return _name; +} + +#else + +IceUtil::ThreadControl::ThreadControl(pthread_t thread) : + _thread(thread), + _detachable(true) +{ +} + +IceUtil::ThreadControl::ThreadControl() : + _thread(pthread_self()), + _detachable(false) +{ +} + +bool +IceUtil::ThreadControl::operator==(const ThreadControl& rhs) const +{ + return pthread_equal(_thread, rhs._thread) != 0; +} + +bool +IceUtil::ThreadControl::operator!=(const ThreadControl& rhs) const +{ + return !operator==(rhs); +} + +void +IceUtil::ThreadControl::join() +{ + if(!_detachable) + { + throw BadThreadControlException(__FILE__, __LINE__); + } + + void* ignore = 0; + int rc = pthread_join(_thread, &ignore); + if(rc != 0) + { + throw ThreadSyscallException(__FILE__, __LINE__, rc); + } +} + +void +IceUtil::ThreadControl::detach() +{ + if(!_detachable) + { + throw BadThreadControlException(__FILE__, __LINE__); + } + + int rc = pthread_detach(_thread); + if(rc != 0) + { + throw ThreadSyscallException(__FILE__, __LINE__, rc); + } +} + +IceUtil::ThreadControl::ID +IceUtil::ThreadControl::id() const +{ + return _thread; +} + +void +IceUtil::ThreadControl::sleep(const Time& timeout) +{ + IceUtil::Int64 msTimeout = timeout.toMilliSeconds(); + if(msTimeout < 0 || msTimeout > 0x7FFFFFFF) + { + throw IceUtil::InvalidTimeoutException(__FILE__, __LINE__, timeout); + } + struct timeval tv = timeout; + struct timespec ts; + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000L; + nanosleep(&ts, 0); +} + +void +IceUtil::ThreadControl::yield() +{ + sched_yield(); +} + +IceUtil::Thread::Thread() : + _started(false), + _running(false) +{ +} + +IceUtil::Thread::Thread(const string& name) : + _name(name), + _started(false), + _running(false) +{ +} + +IceUtil::Thread::~Thread() +{ +} + +extern "C" +{ +static void* +startHook(void* arg) +{ + // + // Ensure that the thread doesn't go away until run() has + // completed. + // + IceUtil::ThreadPtr thread; + + try + { + IceUtil::Thread* rawThread = static_cast<IceUtil::Thread*>(arg); + + thread = rawThread; + + // + // See the comment in IceUtil::Thread::start() for details. + // + rawThread->__decRef(); + thread->run(); + } + catch(...) + { + if(!thread->name().empty()) + { + cerr << thread->name() << " terminating" << endl; + } + std::terminate(); + } + + thread->_done(); + + return 0; +} +} + + +IceUtil::ThreadControl +IceUtil::Thread::start(size_t stackSize) +{ + return start(stackSize, false, 0); +} + +IceUtil::ThreadControl +IceUtil::Thread::start(size_t stackSize, int priority) +{ + return start(stackSize, true, priority); +} +IceUtil::ThreadControl +IceUtil::Thread::start(size_t stackSize, bool realtimeScheduling, int priority) +{ + // + // Keep this alive for the duration of start + // + IceUtil::ThreadPtr keepMe = this; + + IceUtil::Mutex::Lock lock(_stateMutex); + + if(_started) + { + throw ThreadStartedException(__FILE__, __LINE__); + } + + // + // It's necessary to increment the reference count since + // pthread_create won't necessarily call the thread function until + // later. If the user does (new MyThread)->start() then the thread + // object could be deleted before the thread object takes + // ownership. It's also necessary to increment the reference count + // prior to calling pthread_create since the thread itself calls + // __decRef(). + // + __incRef(); + + pthread_attr_t attr; + int rc = pthread_attr_init(&attr); + if(rc != 0) + { + __decRef(); + pthread_attr_destroy(&attr); + throw ThreadSyscallException(__FILE__, __LINE__, rc); + } + if(stackSize > 0) + { + if(stackSize < PTHREAD_STACK_MIN) + { + stackSize = PTHREAD_STACK_MIN; + } +#ifdef __APPLE__ + if(stackSize % 4096 > 0) + { + stackSize = stackSize / 4096 * 4096 + 4096; + } +#endif + rc = pthread_attr_setstacksize(&attr, stackSize); + if(rc != 0) + { + __decRef(); + pthread_attr_destroy(&attr); + throw ThreadSyscallException(__FILE__, __LINE__, rc); + } + } + + if(realtimeScheduling) + { + rc = pthread_attr_setschedpolicy(&attr, SCHED_RR); + if(rc != 0) + { + __decRef(); + throw ThreadSyscallException(__FILE__, __LINE__, rc); + } + sched_param param; + param.sched_priority = priority; + rc = pthread_attr_setschedparam(&attr, ¶m); + if(rc != 0) + { + __decRef(); + pthread_attr_destroy(&attr); + throw ThreadSyscallException(__FILE__, __LINE__, rc); + } + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + } + rc = pthread_create(&_thread, &attr, startHook, this); + pthread_attr_destroy(&attr); + if(rc != 0) + { + __decRef(); + throw ThreadSyscallException(__FILE__, __LINE__, rc); + } + + _started = true; + _running = true; + return ThreadControl(_thread); +} + +IceUtil::ThreadControl +IceUtil::Thread::getThreadControl() const +{ + IceUtil::Mutex::Lock lock(_stateMutex); + if(!_started) + { + throw ThreadNotStartedException(__FILE__, __LINE__); + } + return ThreadControl(_thread); +} + +bool +IceUtil::Thread::operator==(const Thread& rhs) const +{ + return this == &rhs; +} + +bool +IceUtil::Thread::operator!=(const Thread& rhs) const +{ + return this != &rhs; +} + +bool +IceUtil::Thread::operator<(const Thread& rhs) const +{ + return this < &rhs; +} + +bool +IceUtil::Thread::isAlive() const +{ + IceUtil::Mutex::Lock lock(_stateMutex); + return _running; +} + +void +IceUtil::Thread::_done() +{ + IceUtil::Mutex::Lock lock(_stateMutex); + _running = false; +} + +const string& +IceUtil::Thread::name() const +{ + return _name; +} + +#endif |