#pragma once #ifndef _THREAD_H_ #define _THREAD_H_ #ifdef _WIN32 #include #include #include #else #include #include #include #include #include #include #endif #include #include "macros.h" /////////////////////////////////////////////////////////////////////////////// /// Various thread classes for applications that need to do threading and /// aren't going to be including various Microsoft class libraries ////////////////////////////////////////////////////////////////////////////// //******************************************************************** //* Tthread and thread //* //* allows you to run code on a thread via a template. Create a class //* which has a method const unsigned operator()() and use it to instantiate //* the template. If you want to use a bare function, encapsulate it in the //* class pointer_to_thread_function. The Vthread class uses the traditional //* virtual function approach /* Example: class foo { const unsigned operator()() { for (int x = 0; x < 4: ++x) { cout << x << endl; } return 1; } }; unsigned bar() { for (int x = 0; x < 4; ++x) cout << x << endl; return 1; } class narf: public Vthread { const unsigned operator()() { for (int x = 0; x < 4; ++x) { cout << x << endl; } return 1; } }; main() { Tthread f; Tthread b(bar); narf n; n.start(); f.start(); b.start(); ::WaitForSingleObject(f,INFINITE); // or f.join(); ::WaitForSingleObject(b,INFINITE); // or b.join(); ::WaitForSingleObject(n,INFINITE); // or n.join(); } */ //********************************************************************** #ifdef _WIN32 #ifndef ASSERT #define ASSERT(x) { if (!(x)) ::MessageBoxW(0,L"Assert failure",L"ASSERT",MB_OK); } #endif #else #ifndef ASSERT #define ASSERT(x) assert(x) #endif #endif #ifdef _WIN32 #define THREAD_FUNC unsigned __stdcall #else #define THREAD_FUNC void* #endif #ifdef _WIN32 class thread_CORE { #pragma warning(push) #pragma warning(disable: 4127) #pragma warning(disable: 4100) nocopy(thread_CORE) #pragma warning(pop) protected: HANDLE m_threadHandle; public: static int standard_signal_block() throw(){return 0;} // unix only thread_CORE() : m_threadHandle(0) {} ~thread_CORE() throw() { if (m_threadHandle) { ::WaitForSingleObject(m_threadHandle, INFINITE); ::CloseHandle(m_threadHandle); } m_threadHandle = 0; } inline void join() { if (m_threadHandle) { ::WaitForSingleObject(m_threadHandle, INFINITE); } } inline operator HANDLE() const throw() { return m_threadHandle; } static unsigned long getCurrentThreadID() { return (unsigned long)GetCurrentThreadId(); } }; template class Tthread: public thread_CORE, public Handler { static inline unsigned __stdcall _start_func(void *arg) { return reinterpret_cast *>(arg)->operator()(); } public: Tthread(){} template Tthread( P1 &p1):Handler(p1){} template Tthread( P1 &p1, P2 &p2):Handler(p1,p2){} template Tthread( P1 &p1, P2 &p2, P3 &p3):Handler(p1,p2,p3){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4):Handler(p1,p2,p3,p4){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5):Handler(p1,p2,p3,p4,p5){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5,P6 &p6):Handler(p1,p2,p3,p4,p5,p6){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6,P7 &p7):Handler(p1,p2,p3,p4,p5,p6,p7){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7,P8 &p8):Handler(p1,p2,p3,p4,p5,p6,p7,p8){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7, P8 &p8, P9 &p9):Handler(p1,p2,p3,p4,p5,p6,p7,p8,p9){} template Tthread(P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7, P8 &p8, P9 &p9, P10 &p10):Handler(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10){} template Tthread(P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7, P8 &p8, P9 &p9, P10 &p10,P11 &p11):Handler(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11){} template Tthread(P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7, P8 &p8, P9 &p9, P10 &p10,P11 &p11,P12 &p12):Handler(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12){} void start(int __unused = 1) throw(std::runtime_error) { unsigned m_threadIdentifier = 0; m_threadHandle = (HANDLE)::_beginthreadex(NULL, 0, _start_func, this, 0, &m_threadIdentifier); if (!m_threadHandle) { throw std::runtime_error("Could not start thread"); } } }; class SimpleThread { protected: HANDLE m_threadHandle; public: SimpleThread(unsigned (__stdcall *_start_func) (void *arg) = 0, void *user = 0) : m_threadHandle(0) { if (_start_func) { start(_start_func, user); } } ~SimpleThread() throw() { if (m_threadHandle) { ::CloseHandle(m_threadHandle); } m_threadHandle = 0; } void start(unsigned (__stdcall *_start_func) (void *arg), void *user) throw(std::runtime_error) { unsigned m_threadIdentifier = 0; m_threadHandle = (HANDLE)::_beginthreadex(NULL, 0, _start_func, user, 0, &m_threadIdentifier); if (!m_threadHandle) { throw std::runtime_error("Could not start thread"); } } }; #else class thread_CORE { nocopy(thread_CORE) protected: pthread_t m_threadHandle; public: static int standard_signal_block() throw() { sigset_t catchset; sigemptyset(&catchset); sigaddset(&catchset,SIGPIPE); sigaddset(&catchset,SIGTERM); sigaddset(&catchset,SIGHUP); sigaddset(&catchset,SIGINT); sigaddset(&catchset,SIGQUIT); sigaddset(&catchset,SIGTSTP); // ^Z allow this sigaddset(&catchset,SIGCHLD); sigaddset(&catchset,SIGWINCH); sigaddset(&catchset,SIGUSR1); sigaddset(&catchset,SIGUSR2); return pthread_sigmask(SIG_BLOCK,&catchset,NULL); } thread_CORE() : m_threadHandle(0) {} ~thread_CORE() throw() { if (m_threadHandle) { ::pthread_join(m_threadHandle, NULL); } m_threadHandle = 0; } inline void join() { if (m_threadHandle) { ::pthread_join(m_threadHandle,NULL); m_threadHandle = 0; } } inline operator pthread_t() const throw() { return m_threadHandle; } static unsigned long getCurrentThreadID() { return (unsigned long)::pthread_self(); } }; template class Tthread: public thread_CORE, public Handler { static inline void* _start_func(void *arg) { standard_signal_block(); long x = (long)(reinterpret_cast *>(arg))->operator()(); return (void*)x; } public: Tthread(){} template Tthread( P1 &p1):Handler(p1){} template Tthread( P1 &p1, P2 &p2):Handler(p1,p2){} template Tthread( P1 &p1, P2 &p2, P3 &p3):Handler(p1,p2,p3){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4):Handler(p1,p2,p3,p4){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5):Handler(p1,p2,p3,p4,p5){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5,P6 &p6):Handler(p1,p2,p3,p4,p5,p6){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6,P7 &p7):Handler(p1,p2,p3,p4,p5,p6,p7){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7,P8 &p8):Handler(p1,p2,p3,p4,p5,p6,p7,p8){} template Tthread( P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7, P8 &p8, P9 &p9):Handler(p1,p2,p3,p4,p5,p6,p7,p8,p9){} template Tthread(P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7, P8 &p8, P9 &p9, P10 &p10):Handler(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10){} template Tthread(P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7, P8 &p8, P9 &p9, P10 &p10,P11 &p11):Handler(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11){} template Tthread(P1 &p1, P2 &p2, P3 &p3, P4 &p4, P5 &p5, P6 &p6, P7 &p7, P8 &p8, P9 &p9, P10 &p10,P11 &p11,P12 &p12):Handler(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12){} void start(int joined = 1) throw(std::runtime_error) { if (joined && m_threadHandle) { throw std::runtime_error("Thread already exists"); } int ret = pthread_create(&m_threadHandle, NULL, _start_func, this); if (ret) { throw std::runtime_error("Could not start thread" + std::string(ret == EAGAIN ? " [Increase the open files (ulimit -n) limit]" : "")); } } }; class SimpleThread { public: SimpleThread(void *(*_start_func) (void *arg) = 0, void *user = 0) { if (_start_func) { start(_start_func, user); } } void start(void *(*_start_func) (void *arg), void *user) throw(std::runtime_error) { pthread_t m_threadHandle = 0; pthread_attr_t attr; int ret = pthread_attr_init(&attr); if (ret) { throw std::runtime_error("Could not start thread - pthread_attr_init failure"); } ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ret) { pthread_attr_destroy(&attr); throw std::runtime_error("Could not start thread - pthread_attr_setdetachstate failure"); } ret = pthread_create(&m_threadHandle,&attr,_start_func,user); if (ret) { pthread_attr_destroy(&attr); throw std::runtime_error("Could not start thread" + std::string(ret == EAGAIN ? " [Increase the open files (ulimit -n) limit]" : "")); } pthread_attr_destroy(&attr); } }; #endif class pointer_to_thread_function { public: typedef unsigned (*func_t)(); inline pointer_to_thread_function(func_t f):m_function(f){} private: func_t m_function; protected: inline const unsigned operator()() { return (*m_function)(); } }; class vthread_stub { protected: virtual const unsigned operator()() = 0; virtual ~vthread_stub(){} }; typedef Tthread Vthread; #ifdef _WIN32 class event { #pragma warning(push) #pragma warning(disable: 4127) #pragma warning(disable: 4100) nocopy(event) #pragma warning(pop) private: HANDLE m_event; public: event(BOOL bManualReset) throw(std::runtime_error); ~event() throw(); inline operator HANDLE() const throw() { return m_event; } void wait() throw(std::runtime_error); void setEvent() throw(std::runtime_error); void resetEvent() throw(std::runtime_error); }; #else class Win32SyncObject { public: virtual void syncObjectWait() throw(std::runtime_error) = 0; virtual bool syncObjectTimedWait(int milliseconds) throw(std::runtime_error) = 0; virtual ~Win32SyncObject(){} }; typedef Win32SyncObject* Win32SyncObjectPtr; /// these provide basic resource mgmt for typical uses #endif class conditionVariable; namespace AOL_namespace { #pragma pack(push, 1) // conflicts with headers in solaris #ifndef _WIN32 class mutex : public Win32SyncObject #else class mutex #endif { nocopy(mutex) private: pthread_mutex_t m_mutex; public: mutex() throw(std::runtime_error); ~mutex() throw(); void lock() throw(std::runtime_error); bool timedLock(int milliseconds) throw(std::runtime_error); void unlock() throw(std::runtime_error); void syncObjectWait() throw(std::runtime_error) { lock(); } bool syncObjectTimedWait(int milliseconds) throw(std::runtime_error) { return timedLock(milliseconds); } #ifndef _WIN32 inline operator Win32SyncObject*() throw() { return this; } #endif friend class ::conditionVariable; }; #pragma pack(pop) class rwLock { pthread_rwlock_t m_lock; public: rwLock(); ~rwLock(); bool tryRdLock(); void lock(); void rdLock(); void unlock(); }; } class conditionVariable { nocopy(conditionVariable) private: pthread_cond_t m_conditionVariable; public: conditionVariable() throw(std::runtime_error); ~conditionVariable() throw(); void wait(AOL_namespace::mutex &m) throw(std::runtime_error); bool timedWait(AOL_namespace::mutex &m,int milliseconds) throw(std::runtime_error); void signal() throw(std::runtime_error); void broadcast() throw(std::runtime_error); }; #ifndef _WIN32 class event : public Win32SyncObject { nocopy(event) private: conditionVariable m_conditionVariable; AOL_namespace::mutex m_mutex; bool m_manualReset; bool m_signaled; public: event(bool bManualReset) throw(std::runtime_error); void wait() throw(std::runtime_error); bool timedWait(int milliseconds) throw(std::runtime_error); void setEvent() throw(std::runtime_error); void resetEvent() throw(std::runtime_error); void syncObjectWait() throw(std::runtime_error) { wait(); } bool syncObjectTimedWait(int milliseconds) throw(std::runtime_error) { return timedWait(milliseconds); } inline operator Win32SyncObject*() throw() { return this; } }; #endif /* class stackLock Stack based mutex locker/unlocker for unwrapped Win32 mutexes. Equivalent to CAutoLock for CCritSec objects. */ class stackLock { AOL_namespace::mutex &m_m; public: stackLock(AOL_namespace::mutex &m) : m_m(m) { m_m.lock(); } ~stackLock() { m_m.unlock(); } }; class stackRWLock { AOL_namespace::rwLock &m_rw; bool m_locked; public: stackRWLock(AOL_namespace::rwLock &l, bool reader = true, bool lockNow = true) : m_rw(l), m_locked(lockNow) { if (lockNow == false) return; if (reader) m_rw.rdLock(); else m_rw.lock(); } ~stackRWLock() { if (m_locked) m_rw.unlock(); } bool tryRdLock() { if (m_locked == false) { if (m_rw.tryRdLock()) { m_locked = true; return true; } } return false; } void lock() { if (m_locked == false) { m_rw.lock(); m_locked = true; } } void rdLock() { if (m_locked == false) { m_rw.rdLock(); m_locked = true; } } void unlock() { if (m_locked) { m_rw.unlock(); m_locked = false; } } }; #ifndef _WIN32 #define WAIT_ABANDONED (-1) #define WAIT_TIMEOUT (0) #define WAIT_OBJECT_0 (1) #define INFINITE (-1) int WaitForSingleObject(Win32SyncObject &o,int milli_timeout) throw(); inline int WaitForSingleObject(Win32SyncObject *o,int milli_timeout) throw() { return WaitForSingleObject(*o,milli_timeout); } int WaitForMultipleObjects(int count,Win32SyncObjectPtr *objs,bool waitall,int milliseconds) throw(); inline void CloseHandle(Win32SyncObject *o) { delete o; } typedef Win32SyncObject *HANDLE; #endif inline bool _SetEvent(event &e) { bool result=false; try {e.setEvent(); result=true; }catch(...){} return result; } inline bool _SetEvent(event *e) { return _SetEvent(*e); } inline bool _ResetEvent(event &e) { bool result=false; try {e.resetEvent();result=true;}catch(...){} return result; } inline bool _ResetEvent(event *e) { return _ResetEvent(*e); } namespace AOL_namespace { template class synchronizedPrimitive { private: mutable AOL_namespace::mutex m_lock; T m_t; public: synchronizedPrimitive(){} synchronizedPrimitive(const T &t):m_t(t){} T get() const throw() { stackLock sl(m_lock); return m_t; } void set(const T &t) throw() { stackLock sl(m_lock); m_t = t; } }; } // thread safe sleep function void safe_sleep(int sec, int usec = 0); #endif