/
BasicThread.cpp
178 lines (149 loc) · 5.71 KB
/
BasicThread.cpp
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
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#include "stdafx.h"
#include "BasicThread.h"
#include "Autowired.h"
#include "BasicThreadStateBlock.h"
#include "ContextEnumerator.h"
#include "fast_pointer_cast.h"
#include ATOMIC_HEADER
BasicThread::BasicThread(const char* pName):
ContextMember(pName),
m_state(std::make_shared<BasicThreadStateBlock>()),
m_stop(false),
m_running(false),
m_completed(false),
m_priority(ThreadPriority::Default)
{}
BasicThread::~BasicThread(void){}
std::mutex& BasicThread::GetLock(void) {
return m_state->m_lock;
}
void BasicThread::DoRun(std::shared_ptr<CoreObject>&& refTracker) {
assert(m_running);
// Make our own session current before we do anything else:
CurrentContextPusher pusher(GetContext());
// Set the thread name no matter what:
if(GetName())
SetCurrentThreadName();
// Now we wait for the thread to be good to go:
try {
Run();
}
catch(dispatch_aborted_exception&) {
// Okay, this is fine, a dispatcher is terminating--this is a normal way to
// end a dispatcher loop.
}
catch(...) {
try {
// Ask that the enclosing context filter this exception, if possible:
GetContext()->FilterException();
}
catch(...) {
// Generic exception, unhandled, we can't do anything about this
}
// Signal shutdown on the enclosing context--cannot wait, if we wait we WILL deadlock
GetContext()->SignalShutdown(false);
}
// Run loop is over, time to clean up
DoRunLoopCleanup(pusher.Pop(), std::move(refTracker));
}
void BasicThread::DoRunLoopCleanup(std::shared_ptr<CoreContext>&& ctxt, std::shared_ptr<CoreObject>&& refTracker) {
// Take a copy of our state condition shared pointer while we still hold a reference to
// ourselves. This is the only member out of our collection of members that we actually
// need to hold a reference to.
auto state = m_state;
// Perform a manual notification of teardown listeners
NotifyTeardownListeners();
// Transition to stopped state. Synchronization not required, transitions are all one-way
m_stop = true;
m_running = false;
// Need to ensure that "stop" and "running" are actually updated in memory before we mark "complete"
#if autowiring_USE_LIBCXX
std::atomic_thread_fence(std::memory_order_release);
#else
(std::lock_guard<std::mutex>)state->m_lock;
#endif
m_completed = true;
// Tell our CoreRunnable parent that we're done to ensure that our reference count will be cleared.
Stop(false);
// Release our hold on the context. After this point, we have to be VERY CAREFUL that we
// don't try to refer to any of our own member variables, because our own object may have
// already gone out of scope. [this] is potentially dangling.
ctxt.reset();
// Clear our reference tracker, which will notify anyone who is asleep and also maybe
// will destroy the entire underlying context.
refTracker.reset();
// MUST detach here. By this point in the application, it's possible that `this` has already been
// deleted. If that's the case, `state.unique()` is true, and when we go out of scope, the destructor
// for m_thisThread will be invoked. If that happens, the destructor will block for the held thread
// to quit--and, in this case, the thread which is being held is actually us. Blocking on it, in that
// case, would be a trivial deadlock. So, because we're about to quit anyway, we simply detach the
// thread and prepare for final teardown operations.
state->m_thisThread.detach();
// Notify other threads that we are done. At this point, any held references that might still exist
// notification must happen from a synchronized level in order to ensure proper ordering.
std::lock_guard<std::mutex> lk(state->m_lock);
state->m_stateCondition.notify_all();
}
void BasicThread::WaitForStateUpdate(const std::function<bool()>& fn) {
std::unique_lock<std::mutex> lk(m_state->m_lock);
m_state->m_stateCondition.wait(
lk,
[&fn, this] {
return fn() || ShouldStop();
}
);
if(ShouldStop())
throw dispatch_aborted_exception();
}
void BasicThread::PerformStatusUpdate(const std::function<void()>& fn) {
std::unique_lock<std::mutex> lk(m_state->m_lock);
fn();
m_state->m_stateCondition.notify_all();
}
bool BasicThread::ThreadSleep(std::chrono::nanoseconds timeout) {
std::unique_lock<std::mutex> lk(m_state->m_lock);
return m_state->m_stateCondition.wait_for(lk, timeout, [this] { return ShouldStop(); });
}
bool BasicThread::OnStart(void) {
std::shared_ptr<CoreContext> context = m_context.lock();
if(!context)
return false;
// Currently running:
m_running = true;
// Place the new thread entity directly in the space where it goes to avoid
// any kind of races arising from asynchronous access to this space
auto outstanding = GetOutstanding();
m_state->m_thisThread.~thread();
new (&m_state->m_thisThread) std::thread(
[this, outstanding] () mutable {
this->DoRun(std::move(outstanding));
}
);
return true;
}
void BasicThread::OnStop(bool graceful) {
// If we were never started, we need to set our completed flag to true
if (!m_running) {
m_completed = true;
}
// Always invoke stop handler:
OnStop();
}
void BasicThread::DoAdditionalWait(void) {
// Wait for the run loop cleanup to happen in DoRunLoopCleanup
std::unique_lock<std::mutex> lk(m_state->m_lock);
m_state->m_stateCondition.wait(
lk,
[this] {return this->m_completed; }
);
}
void BasicThread::ForceCoreThreadReidentify(void) {
for(const auto& ctxt : ContextEnumerator(AutoGlobalContext())) {
for(const auto& thread : ctxt->CopyBasicThreadList())
thread->SetCurrentThreadName();
}
}
void ForceCoreThreadReidentify(void) {
BasicThread::ForceCoreThreadReidentify();
}