📄 wy_thread.cpp
字号:
if((v=::pthread_attr_destroy(&_thrd_attr))!=0) { // manu says only EINVAL could happen WY_THROW( WyRet(v) ); } if((v=::pthread_attr_init(&_thrd_attr))!=0) { // // manu says only ENOMEM, linux man just says errno. // ::pthread_attr_destroy(&_thrd_attr); // make this member invalid (release) WY_THROW( WyRet(v) ); } if((v=::pthread_attr_setdetachstate(&_thrd_attr,PTHREAD_CREATE_DETACHED))!=0) { // man. indicates only EINVAL WY_THROW( WyRet(v) ); } this->_reset_avars(); _thrd_stat_cond.broadcast(); return(Ok);}catch(const WyRet& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );}catch(const std::exception&) {#ifdef WY_DEBUG Wy::cerr << "std::exception\n";#endif WY_THROW( WyRet() );};// [Non-cancellation Point]void Wy_Thread::tmain_close(void) WY__TSPC(){ _non_cancellable(); WyLock aa(_mtx); WyRet r; // Detect earily (in case default _thrd_id is meaningful to pthread) // if(_thrd_id==Wy_ThreadID::Default_ThreadID) { return; } if(::pthread_equal(_thrd_id,::pthread_self())) { return; // thread can not cancel itself } this->_cleanup_thread(aa);};// [Non-cancellation Point]//// [Syn] The final function of the thread tmain()// _exit_obj is reset from ecode, and return pointer to _exit_obj//// Note: This member locks wy__toltab_ptr->mutex() and this->_mtx//void* Wy_Thread::_tmain_finish(const WyRet& ecode) WY__TSPC()try { // thread cancelability disabled, no need to recover, as going to end. // _non_cancellable(); WyLock aa_toltab( wy__toltab_ptr->mutex() ); // lock toltab first WyLock aa_this(_mtx); _exit_obj=ecode; // Set the thread state to Stopped and boradcast, ref. note8 // (destructor and reset() may wait on this) // _thrd_stat=Stopped; _thrd_stat_cond.broadcast(); // Decrease the number of total active(running) threads // if(wy__toltab_ptr->remove(Wy_ThreadID(_thrd_id))==false) { WY_TERMINATE(""); } #ifdef WY_DEBUG { WyLock atmp(wy_act_mutex); ++wy_joined_cnt; } #endif return(&_exit_obj);}catch(const WyLock::Reply& e) { Wy__Base::put_abnmsg( e->c_repstr() ,__FILE__,__LINE__); Wy__Base::terminate();}catch(const WyRet& e) { Wy__Base::put_abnmsg( e->c_repstr() ,__FILE__,__LINE__); Wy__Base::terminate();}catch(const std::bad_alloc&) { WY_TERMINATE(""); // so we can trace the line}catch(const std::exception&) { WY_TERMINATE(""); // so we can trace the line}catch(...) { WY_TERMINATE(""); // so we can trace the line};// [Cancel Point]//// [Syn] The created thread function. tmain() is invoked from inside.//// [Ret] Pointer to _exit_obj//void * Wy_Thread::wy_thread_begin_func(Wy_Thread *obj_ptr){ // Syncronize with begin() by waiting for Active state // begin() may cancel this thread because it may fail in its latter // processing after the thread had been created // { WyLock aa(obj_ptr->_mtx); #ifndef NDEBUG if(obj_ptr->_thrd_id!=::pthread_self()) { // assertion check WY_TERMINATE(""); } #endif } try { ::pthread_testcancel(); // syncronizing purpose return obj_ptr->_tmain_finish( obj_ptr->tmain() ); } catch(...) { // assert: stack unwound from obj_ptr->tmain() // (_exit_obj is reset the same) // obj_ptr->_tmain_finish( obj_ptr->_exit_obj ); throw; }; // UNREACHABLE};extern "C" inline void * wy__thread_begin_func_c__(void* obj_ptr){ return Wy_Thread::wy_thread_begin_func( reinterpret_cast<Wy_Thread *>(obj_ptr));};// [Non-cancellation Point]//WyRet Wy_Thread::begin(void)try { WyRet r; WyLock aa_toltab( wy__toltab_ptr->mutex() ); // lock toltab first WyLock aa_this(_mtx); if(_in_destroy==true) { WY_THROW( WyRet() ); } if(_thrd_stat!=Null) { WY_RETURN(Wym_EBADF); } if(wy__toltab_ptr->size()>=Wy_Thread::Max_Threads) { WY_RETURN(Wym_ENFILE); } // preset return status // (_thrd_id will be set by pthread_create) _thrd_stat=Active; _exit_obj=Wym_ETHRDCAN; WY_HERE(_exit_obj); int v; try { // assert: _thrd_id==default if((v=::pthread_create(&_thrd_id,&_thrd_attr, wy__thread_begin_func_c__, static_cast<void*>(this)))!=0) { // ENOMEM can return, which is not in the manpage // this->_reset_avars(); _thrd_stat_cond.broadcast(); WY_RETURN( WyRet(v) ); } #ifdef WY_DEBUG { WyLock atmp(wy_act_mutex); ++wy_created_cnt; if(wy_created_cnt<wy_joined_cnt) { WY_TERMINATE(""); } const size_t act=wy_created_cnt-wy_joined_cnt; if(act>wy_act_max) { wy_act_max=act; } } #endif // Check that _thrd_id is not the value identical to the default id // Refer to definition of Wy_ThreadID::Default_ThreadID. // if(_thrd_id==Wy_ThreadID::Default_ThreadID) { WY_THROW( WyRet() ); // assertion failure } WyRet r( wy__toltab_ptr->add(this,_thrd_id) ); if(r!=Ok) { WY_THROW(r); // no error with wy__toltab_ptr->add(..) is expected } } catch(...) { if(_thrd_id!=Wy_ThreadID::Default_ThreadID) { while((v=::pthread_cancel(_thrd_id))!=0) { // ref. note5 if(v==EAGAIN) { continue; // no yield(); } WY_THROW( WyRet(v) ); // ESRCH (or others not documented) }; if(v==0) { NoCancel noc; while(_thrd_stat==Active) { // cancel_point if((r=_thrd_stat_cond.wait(aa_this))!=Ok) { WY_THROW(r); // Wym_EINVAL } } } } this->_reset_avars(); _thrd_stat_cond.broadcast(); throw; }; _thrd_stat_cond.broadcast(); return(Ok);}catch(const WyLock::Reply& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );}catch(const WyRet& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );};// [Non-cancellation Point]//WyRet Wy_Thread::cancel(void)try { WyLock aa(_mtx); if(_in_destroy==true) { WY_THROW( WyRet() ); } // Detect earily (in case default _thrd_id is meaningful to pthread) // if(_thrd_id==Wy_ThreadID::Default_ThreadID) { WY_RETURN(Wym_ESRCH); } if(::pthread_equal(_thrd_id,::pthread_self())) { WY_RETURN(Wym_EPERM); // thread can not cancel itself } int v; while((v=::pthread_cancel(_thrd_id))!=0) { // ref. note5 if(v==EAGAIN) { Wy__Base::yield(); continue; } if(v==ESRCH) { // note9 // may happen sometimes (chk_thread2): cancelled running thread // effects in the meanwhile *this is being destroyed // See also ~Wy_Thread() // WY_RETURN( WyRet(v) ); } WY_THROW( WyRet(v) ); // ESRCH (or others not documented) } return(Ok);}catch(const WyLock::Reply& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );}catch(const WyRet& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );};// [Cancel Point]//WyRet Wy_Thread::wait_not(ThreadState state)try { WyLock aa(_mtx); WyRet r; if(_in_destroy==true) { WY_THROW( WyRet() ); } if(_thrd_id!=Wy_ThreadID::Default_ThreadID) { if(::pthread_equal(_thrd_id,::pthread_self())) { // thread cannot wait for the state of itself WY_RETURN(Wym_EPERM); } } switch(state) { case Active: { while(_thrd_stat==Active) { // cancel_point if((r=_thrd_stat_cond.wait(aa))!=Ok) { WY_THROW( WyRet(r) ); // Wym_EINVAL } } }; break; case Null: { while(_thrd_stat==Null) { // cancel_point if((r=_thrd_stat_cond.wait(aa))!=Ok) { WY_THROW( WyRet(r) ); // Wym_EINVAL } } }; break; case Stopped: { while(_thrd_stat==Stopped) { // cancel_point if((r=_thrd_stat_cond.wait(aa))!=Ok) { WY_THROW( WyRet(r) ); // Wym_EINVAL } } }; break; default: WY_THROW( WyRet() ); }; return(Ok);}catch(const WyLock::Reply& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );}catch(const WyRet& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );};// [Non-cancellation Point]//Wy_ThreadID Wy_Thread::thread_self(void){ //::pthread_once(&wy__toltab_once_var, wy__toltab_once_func); return Wy_ThreadID(::pthread_self()); };// [Cancel Point]//void Wy_Thread::cancel_point(void){ //::pthread_once(&wy__toltab_once_var, wy__toltab_once_func); ::pthread_testcancel(); };// [Non-cancellation Point]//WyRet Wy_Thread::yield(void){ ::pthread_once(&wy__toltab_once_var, wy__toltab_once_func); const int v=Wy__Base::yield(); if(v==0) { return(Ok); } WY_RETURN( WyRet(v) );};// [Non-cancellation Point]//// Note: speed remains an issue with this member//Wy_Thread* Wy_Thread::tbase_ptr(void)try { ::pthread_once(&wy__toltab_once_var, wy__toltab_once_func); WyLock aa( wy__toltab_ptr->mutex() ); const Wy_Thread* retv( wy__toltab_ptr->find(Wy_Thread::thread_self()) ); return(const_cast<Wy_Thread*>(retv));}catch(const WyRet& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );};// [Non-cancellation Point]//void Wy_Thread::exit(WyRet exit_ret)try { ::pthread_once(&wy__toltab_once_var, wy__toltab_once_func); WyRet r; const Wy_ThreadID ctid( Wy_Thread::thread_self() ); WyLock aa_toltab(wy__toltab_ptr->mutex()); const Wy_Thread* optr=wy__toltab_ptr->find(ctid); if(optr==NULL) { // // Note: If the calling thread is not created by Wy_Thread, // ::pthread_exit(r.c_repcode()) is called. // ::pthread_exit((void*)(exit_ret->c_repcode())); } WyLock aa_optr(optr->_mtx); #ifndef NDEBUG if(optr->_thrd_stat!=Active) { // assertion: thread tmain() always sees Active state WY_TERMINATE(""); } #endif const_cast<Wy_Thread*>(optr)->_exit_obj.reset(exit_ret); ::pthread_exit( &(const_cast<Wy_Thread*>(optr)->_exit_obj) );}catch(const WyLock::Reply& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );}catch(const WyRet& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );}catch(const std::bad_alloc&) {#ifdef WY_DEBUG Wy::cerr << "std::bad_alloc\n";#endif WY_THROW( WyRet() );}catch(const std::exception&) {#ifdef WY_DEBUG Wy::cerr << "std::exception\n";#endif WY_THROW( WyRet() );};// [Non-cancellation Point]//size_t Wy_Thread::active_threads(void)try { ::pthread_once(&wy__toltab_once_var, wy__toltab_once_func); WyRet r; if((r=wy__toltab_ptr->mutex().lock())!=Ok) { WY_THROW(r); } const size_t retv( wy__toltab_ptr->size() ); if((r=wy__toltab_ptr->mutex().unlock())!=Ok) { WY_THROW(r); } return(retv);}catch(const WyRet& e) {#ifdef WY_DEBUG Wy::cerr << Wy::wrd(e) << '\n';#endif WY_THROW( WyRet(e) );};// note: function form to limit usecases (so far)//size_t Wy_Thread::max_threads(void) WY__TSPC(){ return Max_Threads;};//----------------------------------------------------------------------------/* note2: Wy_Thread assert the function call never fail: :::pthread_setcancelstate( PTHREAD_CANCEL_ENABLE: PTHREAD_CANCEL_DISABLE , &oldstate) it might be better let this function always succeed. note5: ::pthread_cancel found to return EAGAIN (on RH Linux9.0) while man. indicates only fail on ESRCH. note6: Wy_Thread created threads always returns &_exit_obj, which is already set with desired value except cancelled. (So, value of PTHREAD_CANCELED cannot be any valid address, it is -1 as I saw the source) note8: _thrd_stat transition general rule: Null -> Active .. by begin() Active -> Stopped .. by _tmain_finish() * -> Null .. by reset() note9: In rare testing situation, ESRCH may occur. Dont know how, but defensive begin(void)+ _tmain_finish+ reset(void)+ | ^ v | Stopped +---- ----+ _thrd_stat: Active +--- ---+ ----+ Null --+ . +----- ... . _thrd_id: valid +--- --+ ----+ invalid --+ .+-- +----- real thread .+-+-- --+ ---+-+ +----*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -