// File: lzham_task_pool_win32.cpp // LZHAM is in the Public Domain. Please see the Public Domain declaration at the end of include/lzham.h #include "lzham_core.h" #include "lzham_win32_threading.h" #include "lzham_timer.h" #include #if LZHAM_USE_WIN32_API namespace lzham { task_pool::task_pool() : m_num_threads(0), m_tasks_available(0, 32767), m_num_outstanding_tasks(0), m_exit_flag(false) { utils::zero_object(m_threads); } task_pool::task_pool(uint num_threads) : m_num_threads(0), m_tasks_available(0, 32767), m_num_outstanding_tasks(0), m_exit_flag(false) { utils::zero_object(m_threads); bool status = init(num_threads); LZHAM_VERIFY(status); } task_pool::~task_pool() { deinit(); } bool task_pool::init(uint num_threads) { LZHAM_ASSERT(num_threads <= cMaxThreads); num_threads = math::minimum(num_threads, cMaxThreads); deinit(); bool succeeded = true; m_num_threads = 0; while (m_num_threads < num_threads) { m_threads[m_num_threads] = (HANDLE)_beginthreadex(NULL, 32768, thread_func, this, 0, NULL); LZHAM_ASSERT(m_threads[m_num_threads] != 0); if (!m_threads[m_num_threads]) { succeeded = false; break; } m_num_threads++; } if (!succeeded) { deinit(); return false; } return true; } void task_pool::deinit() { if (m_num_threads) { join(); atomic_exchange32(&m_exit_flag, true); m_tasks_available.release(m_num_threads); for (uint i = 0; i < m_num_threads; i++) { if (m_threads[i]) { for ( ; ; ) { DWORD result = WaitForSingleObject(m_threads[i], 30000); if ((result == WAIT_OBJECT_0) || (result == WAIT_ABANDONED)) break; } CloseHandle(m_threads[i]); m_threads[i] = NULL; } } m_num_threads = 0; atomic_exchange32(&m_exit_flag, false); } m_task_stack.clear(); m_num_outstanding_tasks = 0; } bool task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr) { LZHAM_ASSERT(m_num_threads); LZHAM_ASSERT(pFunc); task tsk; tsk.m_callback = pFunc; tsk.m_data = data; tsk.m_pData_ptr = pData_ptr; tsk.m_flags = 0; if (!m_task_stack.try_push(tsk)) return false; atomic_increment32(&m_num_outstanding_tasks); m_tasks_available.release(1); return true; } // It's the object's responsibility to delete pObj within the execute_task() method, if needed! bool task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr) { LZHAM_ASSERT(m_num_threads); LZHAM_ASSERT(pObj); task tsk; tsk.m_pObj = pObj; tsk.m_data = data; tsk.m_pData_ptr = pData_ptr; tsk.m_flags = cTaskFlagObject; if (!m_task_stack.try_push(tsk)) return false; atomic_increment32(&m_num_outstanding_tasks); m_tasks_available.release(1); return true; } void task_pool::process_task(task& tsk) { if (tsk.m_flags & cTaskFlagObject) tsk.m_pObj->execute_task(tsk.m_data, tsk.m_pData_ptr); else tsk.m_callback(tsk.m_data, tsk.m_pData_ptr); atomic_decrement32(&m_num_outstanding_tasks); } void task_pool::join() { while (atomic_add32(&m_num_outstanding_tasks, 0) > 0) { task tsk; if (m_task_stack.pop(tsk)) { process_task(tsk); } else { lzham_sleep(1); } } } unsigned __stdcall task_pool::thread_func(void* pContext) { task_pool* pPool = static_cast(pContext); for ( ; ; ) { if (!pPool->m_tasks_available.wait()) break; if (pPool->m_exit_flag) break; task tsk; if (pPool->m_task_stack.pop(tsk)) { pPool->process_task(tsk); } } _endthreadex(0); return 0; } static uint g_num_processors; uint lzham_get_max_helper_threads() { if (!g_num_processors) { SYSTEM_INFO system_info; GetSystemInfo(&system_info); g_num_processors = system_info.dwNumberOfProcessors; } if (g_num_processors > 1) { // use all CPU's return LZHAM_MIN(task_pool::cMaxThreads, g_num_processors - 1); } return 0; } } // namespace lzham #endif // LZHAM_USE_WIN32_API