/* * Copyright: JessMA Open Source (ldcsaa@gmail.com) * * Author : Bruce Liang * Website : https://github.com/ldcsaa * Project : https://github.com/ldcsaa/HP-Socket * Blog : http://www.cnblogs.com/ldcsaa * Wiki : http://www.oschina.net/p/hp-socket * QQ Group : 44636872, 75375912 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "hpsocket/GlobalDef.h" #include "hpsocket/GlobalErrno.h" #include "RWLock.h" #include "STLHelper.h" #include #include #include using namespace std; /* Used to retry syscalls that can return EINTR. */ #define NO_EINTR_EXCEPT_THR_INTR(exp) ({ \ long int _rc; \ do {_rc = (long int)(exp);} \ while (IS_HAS_ERROR(_rc) && IS_INTR_ERROR() \ && !::IsThreadInterrupted()); \ _rc; }) #define NO_EINTR_EXCEPT_THR_INTR_INT(exp) ((int)NO_EINTR_EXCEPT_THR_INTR(exp)) class __CThread_Interrupt_ { public: static const int SIG_NO_INTERRUPT = (_NSIG - 5); private: friend BOOL IsThreadInterrupted(); template friend class CThread; private: static BOOL IsInterrupted(); static BOOL InitSigAction(); static void SignalHandler(int sig); private: ~__CThread_Interrupt_(); private: static BOOL sm_bInitFlag; }; inline BOOL IsThreadInterrupted() {return __CThread_Interrupt_::IsInterrupted();} class __CFakeRunnerClass_ {}; template class CThread { public: using F = R (T::*)(P*); using SF = R (*)(P*); struct TWorker { CThread* m_pThread; BOOL m_bDetach; T* m_pRunner; F m_pFunc; P* m_pArg; public: TWorker(CThread* pThread, BOOL bDetach = FALSE, T* pRunner = nullptr, F pFunc = nullptr, P* pArg = nullptr) : m_pThread(pThread) { Reset(bDetach, pRunner, pFunc, pArg); } void Reset(BOOL bDetach = FALSE, T* pRunner = nullptr, F pFunc = nullptr, P* pArg = nullptr) { m_bDetach = bDetach; m_pRunner = pRunner; m_pFunc = pFunc; m_pArg = pArg; } public: template::value && !is_void::value>> PVOID Run(T_*, R_*) { return (PVOID)(UINT_PTR)((m_pRunner->*m_pFunc)(m_pArg)); } template::value>> PVOID Run(T_*, PVOID) { (m_pRunner->*m_pFunc)(m_pArg); return nullptr; } template::value>> PVOID Run(__CFakeRunnerClass_*, R_*) { return (PVOID)(UINT_PTR)(*(SF*)&m_pFunc)(m_pArg); } PVOID Run(__CFakeRunnerClass_*, VOID*) { (*(SF*)&m_pFunc)(m_pArg); return nullptr; } }; friend struct TWorker; public: BOOL Start(SF pFunc, P* pArg = nullptr, BOOL bDetach = FALSE, const pthread_attr_t* pAttr = nullptr) { return Start((__CFakeRunnerClass_*)nullptr, *(F*)&pFunc, pArg, bDetach, pAttr); } BOOL Start(T* pRunner, F pFunc, P* pArg = nullptr, BOOL bDetach = FALSE, const pthread_attr_t* pAttr = nullptr) { int rs = ERROR_INVALID_STATE; if(IsRunning()) ::SetLastError(rs); else { m_Worker.Reset(bDetach, pRunner, pFunc, pArg); SetRunning(TRUE); rs = pthread_create(&m_ulThreadID, pAttr, ThreadProc, (PVOID)(&m_Worker)); if(rs != NO_ERROR) { Reset(); ::SetLastError(rs); } } return (rs == NO_ERROR); } #if !defined(__ANDROID__) BOOL Cancel() { int rs = NO_ERROR; if(!IsRunning() || ::IsSelfThread(m_ulThreadID)) rs = ERROR_INVALID_STATE; else rs = pthread_cancel(m_ulThreadID); if(rs != NO_ERROR) ::SetLastError(rs); return (rs == NO_ERROR); } BOOL Join(R* pResult = nullptr, BOOL bWait = TRUE, LONG lWaitMillsec = INFINITE) { int rs = NO_ERROR; if(!IsRunning() || ::IsSelfThread(m_ulThreadID)) rs = ERROR_INVALID_STATE; else { if(!bWait) rs = pthread_tryjoin_np(m_ulThreadID, (PVOID*)pResult); else if(IS_INFINITE(lWaitMillsec)) rs = pthread_join(m_ulThreadID, (PVOID*)pResult); else { timespec ts; ::GetFutureTimespec(lWaitMillsec, ts, CLOCK_REALTIME); rs = pthread_timedjoin_np(m_ulThreadID, (PVOID*)pResult, &ts); } } if(rs == NO_ERROR) SetRunning(FALSE); else ::SetLastError(rs); return (rs == NO_ERROR); } #else BOOL Cancel() { SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL Join(R* pResult = nullptr) { int rs = NO_ERROR; if(!IsRunning() || ::IsSelfThread(m_ulThreadID)) rs = ERROR_INVALID_STATE; else rs = pthread_join(m_ulThreadID, (PVOID*)pResult); if(rs == NO_ERROR) SetRunning(FALSE); else ::SetLastError(rs); return (rs == NO_ERROR); } #endif BOOL Detach() { int rs = NO_ERROR; if(!IsRunning()) rs = ERROR_INVALID_STATE; else rs = pthread_detach(m_ulThreadID); if(rs == NO_ERROR) Reset(); else ::SetLastError(rs); return (rs == NO_ERROR); } void Reset() { SetRunning(FALSE); m_ulThreadID = 0; m_lNativeID = 0; m_Worker.Reset(); } BOOL Interrupt() { if(!IsRunning()) { SetLastError(ERROR_INVALID_STATE); return FALSE; } return (IS_NO_ERROR(pthread_kill(m_ulThreadID, __CThread_Interrupt_::SIG_NO_INTERRUPT))); } void SetRunning(BOOL bRunning) {m_bRunning = bRunning;} BOOL IsRunning () const {return m_bRunning;} T* GetRunner () const {return m_Worker.m_pRunner;} F GetFunc () const {return m_Worker.m_pFunc;} SF GetSFunc () const {return *(SF*)&m_Worker.m_pFunc;} P* GetArg () const {return m_Worker.m_pArg;} THR_ID GetThreadID () const {return m_ulThreadID;} NTHR_ID GetNativeID () const {return m_lNativeID;} BOOL IsInMyThread () const {return IsMyThreadID(SELF_THREAD_ID);} BOOL IsMyThreadID (THR_ID ulThreadID) const {return ::IsSameThread(ulThreadID, m_ulThreadID);} BOOL IsMyNativeThreadID (NTHR_ID lNativeID) const {return ::IsSameNativeThread(lNativeID, m_lNativeID);} private: static PVOID ThreadProc(LPVOID pv) { UnmaskInterruptSignal(); __CThread_Interrupt_ tlsInterrupt; TWorker* pWorker = (TWorker*)pv; if(pWorker->m_bDetach) pWorker->m_pThread->Detach(); else pWorker->m_pThread->m_lNativeID = SELF_NATIVE_THREAD_ID; PVOID pResult = pWorker->Run((T*)nullptr, (R*)nullptr); return pResult; } static void UnmaskInterruptSignal() { sigset_t ss; sigemptyset(&ss); sigaddset(&ss, __CThread_Interrupt_::SIG_NO_INTERRUPT); pthread_sigmask(SIG_UNBLOCK, &ss, nullptr); } public: CThread() : m_Worker(this) { Reset(); } virtual ~CThread() { if(IsRunning()) { Interrupt(); Join(nullptr); } ASSERT(!IsRunning()); } DECLARE_NO_COPY_CLASS(CThread) private: THR_ID m_ulThreadID; NTHR_ID m_lNativeID; BOOL m_bRunning; TWorker m_Worker; }; template using CStaticThread = CThread<__CFakeRunnerClass_, P, R>; template class CTlsObj { using TLocalMap = unordered_map; public: T* TryGet() { T* pValue = nullptr; { CReadLock locallock(m_lock); auto it = m_map.find(SELF_THREAD_ID); if(it != m_map.end()) pValue = it->second; } return pValue; } template T* Get(_Con_Param&& ... construct_args) { T* pValue = TryGet(); if(pValue == nullptr) { pValue = Construct(forward<_Con_Param>(construct_args) ...); CWriteLock locallock(m_lock); m_map[SELF_THREAD_ID] = pValue; } return pValue; } template T& GetRef(_Con_Param&& ... construct_args) { return *Get(forward<_Con_Param>(construct_args) ...); } T* SetNewAndGetOld(T* pValue) { T* pOldValue = TryGet(); if(pValue != pOldValue) { if(pValue == nullptr) DoRemove(); else { CWriteLock locallock(m_lock); m_map[SELF_THREAD_ID] = pValue; } } return pOldValue; } void Set(T* pValue) { T* pOldValue = SetNewAndGetOld(pValue); if(pValue != pOldValue) DoDelete(pOldValue); } void Remove() { T* pValue = TryGet(); if(pValue != nullptr) { DoDelete(pValue); DoRemove(); } } void Clear() { CWriteLock locallock(m_lock); if(!IsEmpty()) { for(auto it = m_map.begin(), end = m_map.end(); it != end; ++it) DoDelete(it->second); m_map.clear(); } } TLocalMap& GetLocalMap() {return m_map;} const TLocalMap& GetLocalMap() const {return m_map;} CTlsObj& operator = (T* p) {Set(p); return *this;} T* operator -> () {return Get();} const T* operator -> () const {return Get();} T& operator * () {return GetRef();} const T& operator * () const {return GetRef();} size_t Size () const {return m_map.size();} bool IsEmpty() const {return m_map.empty();} private: inline void DoRemove() { CWriteLock locallock(m_lock); m_map.erase(SELF_THREAD_ID); } static inline void DoDelete(T* pValue) { if(pValue != nullptr) delete pValue; } template static inline T* Construct(_Con_Param&& ... construct_args) { return new T(forward<_Con_Param>(construct_args) ...); } public: CTlsObj() { } CTlsObj(T* pValue) { Set(pValue); } ~CTlsObj() { Clear(); } private: CSimpleRWLock m_lock; TLocalMap m_map; DECLARE_NO_COPY_CLASS(CTlsObj) }; template class CTlsSimple { using TLocalMap = unordered_map; static const T DEFAULT = (T)(0); public: BOOL TryGet(T& tValue) { BOOL isOK = FALSE; { CReadLock locallock(m_lock); auto it = m_map.find(SELF_THREAD_ID); if(it != m_map.end()) { tValue = it->second; isOK = TRUE; } } return isOK; } T Get(T tDefault = DEFAULT) { T tValue; if(TryGet(tValue)) return tValue; Set(tDefault); return tDefault; } T SetNewAndGetOld(T tValue) { T tOldValue; if(!TryGet(tOldValue)) tOldValue = DEFAULT; else if(tValue != tOldValue) Set(tValue); return tOldValue; } void Set(T tValue) { CWriteLock locallock(m_lock); m_map[SELF_THREAD_ID] = tValue; } void Remove() { T tValue; if(TryGet(tValue)) { CWriteLock locallock(m_lock); m_map.erase(SELF_THREAD_ID); } } void Clear() { CWriteLock locallock(m_lock); if(!IsEmpty()) m_map.clear(); } TLocalMap& GetLocalMap() {return m_map;} const TLocalMap& GetLocalMap() const {return m_map;} CTlsSimple& operator = (T t) {Set(t); return *this;} BOOL operator == (T t) {return Get() == t;} BOOL operator != (T t) {return Get() != t;} BOOL operator >= (T t) {return Get() >= t;} BOOL operator <= (T t) {return Get() <= t;} BOOL operator > (T t) {return Get() > t;} BOOL operator < (T t) {return Get() < t;} size_t Size () const {return m_map.size();} bool IsEmpty() const {return m_map.empty();} public: CTlsSimple() { } CTlsSimple(T tValue) { Set(tValue); } ~CTlsSimple() { Clear(); } DECLARE_NO_COPY_CLASS(CTlsSimple) private: CSimpleRWLock m_lock; TLocalMap m_map; };