-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththreads_kernel_shared.cpp
More file actions
329 lines (268 loc) · 10.7 KB
/
Copy paththreads_kernel_shared.cpp
File metadata and controls
329 lines (268 loc) · 10.7 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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREADS_KERNEL_SHARED_CPp_
#define DLIB_THREADS_KERNEL_SHARED_CPp_
#include "threads_kernel_shared.h"
#include "../assert.h"
#include "../platform.h"
#include <iostream>
// The point of this block of code is to cause a link time error that will prevent a user
// from compiling part of their application with DLIB_ASSERT enabled and part with them
// disabled since doing that would be a violation of C++'s one definition rule.
extern "C"
{
#ifdef ENABLE_ASSERTS
int USER_ERROR__missing_dlib_all_source_cpp_file__OR__inconsistent_use_of_DEBUG_or_ENABLE_ASSERTS_preprocessor_directives;
#else
int USER_ERROR__missing_dlib_all_source_cpp_file__OR__inconsistent_use_of_DEBUG_or_ENABLE_ASSERTS_preprocessor_directives_;
#endif
}
#ifndef DLIB_THREAD_POOL_TIMEOUT
// default to 30000 milliseconds
#define DLIB_THREAD_POOL_TIMEOUT 30000
#endif
namespace dlib
{
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// threader functions
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
namespace threads_kernel_shared
{
bool thread_pool_has_been_destroyed = false;
// ----------------------------------------------------------------------------------------
struct threader_destruct_helper
{
// cause the thread pool to begin its destruction process when
// global objects start to be destroyed
~threader_destruct_helper()
{
thread_pool().destruct_if_ready();
}
};
// ----------------------------------------------------------------------------------------
threader& thread_pool (
)
{
static threader* thread_pool = new threader;
static threader_destruct_helper a;
return *thread_pool;
}
// ----------------------------------------------------------------------------------------
bool threader::
is_dlib_thread (
thread_id_type id
)
{
auto_mutex M(data_mutex);
return thread_ids.is_member(id);
}
// ----------------------------------------------------------------------------------------
threader::
threader (
) :
total_count(0),
function_pointer(0),
pool_count(0),
data_ready(data_mutex),
data_empty(data_mutex),
destruct(false),
destructed(data_mutex),
do_not_ever_destruct(false)
{
#ifdef WIN32
// Trying to destroy the global thread pool when we are part of a DLL and the
// DLL is being unloaded can sometimes lead to weird behavior. For example, in
// the python interpreter you will get the interpreter to hang. Or if we are
// part of a MATLAB mex file and the file is being unloaded there can also be
// similar weird issues. So when we are using dlib on windows we just disable
// the destruction of the global thread pool since it doesn't matter anyway.
// It's resources will just get freed by the OS. This is even the recommended
// thing to do by Microsoft (http://blogs.msdn.com/b/oldnewthing/archive/2012/01/05/10253268.aspx).
//
// As an aside, it's worth pointing out that the reason we try and free
// resources on program shutdown on other operating systems is so we can have
// clean reports from tools like valgrind which check for memory leaks. But
// trying to do this on windows is a lost cause so we give up in this case and
// follow the Microsoft recommendation.
do_not_ever_destruct = true;
#endif // WIN32
}
// ----------------------------------------------------------------------------------------
threader::
~threader (
)
{
data_mutex.lock();
destruct = true;
data_ready.broadcast();
// wait for all the threads to end
while (total_count > 0)
destructed.wait();
thread_pool_has_been_destroyed = true;
data_mutex.unlock();
}
// ----------------------------------------------------------------------------------------
void threader::
destruct_if_ready (
)
{
if (do_not_ever_destruct)
return;
data_mutex.lock();
// if there aren't any active threads, just maybe some sitting around
// in the pool then just destroy the threader
if (total_count == pool_count)
{
destruct = true;
data_ready.broadcast();
data_mutex.unlock();
delete this;
}
else
{
// There are still some user threads running so there isn't
// much we can really do. Just let the program end without
// cleaning up threading resources.
data_mutex.unlock();
}
}
// ----------------------------------------------------------------------------------------
void threader::
call_end_handlers (
)
{
reg.m.lock();
const thread_id_type id = get_thread_id();
thread_id_type id_copy;
member_function_pointer<> mfp;
// Remove all the member function pointers for this thread from the tree
// and call them.
while (reg.reg[id] != 0)
{
reg.reg.remove(id,id_copy,mfp);
reg.m.unlock();
mfp();
reg.m.lock();
}
reg.m.unlock();
}
// ------------------------------------------------------------------------------------
bool threader::
create_new_thread (
void (*funct)(void*),
void* param
)
{
// get a lock on the data mutex
auto_mutex M(data_mutex);
// loop to ensure that the new function pointer is in the data
while (true)
{
// if the data is empty then add new data and quit loop
if (function_pointer == 0)
{
parameter = param;
function_pointer = funct;
break;
}
else
{
// wait for data to become empty
data_empty.wait();
}
}
// get a thread for this new data
// if a new thread must be created
if (pool_count == 0)
{
// make thread and add it to the pool
if ( threads_kernel_shared_helpers::spawn_thread(thread_starter, this) == false )
{
function_pointer = 0;
parameter = 0;
data_empty.signal();
return false;
}
++total_count;
}
// wake up a thread from the pool
else
{
data_ready.signal();
}
return true;
}
// ------------------------------------------------------------------------------------
void thread_starter (
void* object
)
{
// get a reference to the calling threader object
threader& self = *static_cast<threader*>(object);
{
auto_mutex M(self.data_mutex);
// add this thread id
thread_id_type thread_id = get_thread_id();
self.thread_ids.add(thread_id);
// indicate that this thread is now in the thread pool
++self.pool_count;
while (self.destruct == false)
{
// if data is ready then process it and launch the thread
// if its not ready then go back into the pool
while (self.function_pointer != 0)
{
// indicate that this thread is now out of the thread pool
--self.pool_count;
// get the data for the function call
void (*funct)(void*) = self.function_pointer;
void* param = self.parameter;
self.function_pointer = 0;
// signal that the data is now empty
self.data_empty.signal();
self.data_mutex.unlock();
// Call funct with its intended parameter. If this function throws then
// we intentionally let the exception escape the thread and result in whatever
// happens when it gets caught by the OS (generally the program is terminated).
funct(param);
self.call_end_handlers();
self.data_mutex.lock();
// indicate that this thread is now back in the thread pool
++self.pool_count;
}
if (self.destruct == true)
break;
// if we timed out and there isn't any work to do then
// this thread will quit this loop and end.
if (self.data_ready.wait_or_timeout(DLIB_THREAD_POOL_TIMEOUT) == false &&
self.function_pointer == 0)
break;
}
// remove this thread id from thread_ids
thread_id = get_thread_id();
self.thread_ids.destroy(thread_id);
// indicate that this thread is now out of the thread pool
--self.pool_count;
--self.total_count;
self.destructed.signal();
} // end of auto_mutex M(self.data_mutex) block
}
// ------------------------------------------------------------------------------------
}
// ----------------------------------------------------------------------------------------
bool is_dlib_thread (
thread_id_type id
)
{
return threads_kernel_shared::thread_pool().is_dlib_thread(id);
}
bool is_dlib_thread (
)
{
return is_dlib_thread(get_thread_id());
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_THREADS_KERNEL_SHARED_CPp_