forked from trygve-isaacson/code-vault
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvthread.cpp
More file actions
300 lines (244 loc) · 10.1 KB
/
vthread.cpp
File metadata and controls
300 lines (244 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/*
Copyright c1997-2014 Trygve Isaacson. All rights reserved.
This file is part of the Code Vault version 4.1
http://www.bombaydigital.com/
License: MIT. See LICENSE.md in the Vault top level directory.
*/
/** @file */
#include "vthread.h"
#include <exception>
#include "vexception.h"
#include "vmanagementinterface.h"
#include "vlogger.h"
#include "vmutexlocker.h"
#include "vbento.h"
// This private map allows us to keep track of all VThread objects, and therefore to
// find the current thread's VThread object from the thread ID. We also have an API
// to get info about all these threads.
typedef std::map<VThreadID_Type, VThread*> VThreadIDToVThreadMap;
VThreadIDToVThreadMap gVThreadIDToVThreadMap;
static VMutex gVThreadMapMutex("gVThreadMapMutex", true/*suppress logging because logging itself uses this*/);
static void _vthreadStarting(VThread* thread) {
VMutexLocker locker(&gVThreadMapMutex, "_vthreadStarting");
gVThreadIDToVThreadMap[thread->threadID()] = thread;
}
static void _vthreadEnded(VThread* thread) {
VMutexLocker locker(&gVThreadMapMutex, "_vthreadEnded");
VThreadIDToVThreadMap::iterator position = gVThreadIDToVThreadMap.find(thread->threadID());
if (position != gVThreadIDToVThreadMap.end())
gVThreadIDToVThreadMap.erase(position);
}
/**
VStandinThread is a special VThread object we simply declare as gStandinThread (but never execute) and reference
if we need to return a reference to the current thread but it's not one of our threads.
*/
class VStandinThread : public VThread {
public:
VStandinThread() : VThread("?", "vault.threads.VStandinThread", false, false, NULL) {}
virtual ~VStandinThread() {}
virtual void run() {}
};
static VStandinThread gStandinThread;
static VThread* _getCurrentVThread() {
VThreadID_Type currentThreadID = VThread::threadSelf();
VMutexLocker locker(&gVThreadMapMutex, "_getCurrentVThread");
VThreadIDToVThreadMap::const_iterator position = gVThreadIDToVThreadMap.find(currentThreadID);
if (position == gVThreadIDToVThreadMap.end())
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.
return (*position).second;
// Note: once we return, the thread could stop.
// But since this is called from the current thread, it really can't disappear while caller lives.
// It just can't be passed around to other threads!
}
VThread::VThread(const VString& name, const VString& loggerName, bool deleteAtEnd, bool createDetached, VManagementInterface* manager)
: mIsDeleted(false)
, mName(name)
, mLoggerName(loggerName)
, mDeleteAtEnd(deleteAtEnd)
, mCreateDetached(createDetached)
, mManager(manager)
, mThreadID((VThreadID_Type) - 1)
, mIsRunning(false)
{
}
VThread::~VThread() {
// Detect repeat deletion bug. Can't refer to mName because it's been deleted.
if (mIsDeleted)
VLOGGER_NAMED_ERROR(mLoggerName, VSTRING_FORMAT("Thread delete on already-deleted thread @0x%08X.", this));
mIsDeleted = true;
mIsRunning = false;
mThreadID = (VThreadID_Type) - 1;
}
void VThread::start() {
if (mIsRunning) {
return;
}
mIsRunning = true;
try {
VThread::threadCreate(&mThreadID, mCreateDetached, VThread::userThreadMain, (void*) this);
} catch (...) {
mIsRunning = false;
throw;
}
}
void VThread::stop() {
mIsRunning = false;
}
VThreadID_Type VThread::threadID() const {
return mThreadID;
}
bool VThread::isRunning() const {
return mIsRunning;
}
bool VThread::join() {
// FIXME: could complain here if mCreateDetached is true.
if (! mIsRunning || (mThreadID == (VThreadID_Type) - 1)) {
return true; // never started, or was stopped, so we're done
} else {
return VThread::threadJoin(mThreadID, NULL);
}
}
bool VThread::getDeleteAtEnd() const {
return mDeleteAtEnd;
}
VManagementInterface* VThread::getManagementInterface() const {
return mManager;
}
#ifdef VAULT_SIMPLE_USER_THREAD_MAIN
void* VThread::userThreadMain(void* arg) {
return VThread::threadMain(arg);
}
#endif
void* VThread::threadMain(void* arg) {
VThread* thread = static_cast<VThread*>(arg);
VString threadName = thread->getName();
VString threadLoggerName = thread->getLoggerName();
VLOGGER_NAMED_TRACE(threadLoggerName, VSTRING_FORMAT("VThread::threadMain: start of thread '%s' id 0x%08X.", threadName.chars(), thread->threadID()));
bool deleteAtEnd = thread->getDeleteAtEnd();
VManagementInterface* manager = thread->getManagementInterface();
try {
VAutoreleasePool pool;
_vthreadStarting(thread);
VThread::_threadStarting(thread);
if (manager != NULL) {
manager->threadStarting(thread);
}
thread->run();
} catch (const VException& ex) {
VLOGGER_NAMED_ERROR(threadLoggerName, VSTRING_FORMAT("Thread '%s' main caught exception #%d '%s'.", threadName.chars(), ex.getError(), ex.what()));
} catch (const std::exception& ex) {
VLOGGER_NAMED_ERROR(threadLoggerName, VSTRING_FORMAT("Thread '%s' main caught exception '%s'.", threadName.chars(), ex.what()));
} catch (...) {
VLOGGER_NAMED_ERROR(threadLoggerName, VSTRING_FORMAT("Thread '%s' main caught unknown exception.", threadName.chars()));
}
// Let's be bulletproof even on this notification -- use try/catch.
try {
if (manager != NULL) {
VLOGGER_NAMED_TRACE(threadLoggerName, VSTRING_FORMAT("VThread '%s' notifying manager[0x%08X] of thread end.", threadName.chars(), manager));
manager->threadEnded(thread);
}
} catch (...) {
VLOGGER_NAMED_ERROR(threadLoggerName, VSTRING_FORMAT("Thread '%s' main caught exception notifying manager of thread end.", threadName.chars()));
}
VThread::_threadEnded(thread);
_vthreadEnded(thread);
if (deleteAtEnd) {
delete thread;
}
VLOGGER_NAMED_TRACE(threadLoggerName, VSTRING_FORMAT("VThread::threadMain: completed thread '%s'.", threadName.chars()));
return NULL;
}
// static
VThread* VThread::getCurrentThread() {
return _getCurrentVThread();
}
// static
VString VThread::getCurrentThreadName() {
VThread* currentThread = VThread::getCurrentThread();
if (currentThread != &gStandinThread) {
return currentThread->getName();
}
// It's the stand-in thread for non-VThread threads. Its name is meaningless.
// Format the current OS thread ID.
Vs64 id64 = (Vs64) VThread::threadSelf();
return VSTRING_S64(id64);
}
// static
void VThread::getThreadsInfo(VBentoNode& bento) {
bento.setName("threads");
VMutexLocker locker(&gVThreadMapMutex, "VThread::getThreadsInfo");
for (VThreadIDToVThreadMap::const_iterator i = gVThreadIDToVThreadMap.begin(); i != gVThreadIDToVThreadMap.end(); ++i) {
VThread* thread = (*i).second;
VBentoNode* child = bento.addNewChildNode("thread");
child->addString("name", thread->getName());
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.
child->addBool("isRunning", thread->mIsRunning);
child->addBool("isDeleted", thread->mIsDeleted);
child->addBool("deleteAtEnd", thread->mDeleteAtEnd);
child->addBool("createdDetached", thread->mCreateDetached);
child->addBool("hasManager", thread->mManager != NULL);
}
}
// static
VString VThread::getThreadName(VThreadID_Type threadID) {
VMutexLocker locker(&gVThreadMapMutex, "VThread::getThreadName");
VThreadIDToVThreadMap::iterator position = gVThreadIDToVThreadMap.find(threadID);
if (position == gVThreadIDToVThreadMap.end()) {
return VString::EMPTY();
}
VThread* thread = (*position).second;
VString threadName = thread->getName();
return threadName;
}
// static
void VThread::stopThread(VThreadID_Type threadID) {
VMutexLocker locker(&gVThreadMapMutex, "VThread::stopThread");
VThreadIDToVThreadMap::iterator position = gVThreadIDToVThreadMap.find(threadID);
if (position != gVThreadIDToVThreadMap.end()) {
VThread* thread = (*position).second;
thread->stop();
}
}
#ifndef VAULT_USER_STACKCRAWL_SUPPORT
// static
void VThread::logStackCrawl(const VString& headerMessage, VNamedLoggerPtr logger, bool /*verbose*/) {
if (logger == nullptr) {
VLOGGER_ERROR(VSTRING_FORMAT("%s (VThread::logStackCrawl: User stack crawl not implemented.)", headerMessage.chars()));
} else {
logger->emitStackCrawlLine(VSTRING_FORMAT("%s (VThread::logStackCrawl: User stack crawl not implemented.)", headerMessage.chars()));
}
}
#endif /* VAULT_USER_STACKCRAWL_SUPPORT */
// VMainThread ----------------------------------------------------------------
VMainThread::VMainThread()
: VThread("main", "vault.threads.VMainThread", kDontDeleteSelfAtEnd, kCreateThreadJoinable/*doesn't matter*/, NULL)
{
mThreadID = VThread::threadSelf();
_vthreadStarting(this); // Register this object for lookup by mThreadID.
}
VMainThread::~VMainThread() {
_vthreadEnded(this); // De-register this object.
}
void VMainThread::start() {
VString errorMessage("Error: invalid attempt to start VMainThread.");
VLOGGER_NAMED_FATAL(mLoggerName, errorMessage);
throw VStackTraceException(errorMessage);
}
int VMainThread::execute(int argc, char** argv) {
return VThread::userMain(argc, argv);
}
// VForeignThread ----------------------------------------------------------------
VForeignThread::VForeignThread(const VString& name)
: VThread(name, "vault.threads.VForeignThread", kDontDeleteSelfAtEnd, kCreateThreadJoinable/*doesn't matter*/, NULL)
{
mThreadID = VThread::threadSelf();
_vthreadStarting(this); // Register this object for lookup by mThreadID.
}
VForeignThread::~VForeignThread() {
_vthreadEnded(this); // De-register this object.
}
void VForeignThread::start() {
VString errorMessage("Error: invalid attempt to start VForeignThread.");
VLOGGER_NAMED_FATAL(mLoggerName, errorMessage);
throw VStackTraceException(errorMessage);
}