From 64df75d9aaf3d8ae64ff2881ee571fbb1b3c71f8 Mon Sep 17 00:00:00 2001 From: kevin Date: Sun, 18 Feb 2024 08:37:33 -0500 Subject: [PATCH] Threads now take a label which can be displayed via system tools (e.g. top, ps). --- cwAudioDeviceAlsa.cpp | 2 +- cwAudioDeviceFile.cpp | 2 +- cwAvahiSurface.cpp | 2 +- cwEuCon.cpp | 2 +- cwIo.cpp | 10 +++++----- cwIo.h | 2 +- cwIoMinTest.cpp | 4 ++-- cwMdns.cpp | 2 +- cwMidiDevice.cpp | 5 +++-- cwNbMem.cpp | 2 +- cwSerialPortSrv.cpp | 2 +- cwSocket.cpp | 2 +- cwTcpSocketSrv.cpp | 2 +- cwTcpSocketTest.cpp | 4 ++-- cwThread.cpp | 29 +++++++++++++++++++++++------ cwThread.h | 11 ++++++++++- cwThreadMach.cpp | 10 +++++----- cwThreadMach.h | 2 +- cwWebSockSvr.cpp | 2 +- 19 files changed, 62 insertions(+), 35 deletions(-) diff --git a/cwAudioDeviceAlsa.cpp b/cwAudioDeviceAlsa.cpp index b6abdc0..c866342 100644 --- a/cwAudioDeviceAlsa.cpp +++ b/cwAudioDeviceAlsa.cpp @@ -1368,7 +1368,7 @@ cw::rc_t cw::audio::device::alsa::create( handle_t& hRef, struct driver_str*& dr p->pollfds = mem::allocZ( p->pollfdsAllocCnt ); p->pollfdsDesc = mem::allocZ(p->pollfdsAllocCnt ); - if((rc = thread::create(p->thH,_threadFunc,p)) != kOkRC ) + if((rc = thread::create(p->thH,_threadFunc,p,"alsa_audio")) != kOkRC ) { rc = cwLogError(rc,"Thread create failed."); } diff --git a/cwAudioDeviceFile.cpp b/cwAudioDeviceFile.cpp index 78e0797..001496b 100644 --- a/cwAudioDeviceFile.cpp +++ b/cwAudioDeviceFile.cpp @@ -664,7 +664,7 @@ cw::rc_t cw::audio::device::file::create( handle_t& hRef, struct driver_str*& p->driver.deviceSeek = deviceSeek; p->driver.deviceRealTimeReport = deviceRealTimeReport; - if((rc = create( p->threadH, _threadCbFunc, p )) != kOkRC ) + if((rc = create( p->threadH, _threadCbFunc, p, "audio_dev_test" )) != kOkRC ) { rc = cwLogError(rc,"Audio device file thread create failed."); goto errLabel; diff --git a/cwAvahiSurface.cpp b/cwAvahiSurface.cpp index 1e9d18e..257201a 100644 --- a/cwAvahiSurface.cpp +++ b/cwAvahiSurface.cpp @@ -412,7 +412,7 @@ int main( int argc, const char* argv[] ) // create the TCP listening thread - if((rc = thread::create( app.tcpThreadH, tcpReceiveCallback, &app )) != kOkRC ) + if((rc = thread::create( app.tcpThreadH, tcpReceiveCallback, &app, "avahi_suf" )) != kOkRC ) goto errLabel; // Allocate Avahi thread diff --git a/cwEuCon.cpp b/cwEuCon.cpp index e525eaf..f7b482f 100644 --- a/cwEuCon.cpp +++ b/cwEuCon.cpp @@ -942,7 +942,7 @@ namespace cw return cwLogError(rc,"Unable to create EuCon server."); // Create the application thread - if((rc = thread::create( app.thH, appThreadFunc, &app )) != kOkRC ) + if((rc = thread::create( app.thH, appThreadFunc, &app, "eu_con" )) != kOkRC ) return cwLogError(rc,"App thread create failed."); // Start the application thread diff --git a/cwIo.cpp b/cwIo.cpp index 024f215..483910e 100644 --- a/cwIo.cpp +++ b/cwIo.cpp @@ -371,7 +371,7 @@ namespace cw time::get(t->nextTime); - if((rc = thread_mach::add(p->threadMachH,_timerThreadCb,t)) != kOkRC ) + if((rc = thread_mach::add(p->threadMachH,_timerThreadCb,t, label)) != kOkRC ) { rc = cwLogError(rc,"Timer thread assignment failed."); } @@ -848,7 +848,7 @@ namespace cw // create the socket thread if( p->sockN > 0 ) - if((rc = thread_mach::add(p->threadMachH,_socketThreadFunc,p)) != kOkRC ) + if((rc = thread_mach::add(p->threadMachH,_socketThreadFunc,p,"io_socket")) != kOkRC ) { rc = cwLogError(rc,"Error creating socket thread."); goto errLabel; @@ -1567,7 +1567,7 @@ namespace cw } // create the audio group thread - if((rc = thread_mach::add(p->threadMachH,_audioGroupThreadFunc,p->audioGroupA+i)) != kOkRC ) + if((rc = thread_mach::add(p->threadMachH,_audioGroupThreadFunc,p->audioGroupA+i,"io_audio_group")) != kOkRC ) { rc = cwLogError(rc,"Error creating audio group thread."); goto errLabel; @@ -2462,7 +2462,7 @@ void cw::io::realTimeReport( handle_t h ) // Thread // -cw::rc_t cw::io::threadCreate( handle_t h, unsigned id, bool asyncFl, void* arg ) +cw::rc_t cw::io::threadCreate( handle_t h, unsigned id, bool asyncFl, void* arg, const char* label ) { rc_t rc = kOkRC; io_t* p = _handleToPtr(h); @@ -2475,7 +2475,7 @@ cw::rc_t cw::io::threadCreate( handle_t h, unsigned id, bool asyncFl, void* arg t->link = p->threadL; p->threadL = t; - if((rc = thread_mach::add( p->threadMachH, _threadFunc, t )) != kOkRC ) + if((rc = thread_mach::add( p->threadMachH, _threadFunc, t, label )) != kOkRC ) rc = cwLogError(rc,"Thread create failed."); return rc; diff --git a/cwIo.h b/cwIo.h index 3c5bbcb..0137e72 100644 --- a/cwIo.h +++ b/cwIo.h @@ -177,7 +177,7 @@ namespace cw // // Thread // - rc_t threadCreate( handle_t h, unsigned id, bool asyncFl, void* arg ); + rc_t threadCreate( handle_t h, unsigned id, bool asyncFl, void* arg, const char* label ); //---------------------------------------------------------------------------------------------------------- // diff --git a/cwIoMinTest.cpp b/cwIoMinTest.cpp index ba41c9c..bbeffea 100644 --- a/cwIoMinTest.cpp +++ b/cwIoMinTest.cpp @@ -94,13 +94,13 @@ cw::rc_t cw::min_test( const object_t* cfg ) if((rc = create(app.ioH,cfg,minTestCb,&app)) != kOkRC ) return rc; - if((rc = threadCreate( app.ioH, kThread0Id, asyncFl, &app )) != kOkRC ) + if((rc = threadCreate( app.ioH, kThread0Id, asyncFl, &app, "min_test_0" )) != kOkRC ) { rc = cwLogError(rc,"Thread 0 create failed."); goto errLabel; } - if((rc = threadCreate( app.ioH, kThread1Id, asyncFl, &app )) != kOkRC ) + if((rc = threadCreate( app.ioH, kThread1Id, asyncFl, &app, "min_test_1" )) != kOkRC ) { rc = cwLogError(rc,"Thread 1 create failed."); goto errLabel; diff --git a/cwMdns.cpp b/cwMdns.cpp index 5963bb4..55d5760 100644 --- a/cwMdns.cpp +++ b/cwMdns.cpp @@ -1317,7 +1317,7 @@ cw::rc_t cw::net::mdns::test() } // create the TCP listening thread - if((rc = thread::create( app.tcpThreadH, tcpReceiveCallback, &app )) != kOkRC ) + if((rc = thread::create( app.tcpThreadH, tcpReceiveCallback, &app, "mdns" )) != kOkRC ) goto errLabel; diff --git a/cwMidiDevice.cpp b/cwMidiDevice.cpp index 9935610..7e364cc 100644 --- a/cwMidiDevice.cpp +++ b/cwMidiDevice.cpp @@ -216,7 +216,8 @@ cw::rc_t cw::midi::device::create( handle_t& hRef, if((rc = thread::create(p->threadH, _thread_func, - p)) != kOkRC ) + p, + "midi_dev")) != kOkRC ) { rc = cwLogError(rc,"The MIDI file device thread create failed."); goto errLabel; @@ -556,7 +557,7 @@ errLabel: cw::rc_t cw::midi::device::start( handle_t h ) { - rc_t rc; + rc_t rc = kOkRC; device_t* p = _handleToPtr(h); if( p->fileDevStateId != kPlayingStateId ) diff --git a/cwNbMem.cpp b/cwNbMem.cpp index d736345..209c463 100644 --- a/cwNbMem.cpp +++ b/cwNbMem.cpp @@ -365,7 +365,7 @@ cw::rc_t cw::nbmem::test_multi_threaded() ctx.threadA[i].varA = mem::allocZ(threadVarN); ctx.threadA[i].varN = threadVarN; - if((rc = thread::create(ctx.threadA[i].threadH, _test_thread_func, ctx.threadA + i )) != kOkRC ) + if((rc = thread::create(ctx.threadA[i].threadH, _test_thread_func, ctx.threadA + i, "nb_mem" )) != kOkRC ) break; } diff --git a/cwSerialPortSrv.cpp b/cwSerialPortSrv.cpp index bd73436..7030dc1 100644 --- a/cwSerialPortSrv.cpp +++ b/cwSerialPortSrv.cpp @@ -92,7 +92,7 @@ cw::rc_t cw::serialPortSrv::create( handle_t& h, unsigned pollPeriodMs, unsigned if((rc = serialPort::create( p->mgrH, recvBufByteN)) != kOkRC ) goto errLabel; - if((rc = thread::create( p->threadH, threadCallback, p)) != kOkRC ) + if((rc = thread::create( p->threadH, threadCallback, p, "serial_srv")) != kOkRC ) goto errLabel; p->pollPeriodMs = pollPeriodMs; diff --git a/cwSocket.cpp b/cwSocket.cpp index ae3b391..cbdb215 100644 --- a/cwSocket.cpp +++ b/cwSocket.cpp @@ -1211,7 +1211,7 @@ cw::rc_t cw::socksrv::createMgrSrv( handle_t& hRef, unsigned timeOutMs, unsigne goto errLabel; // create the thread - if((rc = thread::create( p->thH, _threadFunc, p)) != kOkRC ) + if((rc = thread::create( p->thH, _threadFunc, p, "sock")) != kOkRC ) goto errLabel; p->timeOutMs = timeOutMs; diff --git a/cwTcpSocketSrv.cpp b/cwTcpSocketSrv.cpp index 3b2a6d1..0ae6041 100644 --- a/cwTcpSocketSrv.cpp +++ b/cwTcpSocketSrv.cpp @@ -110,7 +110,7 @@ cw::rc_t cw::net::srv::create( if((rc = socket::create( p->sockH, port, flags, timeOutMs, remoteAddr, remotePort, localAddr )) != kOkRC ) goto errLabel; - if((rc = thread::create( p->threadH, _threadFunc, p )) != kOkRC ) + if((rc = thread::create( p->threadH, _threadFunc, p, "tcp_sock_srv" )) != kOkRC ) goto errLabel; p->flags = srvFlags; diff --git a/cwTcpSocketTest.cpp b/cwTcpSocketTest.cpp index 631754c..9d36dde 100644 --- a/cwTcpSocketTest.cpp +++ b/cwTcpSocketTest.cpp @@ -120,7 +120,7 @@ cw::rc_t cw::net::socket::test( portNumber_t localPort, const char* remoteAddr, if((rc = create(app.sockH,localPort, kBlockingFl,timeOutMs, NULL, kInvalidPortNumber )) != kOkRC ) return rc; - if((rc = thread::create( app.threadH, _dgramThreadFunc, &app )) != kOkRC ) + if((rc = thread::create( app.threadH, _dgramThreadFunc, &app, "tcp_sock_test_tcp" )) != kOkRC ) goto errLabel; if((rc = thread::unpause( app.threadH )) != kOkRC ) @@ -176,7 +176,7 @@ cw::rc_t cw::net::socket::test_tcp( portNumber_t localPort, const char* remoteAd return rc; // create the listening thread (which is really only used by the server) - if((rc = thread::create( app.threadH, streamFl ? _tcpStreamThreadFunc : _dgramThreadFunc, &app )) != kOkRC ) + if((rc = thread::create( app.threadH, streamFl ? _tcpStreamThreadFunc : _dgramThreadFunc, &app, "tcp_sock_test" )) != kOkRC ) goto errLabel; // if this is a streaming client then connect to the server (which must have already been started) diff --git a/cwThread.cpp b/cwThread.cpp index 1154ef2..f900e2a 100644 --- a/cwThread.cpp +++ b/cwThread.cpp @@ -31,6 +31,7 @@ namespace cw unsigned pauseMicros; unsigned sleepMicros; pthread_attr_t attr; + char* label; } thread_t; @@ -120,7 +121,7 @@ namespace cw } -cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, int stateMicros, int pauseMicros ) +cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, const char* label, int stateMicros, int pauseMicros ) { rc_t rc; int sysRC; @@ -136,6 +137,7 @@ cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, int s p->pauseMicros = pauseMicros; p->stateId = kPausedThId; p->sleepMicros = 15000; + p->label = mem::duplStr(label); if((sysRC = pthread_attr_init(&p->attr)) != 0) { @@ -162,8 +164,16 @@ cw::rc_t cw::thread::create( handle_t& hRef, cbFunc_t func, void* funcArg, int s rc = cwLogSysError(kOpFailRC,sysRC,"Thread create failed."); } } + + if( label != nullptr ) + pthread_setname_np(p->pThreadH, label); + hRef.set(p); + + + cwLogInfo("Thread %s id:%p created.",cwStringNullGuard(label), p->pThreadH); + return rc; } @@ -182,16 +192,16 @@ cw::rc_t cw::thread::destroy( handle_t& hRef ) // wait for the thread to exit and then deallocate the thread object if((rc = _waitForState(p,kExitedThId)) != kOkRC ) - return cwLogError(rc,"Thread timed out waiting for destroy."); + return cwLogError(rc,"Thread '%s' timed out waiting for destroy.",p->label); // Block until the thread is actually fully cleaned up if((sysRC = pthread_join(p->pThreadH,NULL)) != 0) - rc = cwLogSysError(kOpFailRC,sysRC,"Thread join failed."); + rc = cwLogSysError(kOpFailRC,sysRC,"Thread '%s' join failed.",p->label); //if( pthread_attr_destroy(&p->attr) != 0 ) // rc = cwLogError(kOpFailRC,"Thread attribute destroy failed."); - + mem::release(p->label); mem::release(p); hRef.clear(); @@ -227,7 +237,7 @@ cw::rc_t cw::thread::pause( handle_t h, unsigned cmdFlags ) rc = _waitForState(p,waitId); if( rc != kOkRC ) - cwLogError(rc,"Thread timed out waiting for '%s'. pauseMicros:%i stateMicros:%i sleepMicros:%i", pauseFl ? "pause" : "un-pause",p->pauseMicros,p->stateMicros,p->sleepMicros); + cwLogError(rc,"Thread '%s' timed out waiting for '%s'. pauseMicros:%i stateMicros:%i sleepMicros:%i", p->label, pauseFl ? "pause" : "un-pause",p->pauseMicros,p->stateMicros,p->sleepMicros); return rc; @@ -258,6 +268,13 @@ cw::thread::thread_id_t cw::thread::id() return id.u.id; } +const char* cw::thread::label( handle_t h ) +{ + thread_t* p = _handleToPtr(h); + return p->label==nullptr ? "" : p->label; +} + + unsigned cw::thread::stateTimeOutMicros( handle_t h) { thread_t* p = _handleToPtr(h); @@ -288,7 +305,7 @@ cw::rc_t cw::threadTest() rc_t rc; char c = 0; - if((rc = thread::create(h,_threadTestCb,&val)) != kOkRC ) + if((rc = thread::create(h,_threadTestCb,&val,"thread_test")) != kOkRC ) return rc; if((rc = thread::pause(h,0)) != kOkRC ) diff --git a/cwThread.h b/cwThread.h index 7a12181..4260bd1 100644 --- a/cwThread.h +++ b/cwThread.h @@ -5,6 +5,8 @@ namespace cw { namespace thread { + const int kDefaultStateTimeOutMicros=100000; + const int kDefaultPauseMicros = 10000; typedef enum { kNotInitThId, @@ -23,7 +25,13 @@ namespace cw // The thread is in the 'paused' state after it is created. // stateMicros = total time out duration for switching to the exit state or for switching in/out of pause state. // pauseMicros = duration of thread sleep interval when in paused state. - rc_t create( handle_t& hRef, cbFunc_t func, void* funcArg, int stateTimeOutMicros=100000, int pauseMicros=10000 ); + rc_t create( handle_t& hRef, + cbFunc_t func, + void* funcArg, + const char* label, // Assign a label which will show up via `top -H` or `ps -T`. + int stateTimeOutMicros=kDefaultStateTimeOutMicros, + int pauseMicros=kDefaultPauseMicros ); + rc_t destroy( handle_t& hRef ); @@ -35,6 +43,7 @@ namespace cw // Return the thread id of the calling context. thread_id_t id(); + const char* label( handle_t h ); unsigned stateTimeOutMicros( handle_t h); unsigned pauseMicros( handle_t h ); diff --git a/cwThreadMach.cpp b/cwThreadMach.cpp index 6f863a0..b35401f 100644 --- a/cwThreadMach.cpp +++ b/cwThreadMach.cpp @@ -25,13 +25,13 @@ namespace cw thread_mach_t* _handleToPtr( handle_t h ) { return handleToPtr(h); } - rc_t _add( thread_mach_t* p, threadFunc_t func, void* arg ) + rc_t _add( thread_mach_t* p, threadFunc_t func, void* arg, const char* label ) { rc_t rc = kOkRC; thread_t* t = mem::allocZ(); - if((rc = thread::create(t->thH, func, arg )) != kOkRC ) + if((rc = thread::create(t->thH, func, arg, label==nullptr ? "thread_mach" : label )) != kOkRC ) { rc = cwLogError(rc,"Thread create failed."); goto errLabel; @@ -90,7 +90,7 @@ cw::rc_t cw::thread_mach::create( handle_t& hRef, threadFunc_t threadFunc, void* { void* arg = ctxA + (i*contexRecdByteN); - if((rc = _add(p, threadFunc, arg)) != kOkRC ) + if((rc = _add(p, threadFunc, arg, nullptr)) != kOkRC ) goto errLabel; } @@ -103,10 +103,10 @@ cw::rc_t cw::thread_mach::create( handle_t& hRef, threadFunc_t threadFunc, void* return rc; } -cw::rc_t cw::thread_mach::add( handle_t h, threadFunc_t threadFunc, void* arg ) +cw::rc_t cw::thread_mach::add( handle_t h, threadFunc_t threadFunc, void* arg, const char* label ) { thread_mach_t* p = _handleToPtr(h); - return _add(p,threadFunc,arg); + return _add(p,threadFunc,arg, label); } cw::rc_t cw::thread_mach::destroy( handle_t& hRef ) diff --git a/cwThreadMach.h b/cwThreadMach.h index 57c3921..11cb011 100644 --- a/cwThreadMach.h +++ b/cwThreadMach.h @@ -16,7 +16,7 @@ namespace cw // Create an additional thread. Note that the additional thread will be started by the next // call to 'start()'. - rc_t add( handle_t h, threadFunc_t threadFunc, void* arg ); + rc_t add( handle_t h, threadFunc_t threadFunc, void* arg, const char* label ); // Start all threads rc_t start( handle_t h ); diff --git a/cwWebSockSvr.cpp b/cwWebSockSvr.cpp index 98791fb..e55dbc4 100644 --- a/cwWebSockSvr.cpp +++ b/cwWebSockSvr.cpp @@ -68,7 +68,7 @@ cw::rc_t cw::websockSrv::create( goto errLabel; - if((rc = thread::create(p->_thread,_websockSrvThreadCb,p)) != kOkRC ) + if((rc = thread::create(p->_thread,_websockSrvThreadCb,p,"web_sock_srv")) != kOkRC ) goto errLabel; p->_timeOutMs = timeOutMs;