-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy paththread.cpp
190 lines (164 loc) · 5.73 KB
/
thread.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
179
180
181
182
183
184
185
186
187
188
189
190
#include "thread.h"
#include "os.h"
#include "profiler.h"
#include <time.h>
static SigAction old_handler;
pthread_key_t ProfiledThread::_tls_key;
int ProfiledThread::_buffer_size = 0;
std::atomic<int> ProfiledThread::_running_buffer_pos(0);
std::vector<ProfiledThread *> ProfiledThread::_buffer;
void ProfiledThread::initTLSKey() {
static pthread_once_t tls_initialized = PTHREAD_ONCE_INIT;
pthread_once(&tls_initialized, doInitTLSKey);
}
void ProfiledThread::doInitTLSKey() { pthread_key_create(&_tls_key, freeKey); }
inline void ProfiledThread::freeKey(void *key) {
ProfiledThread *tls_ref = (ProfiledThread *)(key);
if (tls_ref != NULL) {
delete tls_ref;
}
}
void ProfiledThread::initCurrentThread() {
initTLSKey();
ProfiledThread *tls = (ProfiledThread *)pthread_getspecific(_tls_key);
if (tls == NULL) {
int tid = OS::threadId();
tls = ProfiledThread::forTid(tid);
pthread_setspecific(_tls_key, (const void *)tls);
}
}
void ProfiledThread::initExistingThreads() {
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
pthread_once(&initialized, doInitExistingThreads);
}
// The lifetime of this vector requires stronger guarantees.
// We need to ensure that the vector is not removed at the end of the process
// while threads are accessing it. This is to silence the sanitizer but should
// not be considered as a fix
__attribute__((no_sanitize("thread"))) void
ProfiledThread::initCurrentThreadWithBuffer() {
initTLSKey();
if (pthread_getspecific(_tls_key) != NULL) {
// if there is already a TLS value associated just bail out
return;
}
ProfiledThread *tls_ref = NULL;
int pos = _running_buffer_pos++;
if (pos < _buffer_size) {
tls_ref = _buffer[pos];
tls_ref->_tid = OS::threadId();
}
if (tls_ref != NULL) {
pthread_setspecific(_tls_key, (const void *)tls_ref);
} else {
const char *msg = "ProfiledThread TLS buffer too small.";
Profiler::instance()->writeLog(LOG_WARN, msg, strlen(msg));
}
}
void *ProfiledThread::delayedUninstallUSR1(void *unused) {
initTLSKey();
int res = 0;
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 1000000;
// wait for the TLS to be set
while ((!res || errno == EINTR) && pthread_getspecific(_tls_key) == NULL) {
res = nanosleep(&ts, &ts);
}
/*
Wait 5 secs to finish other threads initialization - should be more than
enough. This is the best we can do - we can not use any synchronization
between the signal handler running in threads to eg. have a countdown latch
and uninstall only when all threads completed the init. In addition to that
the threads from the initial list can be terminated by the time they should
process the signal and we would need to synchronize with the thread-end event
captured by JVMTI, otherwise we might be waiting forever.
A fixed timeout approach sounds like an acceptable compromise - in the worst
case there will be a few outliers without the TLS instance associated with
them and they will have to take the slow path to resolve the thread id and
will not be able to use anything depending on data stored in that TLS
instance.
In real life, though, the 5 secs timeout should be more than enough.
*/
ts.tv_sec = 5;
ts.tv_nsec = 0;
do {
res = nanosleep(&ts, &ts);
} while (res == -1 && errno == EINTR);
// now remove the TLS init signal handler
OS::installSignalHandler(SIGUSR1, old_handler);
return NULL;
}
void ProfiledThread::doInitExistingThreads() {
pthread_t thrd;
if (pthread_create(&thrd, NULL, delayedUninstallUSR1, NULL) == 0) {
std::unique_ptr<ThreadList> tlist{OS::listThreads()};
/*
Here is a bit of trickery - we need the TLS variable initialized for all
existing threads but we can not do this from outside. Therefore, we need to
install signal handler to perform the initialization. However, the signal
handler can not allocate - in order to work around that limitation we
pre-allocate an array of ProfiledThread instances for all existing threads.
This array will be used by the threads when handling the signal to pick the
pre-allocated instance which can be stored in the TLS slot.
Any newly started threads will be handled by the JVMTI callback so we need
to worry only about the existing threads here.
*/
prepareBuffer(tlist->size());
old_handler =
OS::installSignalHandler(SIGUSR1, ProfiledThread::signalHandler);
int cntr = 0;
int tid = -1;
while ((tid = tlist->next()) != -1) {
if (tlist->size() <= cntr++) {
break;
}
OS::sendSignalToThread(tid, SIGUSR1);
}
pthread_detach(thrd);
}
}
void ProfiledThread::prepareBuffer(int size) {
Log::debug("Initializing ProfiledThread TLS buffer to %d slots", size);
_running_buffer_pos = 0;
_buffer_size = size;
_buffer.reserve(size);
for (int i = 0; i < size; i++) {
_buffer.push_back(ProfiledThread::inBuffer(i));
}
}
void ProfiledThread::release() {
pthread_key_t key = _tls_key;
if (key == 0) {
return;
}
ProfiledThread *tls = (ProfiledThread *)pthread_getspecific(key);
if (tls != NULL) {
tls->releaseFromBuffer();
delete tls;
pthread_setspecific(key, NULL);
}
}
void ProfiledThread::releaseFromBuffer() {
if (_buffer_pos >= 0) {
_buffer[_buffer_pos] = NULL;
_buffer_pos = -1;
}
}
int ProfiledThread::currentTid() {
ProfiledThread *tls = current();
if (tls != NULL) {
return tls->tid();
}
return OS::threadId();
}
ProfiledThread *ProfiledThread::current() {
pthread_key_t key = _tls_key;
return key != 0 ? (ProfiledThread *)pthread_getspecific(key) : NULL;
}
void ProfiledThread::signalHandler(int signo, siginfo_t *siginfo,
void *ucontext) {
if (signo == SIGUSR1) {
initCurrentThreadWithBuffer();
}
}