Vault  4.1
vthread.cpp
Go to the documentation of this file.
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 

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.