1 /*************************************************************************
3 * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. *
4 * All rights reserved. Email: russ@q12.org Web: www.q12.org *
6 * Threading Windows thread pool implementation file. *
7 * Copyright (C) 2011-2024 Oleh Derevenko. All rights reserved. *
8 * e-mail: odar@eleks.com (change all "a" to "e") *
10 * This library is free software; you can redistribute it and/or *
11 * modify it under the terms of EITHER: *
12 * (1) The GNU Lesser General Public License as published by the Free *
13 * Software Foundation; either version 2.1 of the License, or (at *
14 * your option) any later version. The text of the GNU Lesser *
15 * General Public License is included with this library in the *
17 * (2) The BSD-style license that is included with this library in *
18 * the file LICENSE-BSD.TXT. *
20 * This library is distributed in the hope that it will be useful, *
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
23 * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
25 *************************************************************************/
28 * Windows thread pool implementation for built-in threading support provider.
34 #include <ode/odeconfig.h>
35 #include <ode/error.h>
36 #include <ode/threading_impl.h>
37 #include <ode/odeinit.h>
40 #include "threading_impl_templates.h"
43 #if dBUILTIN_THREADING_IMPL_ENABLED
50 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
53 #if dBUILTIN_THREADING_IMPL_ENABLED
55 #define THREAD_STACK_MAX ((sizeint)(UINT_MAX - 1)) // The absolute maximum would be UINT_MAX but let it be a little bit less to avoid "Comparison is always false" warnings. ;)
61 dxEventObject(): m_event_handle(NULL
) {}
62 ~dxEventObject() { FinalizeObject(); }
64 bool InitializeObject(bool manual_reset
, bool initial_state
);
65 void FinalizeObject();
67 bool WaitInfinitely() { return ::WaitForSingleObject(m_event_handle
, INFINITE
) == WAIT_OBJECT_0
; }
72 HANDLE m_event_handle
;
75 bool dxEventObject::InitializeObject(bool manual_reset
, bool initial_state
)
77 dIASSERT(m_event_handle
== NULL
);
83 HANDLE event_handle
= ::CreateEvent(NULL
, manual_reset
, initial_state
, NULL
);
84 if (event_handle
== NULL
)
89 m_event_handle
= event_handle
;
97 void dxEventObject::FinalizeObject()
99 HANDLE event_handle
= m_event_handle
;
100 if (event_handle
!= NULL
)
102 BOOL close_result
= ::CloseHandle(event_handle
);
103 dICHECK(close_result
); // Object destruction should always succeed
105 m_event_handle
= NULL
;
109 void dxEventObject::SetEvent()
111 BOOL set_result
= ::SetEvent(m_event_handle
);
115 void dxEventObject::ResetEvent()
117 BOOL reset_result
= ::ResetEvent(m_event_handle
);
118 dICHECK(reset_result
);
123 struct dxThreadPoolThreadInfo
126 dxThreadPoolThreadInfo();
127 ~dxThreadPoolThreadInfo();
129 bool Initialize(sizeint stack_size
, unsigned int ode_data_allocate_flags
);
132 bool WaitInitStatus();
136 void WaitAndCloseThreadHandle(HANDLE thread_handle
);
141 dxTHREAD_COMMAND_EXIT
,
142 dxTHREAD_COMMAND_NOOP
,
143 dxTHREAD_COMMAND_SERVE_IMPLEMENTATION
,
146 struct dxServeImplementationParams
148 dxServeImplementationParams(dThreadingImplementationID impl
, dxEventObject
*ready_wait_event
):
149 m_impl(impl
), m_ready_wait_event(ready_wait_event
)
153 dThreadingImplementationID m_impl
;
154 dxEventObject
*m_ready_wait_event
;
157 void ExecuteThreadCommand(dxTHREADCOMMAND command
, void *param
, bool wait_response
);
160 static unsigned CALLBACK
ThreadProcedure_Callback(void *thread_param
);
161 void ThreadProcedure();
162 void ReportInitStatus(bool init_result
);
163 void RunCommandHandlingLoop();
165 void ThreadedServeImplementation(dThreadingImplementationID impl
, dxEventObject
*ready_wait_event
);
166 static void ProcessThreadServeReadiness_Callback(void *context
);
169 HANDLE m_thread_handle
;
171 unsigned int m_ode_data_allocate_flags
;
172 dxTHREADCOMMAND m_command_code
;
173 dxEventObject m_command_event
;
174 dxEventObject m_acknowledgement_event
;
175 void *m_command_param
;
179 dxThreadPoolThreadInfo::dxThreadPoolThreadInfo():
180 m_thread_handle(NULL
),
181 m_ode_data_allocate_flags(0),
182 m_command_code(dxTHREAD_COMMAND_EXIT
),
184 m_acknowledgement_event(),
185 m_command_param(NULL
)
189 dxThreadPoolThreadInfo::~dxThreadPoolThreadInfo()
195 bool dxThreadPoolThreadInfo::Initialize(sizeint stack_size
, unsigned int ode_data_allocate_flags
)
199 bool command_event_allocated
= false, acknowledgement_event_allocated
= false;
201 HANDLE thread_handle
= NULL
;
205 if (stack_size
> THREAD_STACK_MAX
)
207 SetLastError(ERROR_INVALID_PARAMETER
);
211 if (!m_command_event
.InitializeObject(false, false))
216 command_event_allocated
= true;
218 if (!m_acknowledgement_event
.InitializeObject(true, false))
223 acknowledgement_event_allocated
= true;
225 m_ode_data_allocate_flags
= ode_data_allocate_flags
;
227 thread_handle
= (HANDLE
)_beginthreadex(NULL
, (unsigned)stack_size
, &ThreadProcedure_Callback
, (void *)this, 0, NULL
);
228 if (thread_handle
== NULL
) // Not a bug!!! _beginthreadex() returns NULL on failure
233 // It is OK to alter priority for thread without creating it in suspended state as
234 // it is anyway going to be waited for (waited for its init result) and
235 // will not be issues commands until after that.
236 int own_priority
= GetThreadPriority(GetCurrentThread());
237 if (own_priority
!= THREAD_PRIORITY_ERROR_RETURN
)
239 if (!SetThreadPriority(thread_handle
, own_priority
))
241 // own_priority = THREAD_PRIORITY_ERROR_RETURN; -- Well, if priority inheritance fails - just ignore it :-/
245 bool thread_init_result
= WaitInitStatus();
246 if (!thread_init_result
)
248 DWORD error_save
= GetLastError();
249 WaitAndCloseThreadHandle(thread_handle
);
250 SetLastError(error_save
);
254 m_thread_handle
= thread_handle
;
261 if (command_event_allocated
)
263 if (acknowledgement_event_allocated
)
265 m_acknowledgement_event
.FinalizeObject();
268 m_command_event
.FinalizeObject();
275 bool dxThreadPoolThreadInfo::WaitInitStatus()
277 bool acknowledgement_wait_result
= m_acknowledgement_event
.WaitInfinitely();
278 dICHECK(acknowledgement_wait_result
);
280 DWORD error_code
= (DWORD
)(sizeint
)m_command_param
;
282 bool init_status
= error_code
== ERROR_SUCCESS
? true : (SetLastError(error_code
), false);
286 void dxThreadPoolThreadInfo::Finalize()
288 HANDLE thread_handle
= m_thread_handle
;
289 if (thread_handle
!= NULL
)
291 ExecuteThreadCommand(dxTHREAD_COMMAND_EXIT
, NULL
, false);
293 WaitAndCloseThreadHandle(thread_handle
);
294 m_thread_handle
= NULL
;
296 m_command_event
.FinalizeObject();
297 m_acknowledgement_event
.FinalizeObject();
301 void dxThreadPoolThreadInfo::WaitAndCloseThreadHandle(HANDLE thread_handle
)
303 DWORD thread_wait_result
= WaitForSingleObject(thread_handle
, INFINITE
);
304 dICHECK(thread_wait_result
== WAIT_OBJECT_0
);
306 BOOL thread_close_result
= CloseHandle(thread_handle
);
307 dIVERIFY(thread_close_result
);
311 void dxThreadPoolThreadInfo::ExecuteThreadCommand(dxTHREADCOMMAND command
, void *param
, bool wait_response
)
313 bool acknowledgement_wait_result
= m_acknowledgement_event
.WaitInfinitely();
314 dICHECK(acknowledgement_wait_result
);
316 m_acknowledgement_event
.ResetEvent();
318 m_command_code
= command
;
319 m_command_param
= param
;
321 m_command_event
.SetEvent();
325 bool new_acknowledgement_wait_result
= m_acknowledgement_event
.WaitInfinitely();
326 dICHECK(new_acknowledgement_wait_result
);
330 unsigned CALLBACK
dxThreadPoolThreadInfo::ThreadProcedure_Callback(void *thread_param
)
332 dxThreadPoolThreadInfo
*thread_info
= (dxThreadPoolThreadInfo
*)thread_param
;
333 thread_info
->ThreadProcedure();
338 void dxThreadPoolThreadInfo::ThreadProcedure()
340 bool init_result
= dAllocateODEDataForThread(m_ode_data_allocate_flags
) != 0;
342 ReportInitStatus(init_result
);
346 RunCommandHandlingLoop();
348 // dCleanupODEAllDataForThread(); -- this function can only be called if ODE was initialized for manual cleanup. And that is unknown here...
352 void dxThreadPoolThreadInfo::ReportInitStatus(bool init_result
)
355 m_command_param
= (void *)(sizeint
)(init_result
? ERROR_SUCCESS
: ((error_code
= GetLastError()) != ERROR_SUCCESS
? error_code
: ERROR_INTERNAL_ERROR
));
357 m_acknowledgement_event
.SetEvent();
360 void dxThreadPoolThreadInfo::RunCommandHandlingLoop()
362 bool exit_requested
= false;
364 while (!exit_requested
)
366 bool command_wait_result
= m_command_event
.WaitInfinitely();
367 dICHECK(command_wait_result
);
369 const dxTHREADCOMMAND command_code
= m_command_code
;
370 switch (command_code
)
372 case dxTHREAD_COMMAND_EXIT
:
374 m_acknowledgement_event
.SetEvent();
376 exit_requested
= true;
383 // break; -- proceed to case dxTHREAD_COMMAND_NOOP
386 case dxTHREAD_COMMAND_NOOP
:
388 m_acknowledgement_event
.SetEvent();
394 case dxTHREAD_COMMAND_SERVE_IMPLEMENTATION
:
396 const dxServeImplementationParams
*serve_params
= (const dxServeImplementationParams
*)m_command_param
;
397 dThreadingImplementationID impl
= serve_params
->m_impl
;
398 dxEventObject
*ready_wait_event
= serve_params
->m_ready_wait_event
;
400 m_acknowledgement_event
.SetEvent();
402 ThreadedServeImplementation(impl
, ready_wait_event
);
409 void dxThreadPoolThreadInfo::ThreadedServeImplementation(dThreadingImplementationID impl
, dxEventObject
*ready_wait_event
)
411 ((dxIThreadingImplementation
*)impl
)->StickToJobsProcessing(&ProcessThreadServeReadiness_Callback
, (void *)ready_wait_event
);
414 void dxThreadPoolThreadInfo::ProcessThreadServeReadiness_Callback(void *context
)
416 dxEventObject
*ready_wait_event
= (dxEventObject
*)context
;
418 ready_wait_event
->SetEvent();
423 struct dxThreadingThreadPool
:
427 dxThreadingThreadPool();
428 ~dxThreadingThreadPool();
430 bool InitializeThreads(sizeint thread_count
, sizeint stack_size
, unsigned int ode_data_allocate_flags
);
433 void FinalizeThreads();
435 bool InitializeIndividualThreadInfos(dxThreadPoolThreadInfo
*thread_infos
, sizeint thread_count
, sizeint stack_size
, unsigned int ode_data_allocate_flags
);
436 void FinalizeIndividualThreadInfos(dxThreadPoolThreadInfo
*thread_infos
, sizeint thread_count
);
438 bool InitializeSingleThreadInfo(dxThreadPoolThreadInfo
*thread_info
, sizeint stack_size
, unsigned int ode_data_allocate_flags
);
439 void FinalizeSingleThreadInfo(dxThreadPoolThreadInfo
*thread_info
);
442 void ServeThreadingImplementation(dThreadingImplementationID impl
);
443 void WaitIdleState();
446 dxThreadPoolThreadInfo
*m_thread_infos
;
447 sizeint m_thread_count
;
448 dxEventObject m_ready_wait_event
;
452 dxThreadingThreadPool::dxThreadingThreadPool():
453 m_thread_infos(NULL
),
459 dxThreadingThreadPool::~dxThreadingThreadPool()
465 bool dxThreadingThreadPool::InitializeThreads(sizeint thread_count
, sizeint stack_size
, unsigned int ode_data_allocate_flags
)
467 dIASSERT(m_thread_infos
== NULL
);
471 bool wait_event_allocated
= false;
473 dxThreadPoolThreadInfo
*thread_infos
= NULL
;
474 bool thread_infos_allocated
= false;
478 if (!m_ready_wait_event
.InitializeObject(false, false))
483 wait_event_allocated
= true;
485 thread_infos
= (dxThreadPoolThreadInfo
*)dAlloc(thread_count
* sizeof(dxThreadPoolThreadInfo
));
486 if (thread_infos
== NULL
)
491 thread_infos_allocated
= true;
493 if (!InitializeIndividualThreadInfos(thread_infos
, thread_count
, stack_size
, ode_data_allocate_flags
))
498 m_thread_infos
= thread_infos
;
499 m_thread_count
= thread_count
;
506 if (wait_event_allocated
)
508 if (thread_infos_allocated
)
510 dFree(thread_infos
, thread_count
* sizeof(dxThreadPoolThreadInfo
));
513 m_ready_wait_event
.FinalizeObject();
520 void dxThreadingThreadPool::FinalizeThreads()
522 dxThreadPoolThreadInfo
*thread_infos
= m_thread_infos
;
523 if (thread_infos
!= NULL
)
525 sizeint thread_count
= m_thread_count
;
527 FinalizeIndividualThreadInfos(thread_infos
, thread_count
);
528 dFree(thread_infos
, thread_count
* sizeof(dxThreadPoolThreadInfo
));
530 m_ready_wait_event
.FinalizeObject();
535 bool dxThreadingThreadPool::InitializeIndividualThreadInfos(dxThreadPoolThreadInfo
*thread_infos
, sizeint thread_count
, sizeint stack_size
, unsigned int ode_data_allocate_flags
)
537 bool any_fault
= false;
539 dxThreadPoolThreadInfo
*const infos_end
= thread_infos
+ thread_count
;
540 for (dxThreadPoolThreadInfo
*current_info
= thread_infos
; current_info
!= infos_end
; ++current_info
)
542 if (!InitializeSingleThreadInfo(current_info
, stack_size
, ode_data_allocate_flags
))
544 FinalizeIndividualThreadInfos(thread_infos
, current_info
- thread_infos
);
551 bool result
= !any_fault
;
555 void dxThreadingThreadPool::FinalizeIndividualThreadInfos(dxThreadPoolThreadInfo
*thread_infos
, sizeint thread_count
)
557 dxThreadPoolThreadInfo
*const infos_end
= thread_infos
+ thread_count
;
558 for (dxThreadPoolThreadInfo
*current_info
= thread_infos
; current_info
!= infos_end
; ++current_info
)
560 FinalizeSingleThreadInfo(current_info
);
565 bool dxThreadingThreadPool::InitializeSingleThreadInfo(dxThreadPoolThreadInfo
*thread_info
, sizeint stack_size
, unsigned int ode_data_allocate_flags
)
569 new(thread_info
) dxThreadPoolThreadInfo();
571 if (thread_info
->Initialize(stack_size
, ode_data_allocate_flags
))
577 thread_info
->dxThreadPoolThreadInfo::~dxThreadPoolThreadInfo();
583 void dxThreadingThreadPool::FinalizeSingleThreadInfo(dxThreadPoolThreadInfo
*thread_info
)
585 if (thread_info
!= NULL
)
587 thread_info
->dxThreadPoolThreadInfo::~dxThreadPoolThreadInfo();
592 void dxThreadingThreadPool::ServeThreadingImplementation(dThreadingImplementationID impl
)
594 dxThreadPoolThreadInfo::dxServeImplementationParams
params(impl
, &m_ready_wait_event
);
596 dxThreadPoolThreadInfo
*const infos_end
= m_thread_infos
+ m_thread_count
;
597 for (dxThreadPoolThreadInfo
*current_info
= m_thread_infos
; current_info
!= infos_end
; ++current_info
)
599 current_info
->ExecuteThreadCommand(dxThreadPoolThreadInfo::dxTHREAD_COMMAND_SERVE_IMPLEMENTATION
, ¶ms
, true);
601 bool ready_wait_result
= m_ready_wait_event
.WaitInfinitely();
602 dICHECK(ready_wait_result
);
606 void dxThreadingThreadPool::WaitIdleState()
608 dxThreadPoolThreadInfo
*const infos_end
= m_thread_infos
+ m_thread_count
;
609 for (dxThreadPoolThreadInfo
*current_info
= m_thread_infos
; current_info
!= infos_end
; ++current_info
)
611 current_info
->ExecuteThreadCommand(dxThreadPoolThreadInfo::dxTHREAD_COMMAND_NOOP
, NULL
, true);
616 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
619 /*extern */dThreadingThreadPoolID
dThreadingAllocateThreadPool(unsigned thread_count
,
620 sizeint stack_size
, unsigned int ode_data_allocate_flags
, void *reserved
/*=NULL*/)
622 dAASSERT(thread_count
!= 0);
624 #if dBUILTIN_THREADING_IMPL_ENABLED
625 dxThreadingThreadPool
*thread_pool
= new dxThreadingThreadPool();
626 if (thread_pool
!= NULL
)
628 if (thread_pool
->InitializeThreads(thread_count
, stack_size
, ode_data_allocate_flags
))
639 dThreadingThreadPoolID thread_pool
= NULL
;
640 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
642 return (dThreadingThreadPoolID
)thread_pool
;
645 /*extern */void dThreadingThreadPoolServeMultiThreadedImplementation(dThreadingThreadPoolID pool
, dThreadingImplementationID impl
)
647 #if dBUILTIN_THREADING_IMPL_ENABLED
648 dxThreadingThreadPool
*thread_pool
= (dxThreadingThreadPool
*)pool
;
649 thread_pool
->ServeThreadingImplementation(impl
);
650 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
653 /*extern */void dThreadingThreadPoolWaitIdleState(dThreadingThreadPoolID pool
)
655 #if dBUILTIN_THREADING_IMPL_ENABLED
656 dxThreadingThreadPool
*thread_pool
= (dxThreadingThreadPool
*)pool
;
657 thread_pool
->WaitIdleState();
658 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
661 /*extern */void dThreadingFreeThreadPool(dThreadingThreadPoolID pool
)
663 #if dBUILTIN_THREADING_IMPL_ENABLED
664 dxThreadingThreadPool
*thread_pool
= (dxThreadingThreadPool
*)pool
;
666 #endif // #if dBUILTIN_THREADING_IMPL_ENABLED
670 #endif // #if defined(_WIN32)