]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_threading_pthreads.h
Initial vogl checkin
[vogl] / src / voglcore / vogl_threading_pthreads.h
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
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
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_threading_pthreads.h
28 #pragma once
29
30 #include "vogl_core.h"
31
32 #if VOGL_USE_PTHREADS_API
33
34 #include "vogl_atomics.h"
35
36 #if VOGL_NO_ATOMICS
37 #error No atomic operations defined in vogl_platform.h!
38 #endif
39
40 #include <pthread.h>
41 #include <semaphore.h>
42 #include <unistd.h>
43
44 namespace vogl
45 {
46     // g_number_of_processors defaults to 1. Will be higher on multicore machines.
47     extern uint g_number_of_processors;
48
49     void vogl_threading_init();
50
51     typedef uint64_t vogl_thread_id_t;
52     vogl_thread_id_t vogl_get_current_thread_id();
53
54     void vogl_sleep(unsigned int milliseconds);
55
56     uint vogl_get_max_helper_threads();
57
58     class mutex
59     {
60         mutex(const mutex &);
61         mutex &operator=(const mutex &);
62
63     public:
64         mutex(unsigned int spin_count = 0, bool recursive = false);
65         ~mutex();
66         void lock();
67         void unlock();
68         void set_spin_count(unsigned int count);
69
70     private:
71         pthread_mutex_t m_mutex;
72
73 #ifdef VOGL_BUILD_DEBUG
74         unsigned int m_lock_count;
75 #endif
76     };
77
78     class scoped_mutex
79     {
80         scoped_mutex(const scoped_mutex &);
81         scoped_mutex &operator=(const scoped_mutex &);
82
83     public:
84         inline scoped_mutex(mutex &m)
85             : m_mutex(m)
86         {
87             m_mutex.lock();
88         }
89         inline ~scoped_mutex()
90         {
91             m_mutex.unlock();
92         }
93
94     private:
95         mutex &m_mutex;
96     };
97
98     class semaphore
99     {
100         VOGL_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
101
102     public:
103         semaphore(uint32 initialCount, uint32 maximumCount, const char *pName = NULL);
104         ~semaphore();
105
106         void release(uint32 releaseCount = 1);
107         void try_release(uint32 releaseCount = 1);
108         bool wait(uint32 milliseconds = cUINT32_MAX);
109
110     private:
111         sem_t m_sem;
112     };
113
114     class spinlock
115     {
116     public:
117         spinlock();
118         ~spinlock();
119
120         void lock();
121         void unlock();
122
123     private:
124         pthread_spinlock_t m_spinlock;
125
126 #ifdef VOGL_BUILD_DEBUG
127         bool m_in_lock;
128 #endif
129     };
130
131     class scoped_spinlock
132     {
133         scoped_spinlock(const scoped_spinlock &);
134         scoped_spinlock &operator=(const scoped_spinlock &);
135
136     public:
137         inline scoped_spinlock(spinlock &lock)
138             : m_lock(lock)
139         {
140             m_lock.lock();
141         }
142         inline ~scoped_spinlock()
143         {
144             m_lock.unlock();
145         }
146
147     private:
148         spinlock &m_lock;
149     };
150
151     template <typename T, uint cMaxSize>
152     class tsstack
153     {
154     public:
155         inline tsstack()
156             : m_top(0)
157         {
158         }
159
160         inline ~tsstack()
161         {
162         }
163
164         inline void clear()
165         {
166             m_spinlock.lock();
167             m_top = 0;
168             m_spinlock.unlock();
169         }
170
171         inline bool try_push(const T &obj)
172         {
173             bool result = false;
174             m_spinlock.lock();
175             if (m_top < (int)cMaxSize)
176             {
177                 m_stack[m_top++] = obj;
178                 result = true;
179             }
180             m_spinlock.unlock();
181             return result;
182         }
183
184         inline bool pop(T &obj)
185         {
186             bool result = false;
187             m_spinlock.lock();
188             if (m_top > 0)
189             {
190                 obj = m_stack[--m_top];
191                 result = true;
192             }
193             m_spinlock.unlock();
194             return result;
195         }
196
197     private:
198         spinlock m_spinlock;
199         T m_stack[cMaxSize];
200         int m_top;
201     };
202
203     class task_pool
204     {
205     public:
206         task_pool();
207         task_pool(uint num_threads);
208         ~task_pool();
209
210         enum
211         {
212             cMaxThreads = 16
213         };
214         bool init(uint num_threads);
215         void deinit();
216
217         inline uint get_num_threads() const
218         {
219             return m_num_threads;
220         }
221         inline uint32 get_num_outstanding_tasks() const
222         {
223             return static_cast<uint32>(m_total_submitted_tasks - m_total_completed_tasks);
224         }
225
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);
229
230         class executable_task
231         {
232         public:
233             virtual ~executable_task()
234             {
235             }
236             virtual void execute_task(uint64_t data, void *pData_ptr) = 0;
237         };
238
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);
241
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);
244
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);
247
248         void join();
249
250     private:
251         struct task
252         {
253             inline task()
254                 : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0)
255             {
256             }
257
258             uint64_t m_data;
259             void *m_pData_ptr;
260
261             union
262             {
263                 task_callback_func m_callback;
264                 executable_task *m_pObj;
265             };
266
267             uint m_flags;
268         };
269
270         tsstack<task, cMaxThreads> m_task_stack;
271
272         uint m_num_threads;
273         pthread_t m_threads[cMaxThreads];
274
275         // Signalled whenever a task is queued up.
276         semaphore m_tasks_available;
277
278         // Signalled when all outstanding tasks are completed.
279         semaphore m_all_tasks_completed;
280
281         enum task_flags
282         {
283             cTaskFlagObject = 1
284         };
285
286         atomic32_t m_total_submitted_tasks;
287         atomic32_t m_total_completed_tasks;
288         atomic32_t m_exit_flag;
289
290         void process_task(task &tsk);
291
292         static void *thread_func(void *pContext);
293     };
294
295     enum object_task_flags
296     {
297         cObjectTaskFlagDefault = 0,
298         cObjectTaskFlagDeleteAfterExecution = 1
299     };
300
301     template <typename T>
302     class object_task : public task_pool::executable_task
303     {
304     public:
305         object_task(uint flags = cObjectTaskFlagDefault)
306             : m_pObject(NULL),
307               m_pMethod(NULL),
308               m_flags(flags)
309         {
310         }
311
312         virtual ~object_task()
313         {
314         }
315
316         typedef void (T::*object_method_ptr)(uint64_t data, void *pData_ptr);
317
318         object_task(T *pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
319             : m_pObject(pObject),
320               m_pMethod(pMethod),
321               m_flags(flags)
322         {
323             VOGL_ASSERT(pObject && pMethod);
324         }
325
326         void init(T *pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
327         {
328             VOGL_ASSERT(pObject && pMethod);
329
330             m_pObject = pObject;
331             m_pMethod = pMethod;
332             m_flags = flags;
333         }
334
335         T *get_object() const
336         {
337             return m_pObject;
338         }
339         object_method_ptr get_method() const
340         {
341             return m_pMethod;
342         }
343
344         virtual void execute_task(uint64_t data, void *pData_ptr)
345         {
346             (m_pObject->*m_pMethod)(data, pData_ptr);
347
348             if (m_flags & cObjectTaskFlagDeleteAfterExecution)
349                 vogl_delete(this);
350         }
351
352     protected:
353         T *m_pObject;
354
355         object_method_ptr m_pMethod;
356
357         uint m_flags;
358     };
359
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)
362     {
363         object_task<S> *pTask = vogl_new(object_task<S>, pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
364         if (!pTask)
365             return false;
366         return queue_task(pTask, data, pData_ptr);
367     }
368
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)
371     {
372         VOGL_ASSERT(pObject);
373         VOGL_ASSERT(num_tasks);
374         if (!num_tasks)
375             return true;
376
377         bool status = true;
378
379         uint i;
380         for (i = 0; i < num_tasks; i++)
381         {
382             task tsk;
383
384             tsk.m_pObj = vogl_new(object_task<S>, pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
385             if (!tsk.m_pObj)
386             {
387                 status = false;
388                 break;
389             }
390
391             tsk.m_data = first_data + i;
392             tsk.m_pData_ptr = pData_ptr;
393             tsk.m_flags = cTaskFlagObject;
394
395             atomic_increment32(&m_total_submitted_tasks);
396
397             if (!m_task_stack.try_push(tsk))
398             {
399                 atomic_increment32(&m_total_completed_tasks);
400
401                 status = false;
402                 break;
403             }
404         }
405
406         if (i)
407         {
408             m_tasks_available.release(i);
409         }
410
411         return status;
412     }
413
414 } // namespace vogl
415
416 #endif // VOGL_USE_PTHREADS_API