Vault
4.1
|
00001 /* 00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved. 00003 This file is part of the Code Vault version 4.1 00004 http://www.bombaydigital.com/ 00005 License: MIT. See LICENSE.md in the Vault top level directory. 00006 */ 00007 00010 #include "vthread.h" 00011 00012 #include <exception> 00013 #include "vexception.h" 00014 #include "vmanagementinterface.h" 00015 #include "vlogger.h" 00016 #include "vmutexlocker.h" 00017 #include "vbento.h" 00018 00019 // This private map allows us to keep track of all VThread objects, and therefore to 00020 // find the current thread's VThread object from the thread ID. We also have an API 00021 // to get info about all these threads. 00022 typedef std::map<VThreadID_Type, VThread*> VThreadIDToVThreadMap; 00023 VThreadIDToVThreadMap gVThreadIDToVThreadMap; 00024 static VMutex gVThreadMapMutex("gVThreadMapMutex", true/*suppress logging because logging itself uses this*/); 00025 00026 static void _vthreadStarting(VThread* thread) { 00027 VMutexLocker locker(&gVThreadMapMutex, "_vthreadStarting"); 00028 gVThreadIDToVThreadMap[thread->threadID()] = thread; 00029 } 00030 00031 static void _vthreadEnded(VThread* thread) { 00032 VMutexLocker locker(&gVThreadMapMutex, "_vthreadEnded"); 00033 VThreadIDToVThreadMap::iterator position = gVThreadIDToVThreadMap.find(thread->threadID()); 00034 if (position != gVThreadIDToVThreadMap.end()) 00035 gVThreadIDToVThreadMap.erase(position); 00036 } 00037 00042 class VStandinThread : public VThread { 00043 public: 00044 VStandinThread() : VThread("?", "vault.threads.VStandinThread", false, false, NULL) {} 00045 virtual ~VStandinThread() {} 00046 virtual void run() {} 00047 }; 00048 00049 static VStandinThread gStandinThread; 00050 00051 static VThread* _getCurrentVThread() { 00052 VThreadID_Type currentThreadID = VThread::threadSelf(); 00053 VMutexLocker locker(&gVThreadMapMutex, "_getCurrentVThread"); 00054 VThreadIDToVThreadMap::const_iterator position = gVThreadIDToVThreadMap.find(currentThreadID); 00055 if (position == gVThreadIDToVThreadMap.end()) 00056 return &gStandinThread; // If called from main thread, or non-VThread-derived thread, we won't find a VThread. This allows us to return something workable to any caller. 00057 00058 return (*position).second; 00059 // Note: once we return, the thread could stop. 00060 // But since this is called from the current thread, it really can't disappear while caller lives. 00061 // It just can't be passed around to other threads! 00062 } 00063 00064 VThread::VThread(const VString& name, const VString& loggerName, bool deleteAtEnd, bool createDetached, VManagementInterface* manager) 00065 : mIsDeleted(false) 00066 , mName(name) 00067 , mLoggerName(loggerName) 00068 , mDeleteAtEnd(deleteAtEnd) 00069 , mCreateDetached(createDetached) 00070 , mManager(manager) 00071 , mThreadID((VThreadID_Type) - 1) 00072 , mIsRunning(false) 00073 { 00074 } 00075 00076 VThread::~VThread() { 00077 // Detect repeat deletion bug. Can't refer to mName because it's been deleted. 00078 if (mIsDeleted) 00079 VLOGGER_NAMED_ERROR(mLoggerName, VSTRING_FORMAT("Thread delete on already-deleted thread @0x%08X.", this)); 00080 00081 mIsDeleted = true; 00082 mIsRunning = false; 00083 mThreadID = (VThreadID_Type) - 1; 00084 } 00085 00086 void VThread::start() { 00087 if (mIsRunning) { 00088 return; 00089 } 00090 00091 mIsRunning = true; 00092 00093 try { 00094 VThread::threadCreate(&mThreadID, mCreateDetached, VThread::userThreadMain, (void*) this); 00095 } catch (...) { 00096 mIsRunning = false; 00097 throw; 00098 } 00099 } 00100 00101 void VThread::stop() { 00102 mIsRunning = false; 00103 } 00104 00105 VThreadID_Type VThread::threadID() const { 00106 return mThreadID; 00107 } 00108 00109 bool VThread::isRunning() const { 00110 return mIsRunning; 00111 } 00112 00113 bool VThread::join() { 00114 // FIXME: could complain here if mCreateDetached is true. 00115 00116 if (! mIsRunning || (mThreadID == (VThreadID_Type) - 1)) { 00117 return true; // never started, or was stopped, so we're done 00118 } else { 00119 return VThread::threadJoin(mThreadID, NULL); 00120 } 00121 } 00122 00123 bool VThread::getDeleteAtEnd() const { 00124 return mDeleteAtEnd; 00125 } 00126 00127 VManagementInterface* VThread::getManagementInterface() const { 00128 return mManager; 00129 } 00130 00131 #ifdef VAULT_SIMPLE_USER_THREAD_MAIN 00132 void* VThread::userThreadMain(void* arg) { 00133 return VThread::threadMain(arg); 00134 } 00135 #endif 00136 00137 void* VThread::threadMain(void* arg) { 00138 VThread* thread = static_cast<VThread*>(arg); 00139 VString threadName = thread->getName(); 00140 VString threadLoggerName = thread->getLoggerName(); 00141 00142 VLOGGER_NAMED_TRACE(threadLoggerName, VSTRING_FORMAT("VThread::threadMain: start of thread '%s' id 0x%08X.", threadName.chars(), thread->threadID())); 00143 00144 bool deleteAtEnd = thread->getDeleteAtEnd(); 00145 00146 VManagementInterface* manager = thread->getManagementInterface(); 00147 00148 try { 00149 VAutoreleasePool pool; 00150 _vthreadStarting(thread); 00151 VThread::_threadStarting(thread); 00152 00153 if (manager != NULL) { 00154 manager->threadStarting(thread); 00155 } 00156 00157 thread->run(); 00158 } catch (const VException& ex) { 00159 VLOGGER_NAMED_ERROR(threadLoggerName, VSTRING_FORMAT("Thread '%s' main caught exception #%d '%s'.", threadName.chars(), ex.getError(), ex.what())); 00160 } catch (const std::exception& ex) { 00161 VLOGGER_NAMED_ERROR(threadLoggerName, VSTRING_FORMAT("Thread '%s' main caught exception '%s'.", threadName.chars(), ex.what())); 00162 } catch (...) { 00163 VLOGGER_NAMED_ERROR(threadLoggerName, VSTRING_FORMAT("Thread '%s' main caught unknown exception.", threadName.chars())); 00164 } 00165 00166 // Let's be bulletproof even on this notification -- use try/catch. 00167 try { 00168 if (manager != NULL) { 00169 VLOGGER_NAMED_TRACE(threadLoggerName, VSTRING_FORMAT("VThread '%s' notifying manager[0x%08X] of thread end.", threadName.chars(), manager)); 00170 manager->threadEnded(thread); 00171 } 00172 } catch (...) { 00173 VLOGGER_NAMED_ERROR(threadLoggerName, VSTRING_FORMAT("Thread '%s' main caught exception notifying manager of thread end.", threadName.chars())); 00174 } 00175 00176 VThread::_threadEnded(thread); 00177 _vthreadEnded(thread); 00178 00179 if (deleteAtEnd) { 00180 delete thread; 00181 } 00182 00183 VLOGGER_NAMED_TRACE(threadLoggerName, VSTRING_FORMAT("VThread::threadMain: completed thread '%s'.", threadName.chars())); 00184 00185 return NULL; 00186 } 00187 00188 // static 00189 VThread* VThread::getCurrentThread() { 00190 return _getCurrentVThread(); 00191 } 00192 00193 // static 00194 VString VThread::getCurrentThreadName() { 00195 VThread* currentThread = VThread::getCurrentThread(); 00196 if (currentThread != &gStandinThread) { 00197 return currentThread->getName(); 00198 } 00199 00200 // It's the stand-in thread for non-VThread threads. Its name is meaningless. 00201 // Format the current OS thread ID. 00202 Vs64 id64 = (Vs64) VThread::threadSelf(); 00203 return VSTRING_S64(id64); 00204 } 00205 00206 // static 00207 void VThread::getThreadsInfo(VBentoNode& bento) { 00208 bento.setName("threads"); 00209 00210 VMutexLocker locker(&gVThreadMapMutex, "VThread::getThreadsInfo"); 00211 for (VThreadIDToVThreadMap::const_iterator i = gVThreadIDToVThreadMap.begin(); i != gVThreadIDToVThreadMap.end(); ++i) { 00212 VThread* thread = (*i).second; 00213 VBentoNode* child = bento.addNewChildNode("thread"); 00214 child->addString("name", thread->getName()); 00215 child->addS64("threadID", reinterpret_cast<Vs64>((void*) thread->mThreadID)); // Convoluted casting to make all platforms happy showing different id types as a 64-bit number. 00216 child->addBool("isRunning", thread->mIsRunning); 00217 child->addBool("isDeleted", thread->mIsDeleted); 00218 child->addBool("deleteAtEnd", thread->mDeleteAtEnd); 00219 child->addBool("createdDetached", thread->mCreateDetached); 00220 child->addBool("hasManager", thread->mManager != NULL); 00221 } 00222 } 00223 00224 // static 00225 VString VThread::getThreadName(VThreadID_Type threadID) { 00226 VMutexLocker locker(&gVThreadMapMutex, "VThread::getThreadName"); 00227 VThreadIDToVThreadMap::iterator position = gVThreadIDToVThreadMap.find(threadID); 00228 if (position == gVThreadIDToVThreadMap.end()) { 00229 return VString::EMPTY(); 00230 } 00231 00232 VThread* thread = (*position).second; 00233 VString threadName = thread->getName(); 00234 return threadName; 00235 } 00236 00237 // static 00238 void VThread::stopThread(VThreadID_Type threadID) { 00239 VMutexLocker locker(&gVThreadMapMutex, "VThread::stopThread"); 00240 VThreadIDToVThreadMap::iterator position = gVThreadIDToVThreadMap.find(threadID); 00241 if (position != gVThreadIDToVThreadMap.end()) { 00242 VThread* thread = (*position).second; 00243 thread->stop(); 00244 } 00245 } 00246 00247 #ifndef VAULT_USER_STACKCRAWL_SUPPORT 00248 // static 00249 void VThread::logStackCrawl(const VString& headerMessage, VNamedLoggerPtr logger, bool /*verbose*/) { 00250 if (logger == nullptr) { 00251 VLOGGER_ERROR(VSTRING_FORMAT("%s (VThread::logStackCrawl: User stack crawl not implemented.)", headerMessage.chars())); 00252 } else { 00253 logger->emitStackCrawlLine(VSTRING_FORMAT("%s (VThread::logStackCrawl: User stack crawl not implemented.)", headerMessage.chars())); 00254 } 00255 } 00256 #endif /* VAULT_USER_STACKCRAWL_SUPPORT */ 00257 00258 // VMainThread ---------------------------------------------------------------- 00259 00260 VMainThread::VMainThread() 00261 : VThread("main", "vault.threads.VMainThread", kDontDeleteSelfAtEnd, kCreateThreadJoinable/*doesn't matter*/, NULL) 00262 { 00263 mThreadID = VThread::threadSelf(); 00264 _vthreadStarting(this); // Register this object for lookup by mThreadID. 00265 } 00266 00267 VMainThread::~VMainThread() { 00268 _vthreadEnded(this); // De-register this object. 00269 } 00270 00271 void VMainThread::start() { 00272 VString errorMessage("Error: invalid attempt to start VMainThread."); 00273 VLOGGER_NAMED_FATAL(mLoggerName, errorMessage); 00274 throw VStackTraceException(errorMessage); 00275 } 00276 00277 int VMainThread::execute(int argc, char** argv) { 00278 return VThread::userMain(argc, argv); 00279 } 00280 00281 // VForeignThread ---------------------------------------------------------------- 00282 00283 VForeignThread::VForeignThread(const VString& name) 00284 : VThread(name, "vault.threads.VForeignThread", kDontDeleteSelfAtEnd, kCreateThreadJoinable/*doesn't matter*/, NULL) 00285 { 00286 mThreadID = VThread::threadSelf(); 00287 _vthreadStarting(this); // Register this object for lookup by mThreadID. 00288 } 00289 00290 VForeignThread::~VForeignThread() { 00291 _vthreadEnded(this); // De-register this object. 00292 } 00293 00294 void VForeignThread::start() { 00295 VString errorMessage("Error: invalid attempt to start VForeignThread."); 00296 VLOGGER_NAMED_FATAL(mLoggerName, errorMessage); 00297 throw VStackTraceException(errorMessage); 00298 } 00299 00300