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_threading_pthreads.h
30 #include "vogl_core.h"
32 #if VOGL_USE_PTHREADS_API
34 #include "vogl_atomics.h"
37 #error No atomic operations defined in vogl_platform.h!
41 #include <semaphore.h>
46 // g_number_of_processors defaults to 1. Will be higher on multicore machines.
47 extern uint g_number_of_processors;
49 void vogl_threading_init();
51 typedef uint64_t vogl_thread_id_t;
52 vogl_thread_id_t vogl_get_current_thread_id();
54 void vogl_sleep(unsigned int milliseconds);
56 uint vogl_get_max_helper_threads();
61 mutex &operator=(const mutex &);
64 mutex(unsigned int spin_count = 0, bool recursive = false);
68 void set_spin_count(unsigned int count);
71 pthread_mutex_t m_mutex;
73 #ifdef VOGL_BUILD_DEBUG
74 unsigned int m_lock_count;
80 scoped_mutex(const scoped_mutex &);
81 scoped_mutex &operator=(const scoped_mutex &);
84 inline scoped_mutex(mutex &m)
89 inline ~scoped_mutex()
100 VOGL_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
103 semaphore(uint32 initialCount, uint32 maximumCount, const char *pName = NULL);
106 void release(uint32 releaseCount = 1);
107 void try_release(uint32 releaseCount = 1);
108 bool wait(uint32 milliseconds = cUINT32_MAX);
124 pthread_spinlock_t m_spinlock;
126 #ifdef VOGL_BUILD_DEBUG
131 class scoped_spinlock
133 scoped_spinlock(const scoped_spinlock &);
134 scoped_spinlock &operator=(const scoped_spinlock &);
137 inline scoped_spinlock(spinlock &lock)
142 inline ~scoped_spinlock()
151 template <typename T, uint cMaxSize>
171 inline bool try_push(const T &obj)
175 if (m_top < (int)cMaxSize)
177 m_stack[m_top++] = obj;
184 inline bool pop(T &obj)
190 obj = m_stack[--m_top];
207 task_pool(uint num_threads);
214 bool init(uint num_threads);
217 inline uint get_num_threads() const
219 return m_num_threads;
221 inline uint32 get_num_outstanding_tasks() const
223 return static_cast<uint32>(m_total_submitted_tasks - m_total_completed_tasks);
226 // C-style task callback
227 typedef void (*task_callback_func)(uint64_t data, void *pData_ptr);
228 bool queue_task(task_callback_func pFunc, uint64_t data = 0, void *pData_ptr = NULL);
230 class executable_task
233 virtual ~executable_task()
236 virtual void execute_task(uint64_t data, void *pData_ptr) = 0;
239 // It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
240 bool queue_task(executable_task *pObj, uint64_t data = 0, void *pData_ptr = NULL);
242 template <typename S, typename T>
243 inline bool queue_object_task(S *pObject, T pObject_method, uint64_t data = 0, void *pData_ptr = NULL);
245 template <typename S, typename T>
246 inline bool queue_multiple_object_tasks(S *pObject, T pObject_method, uint64_t first_data, uint num_tasks, void *pData_ptr = NULL);
254 : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0)
263 task_callback_func m_callback;
264 executable_task *m_pObj;
270 tsstack<task, cMaxThreads> m_task_stack;
273 pthread_t m_threads[cMaxThreads];
275 // Signalled whenever a task is queued up.
276 semaphore m_tasks_available;
278 // Signalled when all outstanding tasks are completed.
279 semaphore m_all_tasks_completed;
286 atomic32_t m_total_submitted_tasks;
287 atomic32_t m_total_completed_tasks;
288 atomic32_t m_exit_flag;
290 void process_task(task &tsk);
292 static void *thread_func(void *pContext);
295 enum object_task_flags
297 cObjectTaskFlagDefault = 0,
298 cObjectTaskFlagDeleteAfterExecution = 1
301 template <typename T>
302 class object_task : public task_pool::executable_task
305 object_task(uint flags = cObjectTaskFlagDefault)
312 virtual ~object_task()
316 typedef void (T::*object_method_ptr)(uint64_t data, void *pData_ptr);
318 object_task(T *pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
319 : m_pObject(pObject),
323 VOGL_ASSERT(pObject && pMethod);
326 void init(T *pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
328 VOGL_ASSERT(pObject && pMethod);
335 T *get_object() const
339 object_method_ptr get_method() const
344 virtual void execute_task(uint64_t data, void *pData_ptr)
346 (m_pObject->*m_pMethod)(data, pData_ptr);
348 if (m_flags & cObjectTaskFlagDeleteAfterExecution)
355 object_method_ptr m_pMethod;
360 template <typename S, typename T>
361 inline bool task_pool::queue_object_task(S *pObject, T pObject_method, uint64_t data, void *pData_ptr)
363 object_task<S> *pTask = vogl_new(object_task<S>, pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
366 return queue_task(pTask, data, pData_ptr);
369 template <typename S, typename T>
370 inline bool task_pool::queue_multiple_object_tasks(S *pObject, T pObject_method, uint64_t first_data, uint num_tasks, void *pData_ptr)
372 VOGL_ASSERT(pObject);
373 VOGL_ASSERT(num_tasks);
380 for (i = 0; i < num_tasks; i++)
384 tsk.m_pObj = vogl_new(object_task<S>, pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
391 tsk.m_data = first_data + i;
392 tsk.m_pData_ptr = pData_ptr;
393 tsk.m_flags = cTaskFlagObject;
395 atomic_increment32(&m_total_submitted_tasks);
397 if (!m_task_stack.try_push(tsk))
399 atomic_increment32(&m_total_completed_tasks);
408 m_tasks_available.release(i);
416 #endif // VOGL_USE_PTHREADS_API