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