1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
27 // File: vogl_win32_threading.h
30 #include "vogl_core.h"
32 #include "vogl_atomics.h"
34 #error No atomic operations defined in vogl_platform.h!
39 // g_number_of_processors defaults to 1. Will be higher on multicore machines.
40 extern uint g_number_of_processors;
42 void vogl_threading_init();
44 typedef uint64_t vogl_thread_id_t;
45 vogl_thread_id_t vogl_get_current_thread_id();
47 void vogl_sleep(unsigned int milliseconds);
49 uint vogl_get_max_helper_threads();
53 VOGL_NO_COPY_OR_ASSIGNMENT_OP(mutex);
56 mutex(unsigned int spin_count = 0);
60 void set_spin_count(unsigned int count);
65 #ifdef VOGL_BUILD_DEBUG
66 unsigned int m_lock_count;
72 scoped_mutex(const scoped_mutex &);
73 scoped_mutex &operator=(const scoped_mutex &);
76 inline scoped_mutex(mutex &m)
81 inline ~scoped_mutex()
90 // Simple non-recursive spinlock.
93 VOGL_NO_COPY_OR_ASSIGNMENT_OP(spinlock);
101 void lock(uint32 max_spins = 4096, bool yielding = true);
103 inline void lock_no_barrier(uint32 max_spins = 4096, bool yielding = true)
105 lock(max_spins, yielding);
110 inline void unlock_no_barrier()
116 volatile int32 m_flag;
119 class scoped_spinlock
121 scoped_spinlock(const scoped_spinlock &);
122 scoped_spinlock &operator=(const scoped_spinlock &);
125 inline scoped_spinlock(spinlock &lock)
130 inline ~scoped_spinlock()
141 VOGL_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
144 // TODO: Change to uint32
145 semaphore(int32 initialCount, int32 maximumCount, const char *pName = NULL);
149 inline HANDLE get_handle(void) const
154 void release(int32 releaseCount = 1, int32 *pPreviousCount = NULL);
155 bool try_release(int32 releaseCount = 1, int32 *pPreviousCount = NULL);
157 bool wait(uint32 milliseconds = cUINT32_MAX);
163 template <typename T>
166 VOGL_NO_COPY_OR_ASSIGNMENT_OP(tsstack);
169 inline tsstack(bool use_freelist = true)
170 : m_use_freelist(use_freelist)
172 VOGL_VERIFY(((ptr_bits_t) this & (VOGL_GET_ALIGNMENT(tsstack) - 1)) == 0);
173 InitializeSListHead(&m_stack_head);
174 InitializeSListHead(&m_freelist_head);
186 node *pNode = (node *)InterlockedPopEntrySList(&m_stack_head);
190 VOGL_MEMORY_IMPORT_BARRIER
192 helpers::destruct(&pNode->m_obj);
200 inline void flush_freelist()
207 node *pNode = (node *)InterlockedPopEntrySList(&m_freelist_head);
211 VOGL_MEMORY_IMPORT_BARRIER
217 inline bool try_push(const T &obj)
219 node *pNode = alloc_node();
223 helpers::construct(&pNode->m_obj, obj);
225 VOGL_MEMORY_EXPORT_BARRIER
227 InterlockedPushEntrySList(&m_stack_head, &pNode->m_slist_entry);
232 inline bool pop(T &obj)
234 node *pNode = (node *)InterlockedPopEntrySList(&m_stack_head);
238 VOGL_MEMORY_IMPORT_BARRIER
242 helpers::destruct(&pNode->m_obj);
250 SLIST_HEADER m_stack_head;
251 SLIST_HEADER m_freelist_head;
255 SLIST_ENTRY m_slist_entry;
261 inline node *alloc_node()
263 node *pNode = m_use_freelist ? (node *)InterlockedPopEntrySList(&m_freelist_head) : NULL;
266 pNode = (node *)vogl_malloc(sizeof(node));
271 inline void free_node(node *pNode)
274 InterlockedPushEntrySList(&m_freelist_head, &pNode->m_slist_entry);
280 // Simple multithreaded task pool. This class assumes a single global thread will be issuing tasks and joining.
283 VOGL_NO_COPY_OR_ASSIGNMENT_OP(task_pool);
287 task_pool(uint num_threads);
294 bool init(uint num_threads);
297 inline uint get_num_threads() const
299 return m_num_threads;
301 inline uint32 get_num_outstanding_tasks() const
303 return m_total_submitted_tasks - m_total_completed_tasks;
306 // C-style task callback
307 typedef void (*task_callback_func)(uint64_t data, void *pData_ptr);
308 bool queue_task(task_callback_func pFunc, uint64_t data = 0, void *pData_ptr = NULL);
310 class executable_task
313 virtual void execute_task(uint64_t data, void *pData_ptr) = 0;
316 // It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
317 bool queue_task(executable_task *pObj, uint64_t data = 0, void *pData_ptr = NULL);
319 template <typename S, typename T>
320 inline bool queue_object_task(S *pObject, T pObject_method, uint64_t data = 0, void *pData_ptr = NULL);
322 template <typename S, typename T>
323 inline bool queue_multiple_object_tasks(S *pObject, T pObject_method, uint64_t first_data, uint num_tasks, void *pData_ptr = NULL);
325 // Waits for all outstanding tasks (if any) to complete.
326 // The calling thread will steal any outstanding tasks from worker threads, if possible.
332 //inline task() : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0) { }
339 task_callback_func m_callback;
340 executable_task *m_pObj;
346 typedef tsstack<task> ts_task_stack_t;
347 ts_task_stack_t *m_pTask_stack;
350 HANDLE m_threads[cMaxThreads];
352 // Signalled whenever a task is queued up.
353 semaphore m_tasks_available;
355 // Signalled when all outstanding tasks are completed.
356 semaphore m_all_tasks_completed;
363 volatile atomic32_t m_total_submitted_tasks;
364 volatile atomic32_t m_total_completed_tasks;
365 volatile atomic32_t m_exit_flag;
367 void process_task(task &tsk);
369 static unsigned __stdcall thread_func(void *pContext);
372 enum object_task_flags
374 cObjectTaskFlagDefault = 0,
375 cObjectTaskFlagDeleteAfterExecution = 1
378 template <typename T>
379 class object_task : public task_pool::executable_task
382 object_task(uint flags = cObjectTaskFlagDefault)
389 typedef void (T::*object_method_ptr)(uint64_t data, void *pData_ptr);
391 object_task(T *pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
392 : m_pObject(pObject),
396 VOGL_ASSERT(pObject && pMethod);
399 void init(T *pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
401 VOGL_ASSERT(pObject && pMethod);
408 T *get_object() const
412 object_method_ptr get_method() const
417 virtual void execute_task(uint64_t data, void *pData_ptr)
419 (m_pObject->*m_pMethod)(data, pData_ptr);
421 if (m_flags & cObjectTaskFlagDeleteAfterExecution)
428 object_method_ptr m_pMethod;
433 template <typename S, typename T>
434 inline bool task_pool::queue_object_task(S *pObject, T pObject_method, uint64_t data, void *pData_ptr)
436 object_task<S> *pTask = vogl_new<object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
439 return queue_task(pTask, data, pData_ptr);
442 template <typename S, typename T>
443 inline bool task_pool::queue_multiple_object_tasks(S *pObject, T pObject_method, uint64_t first_data, uint num_tasks, void *pData_ptr)
445 VOGL_ASSERT(pObject);
446 VOGL_ASSERT(num_tasks);
453 for (i = 0; i < num_tasks; i++)
457 tsk.m_pObj = vogl_new<object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
464 tsk.m_data = first_data + i;
465 tsk.m_pData_ptr = pData_ptr;
466 tsk.m_flags = cTaskFlagObject;
468 atomic_increment32(&m_total_submitted_tasks);
470 if (!m_pTask_stack->try_push(tsk))
472 atomic_increment32(&m_total_completed_tasks);
481 m_tasks_available.release(i);