1 /*========================== begin_copyright_notice ============================
2
3 Copyright (C) 2017-2021 Intel Corporation
4
5 SPDX-License-Identifier: MIT
6
7 ============================= end_copyright_notice ===========================*/
8
9 #pragma once
10
11 #ifdef ISTDLIB_MT
12 #ifdef WIN32_NO_STATUS
13 #undef WIN32_NO_STATUS
14 #include <ntstatus.h>
15 #define WIN32_NO_STATUS
16 #endif
17 #endif
18
19 #ifdef _WIN32
20 # include "types.h"
21 # include <process.h>
22 # include <malloc.h>
23 #endif // _WIN32
24
25 namespace iSTD
26 {
27
28 #ifdef _WIN32
29
30 /*****************************************************************************\
31 Critical Section types
32 \*****************************************************************************/
33 #ifdef ISTDLIB_MT
34 #define DECL_CRITICAL_SECTION(x) CRITICAL_SECTION x
35 #define INIT_CRITICAL_SECTION(x) ::InitializeCriticalSection( &(x) )
36 #define DELETE_CRITICAL_SECTION(x) ::DeleteCriticalSection( &(x) )
37 #define ENTER_CRITICAL_SECTION(x) ::EnterCriticalSection( &(x) )
38 #define LEAVE_CRITICAL_SECTION(x) ::LeaveCriticalSection( &(x) )
39 #else
40 #define DECL_CRITICAL_SECTION(x)
41 #define INIT_CRITICAL_SECTION(x)
42 #define DELETE_CRITICAL_SECTION(x)
43 #define ENTER_CRITICAL_SECTION(x)
44 #define LEAVE_CRITICAL_SECTION(x)
45 #endif
46
47 /*****************************************************************************\
48 Critical Section debug
49 \*****************************************************************************/
50 // The following macros are used to allow synchronizing a function
51 // to debug multithreading issues due to re-entrancy
52 #define ENABLE_THREADED_FUNCTION_SYNC FALSE
53
54 #if defined(ISTDLIB_MT) && ENABLE_THREADED_FUNCTION_SYNC
55
56 // Structure to store data
57 struct THREADED_FUNCTION_DATA
58 {
59 // switch to enable\disable synchronizing a function to debug
60 // possible threading issues. intended to be toggled manually
61 // using the debugger. always disabled by default.
62 bool IsEnabled;
63
64 // critical section data to synchronize function
65 DECL_CRITICAL_SECTION( CS );
66
67 // default constructor to initialize data
THREADED_FUNCTION_DATATHREADED_FUNCTION_DATA68 THREADED_FUNCTION_DATA()
69 {
70 IsEnabled = false;
71 INIT_CRITICAL_SECTION( CS );
72 };
73
74 // default destructor to clean-up data
~THREADED_FUNCTION_DATATHREADED_FUNCTION_DATA75 ~THREADED_FUNCTION_DATA()
76 {
77 DELETE_CRITICAL_SECTION( CS );
78 };
79 };
80
81 // Macro for entry-point of threaded function
82 // Each function has a unique (static) enable and critical section
83 #define THREADED_FUNCTION_ENTER \
84 static iSTD::THREADED_FUNCTION_DATA sTFD; \
85 if( sTFD.IsEnabled ) \
86 { \
87 ENTER_CRITICAL_SECTION( sTFD.CS ); \
88 }
89
90 // Macro for exit-point of threaded function
91 #define THREADED_FUNCTION_EXIT \
92 if( sTFD.IsEnabled ) \
93 { \
94 LEAVE_CRITICAL_SECTION( sTFD.CS ); \
95 }
96
97 #else
98
99 #define THREADED_FUNCTION_ENTER
100 #define THREADED_FUNCTION_EXIT
101
102 #endif
103
104 /*****************************************************************************\
105 Mutex debug
106 \*****************************************************************************/
107 // The following macros are used to check that classes\functions that are not
108 // re-entrant, are never executed concurently for a single instance
109 #define ENABLE_DEBUG_MUTEX FALSE
110
111 #if defined(ISTDLIB_MT) && ENABLE_DEBUG_MUTEX
112
113 // Structure to store data needed by the debug mutex macros
114 // 1) a mutex handle to ensure only a single thread exists between Acquire and Release
115 // 2) a counter for the number of threads trying to enter
116 struct DEBUG_MUTEX_DATA
117 {
118 HANDLE Mutex;
119 (unsigned __int64*) pCounter;
120 };
121
122 // Declare the data structure
123 #define DECL_DEBUG_MUTEX(x) iSTD::DEBUG_MUTEX_DATA x
124
125 // Initialize the debug mutex data
126 // Read Access counter is x.pCounter[0]
127 // Write Access counter is x.pCounter[1]
128 #define INIT_DEBUG_MUTEX(x) \
129 { \
130 x.Mutex = ::CreateMutex( NULL, FALSE, NULL ); \
131 x.pCounter = (unsigned __int64*)::_aligned_malloc( \
132 2 * sizeof(unsigned __int64), sizeof(unsigned __int64) ); \
133 x.pCounter[0] = 0; \
134 x.pCounter[1] = 0; \
135 }
136
137 // Clean-up the debug mutex data
138 #define DELETE_DEBUG_MUTEX(x) \
139 { \
140 ::CloseHandle( x.Mutex ); \
141 ::_aligned_free( x.pCounter ); \
142 }
143
144 // Increment the counter of the number of threads entering with read access
145 // Acquire the mutex and break if another thread is waiting for write access
146 #define ACQUIRE_DEBUG_MUTEX_READ(x) \
147 { \
148 ::InterlockedIncrement( (unsigned __int64*)&( x.pCounter[0] ) ); \
149 while( WAIT_OBJECT_0 != ::WaitForSingleObject( x.Mutex, 1 ) ) \
150 if ( 0 != ::InterlockedOr( (unsigned __int64*)&( x.pCounter[1] ), 0 ) ) \
151 __debugbreak(); \
152 }
153
154 // Break if there is another thread waiting for write access
155 // Decrement read access counter
156 // Release the mutex
157 #define RELEASE_DEBUG_MUTEX_READ(x) \
158 { \
159 if( 0 != ::InterlockedOr( (unsigned __int64*)&( x.pCounter[1] ), 0 ) ) \
160 __debugbreak(); \
161 ::InterlockedDecrement( (unsigned __int64*)&( x.pCounter[0] ) ); \
162 ::ReleaseMutex( x.Mutex ); \
163 }
164
165 // Increment the counter of the number of threads entering with write access
166 // Acquire the mutex and break if another thread owns the mutex
167 #define ACQUIRE_DEBUG_MUTEX_WRITE(x) \
168 { \
169 ::InterlockedIncrement( (unsigned __int64*)&( x.pCounter[1] ) ); \
170 while( WAIT_OBJECT_0 != ::WaitForSingleObject( x.Mutex, 1 ) ) \
171 __debugbreak(); \
172 }
173
174 // Break if there is another thread waiting for read access
175 // Decrement the counter and break if there is another thread waiting for write access
176 // Release the mutex
177 #define RELEASE_DEBUG_MUTEX_WRITE(x) \
178 { \
179 if( 0 != ::InterlockedOr( (unsigned __int64*)&( x.pCounter[0] ), 0 ) ) \
180 __debugbreak(); \
181 if( 0 != ::InterlockedDecrement( (unsigned __int64*)&( x.pCounter[1] ) ) ) \
182 __debugbreak(); \
183 ::ReleaseMutex( x.Mutex ); \
184 }
185
186 #else
187 #define DECL_DEBUG_MUTEX(x)
188 #define INIT_DEBUG_MUTEX(x)
189 #define DELETE_DEBUG_MUTEX(x)
190 #define ACQUIRE_DEBUG_MUTEX_READ(x)
191 #define RELEASE_DEBUG_MUTEX_READ(x)
192 #define ACQUIRE_DEBUG_MUTEX_WRITE(x)
193 #define RELEASE_DEBUG_MUTEX_WRITE(x)
194 #endif
195
196 #ifdef ISTDLIB_MT
197
198 /*****************************************************************************\
199 Thread Management types
200 \*****************************************************************************/
201 // These are heavy-weight threads; consider using Thread-Pools instead
202 #define THREAD_ARGUMENT void*
203 typedef unsigned int (__stdcall *THREAD_FUNCTION)( THREAD_ARGUMENT );
204
205 #ifdef ISTDLIB_MT
206 #define THREAD_HANDLE HANDLE
207 #else
208 struct THREAD_DATA
209 {
210 THREAD_FUNCTION func;
211 THREAD_ARGUMENT arg;
212 };
213 #define THREAD_HANDLE THREAD_DATA*
214 #endif
215
216 /*****************************************************************************\
217 Function:
218 CreateThreads
219
220 Input:
221 const DWORD numThreads - number of threads to create
222 THREAD_FUNCTION func - pointer to function to execute
223 THREAD_ARGUMENT* args - array of per-thread arguments
224
225 Output:
226 THREAD_HANDLE* threads - array of per-thread handles
227 THREAD_HANDLE* beginEvents - array of per-thread events to signal the
228 thread to begin
229 THREAD_HANDLE* endEvents - array of per-thread events the thread sets
230 when complete
231 HRESULT
232
233 \*****************************************************************************/
CreateThreads(const DWORD numThreads,THREAD_FUNCTION func,THREAD_ARGUMENT * args,THREAD_HANDLE * threads,THREAD_HANDLE * beginEvents,THREAD_HANDLE * endEvents)234 inline HRESULT CreateThreads(
235 const DWORD numThreads,
236 THREAD_FUNCTION func,
237 THREAD_ARGUMENT* args,
238 THREAD_HANDLE* threads,
239 THREAD_HANDLE* beginEvents,
240 THREAD_HANDLE* endEvents )
241 {
242 HRESULT hr = S_OK;
243
244 for( DWORD i = 0; i < numThreads; ++i )
245 {
246 #ifdef ISTDLIB_MT
247
248 beginEvents[i] = ::CreateEvent( NULL, FALSE, FALSE, NULL );
249 endEvents[i] = ::CreateEvent( NULL, FALSE, FALSE, NULL );
250
251 threads[i] = ( THREAD_HANDLE )::_beginthreadex(
252 NULL,
253 0,
254 func,
255 args[i],
256 CREATE_SUSPENDED,
257 NULL );
258
259 if( threads[i] )
260 {
261 ::ResumeThread( threads[i] );
262 }
263 else
264 {
265 hr = E_FAIL;
266 }
267
268 #else
269
270 threads[i] = (THREAD_DATA*)malloc( sizeof(THREAD_DATA) );
271
272 if( threads[i] )
273 {
274 threads[i]->func = func;
275 threads[i]->arg = args[i];
276
277 beginEvents[i] = threads[i];
278 endEvents[i] = NULL;
279 }
280 else
281 {
282 hr = E_FAIL;
283 }
284
285 #endif
286 }
287
288 return hr;
289 }
290
291 /*****************************************************************************\
292 StartThreads
293 \*****************************************************************************/
StartThreads(const DWORD numThreads,THREAD_HANDLE * beginEvents)294 inline void StartThreads(
295 const DWORD numThreads,
296 THREAD_HANDLE* beginEvents )
297 {
298 for( DWORD i = 0; i < numThreads; ++i )
299 {
300 #ifdef ISTDLIB_MT
301
302 ::SetEvent( beginEvents[i] );
303
304 #else
305
306 beginEvents[i]->func( beginEvents[i]->arg );
307
308 #endif
309 }
310 }
311
312 /*****************************************************************************\
313 WaitForThreads
314 \*****************************************************************************/
WaitForThreads(const DWORD numThreads,THREAD_HANDLE * endEvents)315 inline void WaitForThreads(
316 const DWORD numThreads,
317 THREAD_HANDLE* endEvents )
318 {
319 #ifdef ISTDLIB_MT
320
321 #ifdef _DEBUG
322
323 // deadlock detection
324 DWORD result = 0;
325 do
326 {
327 result = ::WaitForMultipleObjects(
328 numThreads,
329 endEvents,
330 TRUE,
331 1000 );
332 result &= 0xfffffff0;
333 ASSERT( WAIT_OBJECT_0 == result );
334 }
335 while( WAIT_OBJECT_0 != result );
336
337 #else
338
339 ::WaitForMultipleObjects(
340 numThreads,
341 endEvents,
342 TRUE,
343 INFINITE );
344
345 #endif
346
347 #endif
348 }
349
350 /*****************************************************************************\
351 DeleteThreads
352 \*****************************************************************************/
DeleteThreads(const DWORD numThreads,THREAD_HANDLE * threads,THREAD_HANDLE * beginEvents,THREAD_HANDLE * endEvents)353 inline void DeleteThreads(
354 const DWORD numThreads,
355 THREAD_HANDLE* threads,
356 THREAD_HANDLE* beginEvents,
357 THREAD_HANDLE* endEvents )
358 {
359 for( DWORD i = 0; i < numThreads; ++i )
360 {
361 #ifdef ISTDLIB_MT
362
363 ::CloseHandle( threads[i] );
364 ::CloseHandle( endEvents[i] );
365 ::CloseHandle( beginEvents[i] );
366
367 #else
368
369 free( threads[i] );
370 threads[i] = NULL;
371 endEvents[i] = NULL;
372 beginEvents[i] = NULL;
373
374 #endif
375 }
376 }
377
378 /*****************************************************************************\
379 Thread-Pool Management types
380 \*****************************************************************************/
381 #if defined(ISTDLIB_MT) && (_WIN32_WINNT >= 0x0600)
382 // Minimum supported client: Windows Vista (_WIN32_WINNT_VISTA = 0x0600)
383
384 /*****************************************************************************\
385
386 Function:
387 CreateThreadPool
388
389 Description:
390 Creates a thread pool
391
392 Input:
393 PTP_CALLBACK_ENVIRON ptrPoolEnv - pointer to storage where thread pool
394 environment will be stored
395 DWORD minThreads - minimum number of requested threads in the thread pool.
396 DWORD maxThreads - maximum number of requested threads in the thread pool.
397
398 Output:
399 BOOL - Success or Fail
400
401 Notes:
402 Client is responsible for management of PTP_CALLBACK_ENVIRON memory
403
404 \*****************************************************************************/
CreateThreadPool(PTP_CALLBACK_ENVIRON ptrPoolEnv,const DWORD minThreads,const DWORD maxThreads)405 inline BOOL CreateThreadPool(
406 PTP_CALLBACK_ENVIRON ptrPoolEnv,
407 const DWORD minThreads,
408 const DWORD maxThreads )
409 {
410 if( ptrPoolEnv )
411 {
412 InitializeThreadpoolEnvironment( ptrPoolEnv );
413
414 PTP_POOL ptrPool = CreateThreadpool( NULL );
415 if( ptrPool )
416 {
417 SetThreadpoolThreadMinimum( ptrPool, minThreads );
418 SetThreadpoolThreadMaximum( ptrPool, maxThreads );
419
420 SetThreadpoolCallbackRunsLong( ptrPoolEnv );
421 SetThreadpoolCallbackPool( ptrPoolEnv, ptrPool );
422 return TRUE;
423 }
424 }
425 return FALSE;
426 }
427
428 /*****************************************************************************\
429
430 Function:
431 CreateThreadPool
432
433 Description:
434 Creates a thread pool
435
436 Input:
437 PTP_CALLBACK_ENVIRON ptrPoolEnv - pointer to storage where thread pool
438 environment will be stored
439
440 Output:
441 BOOL - Success or Fail
442
443 Notes:
444 Client is responsible for management of PTP_CALLBACK_ENVIRON memory
445
446 \*****************************************************************************/
CreateThreadPool(PTP_CALLBACK_ENVIRON ptrPoolEnv)447 inline BOOL CreateThreadPool(
448 PTP_CALLBACK_ENVIRON ptrPoolEnv )
449 {
450 SYSTEM_INFO si = { 0 };
451 ::GetSystemInfo( &si );
452 return CreateThreadPool( ptrPoolEnv, si.dwNumberOfProcessors, si.dwNumberOfProcessors );
453 }
454
455 /*****************************************************************************\
456
457 Function:
458 DeleteThreadPool
459
460 Description:
461 Deletes a thread pool
462
463 Input:
464 PTP_CALLBACK_ENVIRON ptrPoolEnv - pointer to storage where thread pool
465 environment is stored
466
467 Output:
468 none
469
470 Notes:
471 Client is responsible for management of PTP_CALLBACK_ENVIRON memory
472
473 \*****************************************************************************/
DeleteThreadPool(PTP_CALLBACK_ENVIRON ptrPoolEnv)474 inline void DeleteThreadPool(
475 PTP_CALLBACK_ENVIRON ptrPoolEnv )
476 {
477 if( ptrPoolEnv )
478 {
479 if( ptrPoolEnv->Pool )
480 {
481 CloseThreadpool( ptrPoolEnv->Pool );
482 }
483 }
484 }
485
486 /*****************************************************************************\
487
488 Function:
489 CreateThreadPoolWork
490
491 Description:
492 Creates a thread pool work item
493
494 Input:
495 PTP_WORK_CALLBACK pWorkFunc - pointer to function to add to thread pool
496 PVOID pWorkData - optional data to pass to function
497 PTP_CALLBACK_ENVIRON ptrPoolEnv - thread pool environment
498
499 Output:
500 PTP_WORK - pointer to work item
501
502 \*****************************************************************************/
CreateThreadPoolWork(PTP_WORK_CALLBACK pWorkFunc,PVOID pWorkData,PTP_CALLBACK_ENVIRON ptrPoolEnv)503 inline PTP_WORK CreateThreadPoolWork(
504 PTP_WORK_CALLBACK pWorkFunc,
505 PVOID pWorkData,
506 PTP_CALLBACK_ENVIRON ptrPoolEnv )
507 {
508 return CreateThreadpoolWork( pWorkFunc, pWorkData, ptrPoolEnv );
509 }
510
511 /*****************************************************************************\
512
513 Function:
514 SubmitThreadPoolWork
515
516 Description:
517 Submits a thread pool work item to the thread pool
518
519 Input:
520 PTP_WORK - pointer to work item
521
522 Output:
523 none
524
525 \*****************************************************************************/
SubmitThreadPoolWork(PTP_WORK pWorkItem)526 inline void SubmitThreadPoolWork(
527 PTP_WORK pWorkItem )
528 {
529 if( pWorkItem )
530 {
531 SubmitThreadpoolWork( pWorkItem );
532 }
533 }
534
535 /*****************************************************************************\
536
537 Function:
538 WaitForThreadPoolWork
539
540 Description:
541 Waits for the thread pool work item to complete
542
543 Input:
544 PTP_WORK ptrWork - pointer to work item
545
546 Output:
547 none
548
549 \*****************************************************************************/
WaitForThreadPoolWork(PTP_WORK ptrWork)550 inline void WaitForThreadPoolWork(
551 PTP_WORK ptrWork )
552 {
553 if( ptrWork )
554 {
555 WaitForThreadpoolWorkCallbacks( ptrWork, FALSE );
556 }
557 }
558
559 /*****************************************************************************\
560
561 Function:
562 DeleteThreadPoolWork
563
564 Description:
565 Deletes the thread pool work item to complete
566
567 Input:
568 PTP_WORK ptrWork - pointer to work item
569
570 Output:
571 none
572
573 \*****************************************************************************/
DeleteThreadPoolWork(PTP_WORK & ptrWork)574 inline void DeleteThreadPoolWork(
575 PTP_WORK& ptrWork )
576 {
577 if( ptrWork )
578 {
579 CloseThreadpoolWork( ptrWork );
580 ptrWork = NULL;
581 }
582 }
583
584 #endif //defined(ISTDLIB_MT) && (_WIN32_WINNT >= 0x0600)
585
586 #endif //ISTDLIB_MT
587
588 #else // _WIN32
589
590 #define THREADED_FUNCTION_ENTER
591 #define THREADED_FUNCTION_EXIT
592
593 #define DECL_CRITICAL_SECTION(x)
594 #define INIT_CRITICAL_SECTION(x)
595 #define DELETE_CRITICAL_SECTION(x)
596 #define ENTER_CRITICAL_SECTION(x)
597 #define LEAVE_CRITICAL_SECTION(x)
598
599 #define DECL_DEBUG_MUTEX(x)
600 #define INIT_DEBUG_MUTEX(x)
601 #define DELETE_DEBUG_MUTEX(x)
602 #define ACQUIRE_DEBUG_MUTEX_READ(x)
603 #define RELEASE_DEBUG_MUTEX_READ(x)
604 #define ACQUIRE_DEBUG_MUTEX_WRITE(x)
605 #define RELEASE_DEBUG_MUTEX_WRITE(x)
606
607 #endif // _WIN32
608
609 } // iSTD
610