1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_ThreadPool.h
4 
5   Copyright (C)2009 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 30270 $
14  *---------------------------------------------------------------------------*/
15 
16 /*! @file
17     @brief    ThreadPool に関する API の宣言
18 
19             このファイル内の記述は将来的に大幅に変更される可能性があります。
20             直接使わないでください。
21 
22     @deprecated
23 
24     :include nn/os.h
25 */
26 
27 #ifndef NN_OS_OS_THREADPOOL_H_
28 #define NN_OS_OS_THREADPOOL_H_
29 
30 #include <nn/os/os_Synchronization.h>
31 #include <nn/os/os_StackMemoryBlock.h>
32 #include <nn/os/os_Thread.h>
33 #include <nn/os/os_Event.h>
34 #include <nn/os/os_LightEvent.h>
35 #include <nn/os/os_CriticalSection.h>
36 #include <nn/fnd/fnd_Queue.h>
37 #include <nn/util/util_NonCopyable.h>
38 #include <nn/os/os_Task.h>
39 
40 #ifdef __cplusplus
41 
42 namespace nn { namespace os {
43 
44 /*!
45     @brief  スレッドプールクラスです。
46 
47     :private
48 */
49 class ThreadPool : public IWaitTaskInvoker, private nn::util::NonCopyable<ThreadPool>
50 {
51 public:
52 
53     // 初期化時の第1引数に与えるバッファのサイズ。
54     // このバッファはユーザ側で確保する必要がある。
55     /*!
56         @brief      スレッドプールで使用するバッファのサイズを計算します。
57 
58         @param[in]  numMaxWaitObjects   同期オブジェクトの最大数を指定します。
59         @param[in]  numWorkerThreads    ワーカスレッドの数を指定します。
60 
61         @return     スレッドプールで使用するバッファのサイズを返します。
62      */
63     static size_t GetWorkBufferSize(size_t numMaxWaitObjects, size_t numWorkerThreads);
64 
65     /*!
66         @brief      コンストラクタ
67 
68         スレッドプールの初期化を行いません。
69      */
70     ThreadPool();
71     /*!
72         @brief      コンストラクタ
73 
74         @param[in]  workBuffer          スレッドプールで使用するバッファを指定します。バッファサイズは GetWorkBufferSize() で計算します。
75         @param[in]  numMaxWaitObjects   同期オブジェクトの最大数を指定します。
76         @param[in]  numWorkerThreads    ワーカスレッドの数を指定します。
77         @param[in]  workerStackBottoms  ワーカスレッドのスタックポインタの開始アドレスを指定します。
78         @param[in]  workerPriority      ワーカスレッドの優先度を指定します。
79      */
80     ThreadPool(void* workBuffer, size_t numMaxWaitObjects, size_t numWorkerThreads, uptr workerStackBottoms[], s32 workerPriority = DEFAULT_THREAD_PRIORITY);
81 
82 #if NN_PLATFORM_HAS_MMU
83     /*!
84         @brief      コンストラクタ
85 
86         @param[in]  workBuffer          スレッドプールで使用するバッファを指定します。バッファサイズは GetWorkBufferSize() で計算します。
87         @param[in]  numMaxWaitObjects   同期オブジェクトの最大数を指定します。
88         @param[in]  numWorkerThreads    ワーカスレッドの数を指定します。
89         @param[in]  workerStacks        ワーカスレッドのスタックポインタを指定します。
90         @param[in]  workerPriority      ワーカスレッドの優先度を指定します。
91      */
92     ThreadPool(void* workBuffer, size_t numMaxWaitObjects, size_t numWorkerThreads, nn::os::StackMemoryBlock workerStacks[], s32 workerPriority = DEFAULT_THREAD_PRIORITY);
93 #endif  // if NN_PLATFORM_HAS_MMU
94 
95     /*!
96         @brief      デストラクタ
97 
98         内部で Finalize() を呼びます。
99      */
~ThreadPool()100     virtual ~ThreadPool() { Finalize(); }
101 
102     /*!
103         @brief      スレッドプールの初期化を行います。
104 
105         @param[in]  workBuffer          スレッドプールで使用するバッファを指定します。バッファサイズは GetWorkBufferSize() で計算します。
106         @param[in]  numMaxWaitObjects   同期オブジェクトの最大数を指定します。
107         @param[in]  numWorkerThreads    ワーカスレッドの数を指定します。
108         @param[in]  workerStackBottoms  ワーカスレッドのスタックポインタの開始アドレスを指定します。
109         @param[in]  workerPriority      ワーカスレッドの優先度を指定します。
110 
111         @return     無し。
112      */
113     void Initialize(void* workBuffer, size_t numMaxWaitObjects, size_t numWorkerThreads, uptr workerStackBottoms[], s32 workerPriority = DEFAULT_THREAD_PRIORITY);
114 #if NN_PLATFORM_HAS_MMU
115     /*!
116         @brief      スレッドプールの初期化を行います。
117 
118         @param[in]  workBuffer          スレッドプールで使用するバッファを指定します。バッファサイズは GetWorkBufferSize() で計算します。
119         @param[in]  numMaxWaitObjects   同期オブジェクトの最大数を指定します。
120         @param[in]  numWorkerThreads    ワーカスレッドの数を指定します。
121         @param[in]  workerStacks        ワーカスレッドのスタックを指定します。
122         @param[in]  workerPriority      ワーカスレッドの優先度を指定します。
123 
124         @return     無し。
125      */
126     void Initialize(void* workBuffer, size_t numMaxWaitObjects, size_t numWorkerThreads, nn::os::StackMemoryBlock workerStacks[], s32 workerPriority = DEFAULT_THREAD_PRIORITY);
127 #endif  // if NN_PLATFORM_HAS_MMU
128 
129     // TODO: TryInitialize 必要? 巻き戻しが非常に大変なため、実装の優先度は低。
130 
131     /*!
132         @brief      スレッドプールを破棄します。
133 
134         @return     無し。
135      */
136     void Finalize();
137 
138     /*!
139         @brief      プールスレッドで処理するタスクを追加します。
140 
141                     空いているプールスレッドがあれば、プールスレッドで即座に task->Invoke() を実行します。
142                     空いているプールスレッドがなければ、タスクをキューに入れプールスレッドが空くのを待ちます。
143 
144         @param[in]  task   プールスレッドで処理するタスクを指定します。
145 
146         @return     無し。
147      */
AddTask(QueueableTask * task)148     virtual void AddTask(QueueableTask* task) { AddToExecuteQueue(task); }
149 
150     /*!
151         @brief      同期して処理を開始するタスクを追加します。
152 
153                     同期スレッドに空きがあれば、タスクを同期タスクに登録し task->GetWaitObject() を待った後、
154                     プールスレッドで task->Invoke() を実行します。
155                     同期スレッドに空きがなければ、タスクをキューに入れ同期スレッドが空くのを待ちます。
156                     タスクがキューに入っている間は同期待ちは行われません。
157 
158         @param[in]  task   同期スレッドで処理するタスクを指定します。
159 
160         @return     無し。
161      */
AddWaitTask(QueueableWaitTask * task)162     virtual void AddWaitTask(QueueableWaitTask* task) { AddToWaitQueue(task); }
163 
164 private:
165 
166     size_t m_NumMaxWaitObjects;
167     size_t m_NumThreads;
168 
169     uptr m_Buffer;
170     Thread* GetThreads() const;
171     nn::Handle* GetWaitHandleBuffer() const;
172     QueueableWaitTask** GetWaitTaskBuffer() const;
173 
174     s32 m_WaitingCount;
175 
176     bool m_Finalizing;
177     bit8 m_Padding[3];
178 
179     static const size_t WAIT_THREAD_STACK_SIZE = 392;
180     StackBuffer<WAIT_THREAD_STACK_SIZE> m_WaitThreadStack;
181 
182     Thread m_WaitThread;
183     nn::fnd::IntrusiveQueue<QueueableTask> m_WaitQueue;
184     nn::os::CriticalSection m_WaitLock;
185     nn::os::Event m_WaitEvent;
186     void AddToWaitQueue(QueueableWaitTask*);
187 
188     nn::fnd::IntrusiveQueue<QueueableTask> m_ExecuteQueue;
189     nn::os::CriticalSection m_ExecuteLock;
190     nn::os::LightEvent m_ExecuteEvent;
191     void AddToExecuteQueue(QueueableTask*);
192 
193 
194     static void WaitThreadFunc(ThreadPool*);
195     void WaitThreadFunc();
196     static void ExecuteThreadFunc(ThreadPool*);
197     void ExecuteThreadFunc();
198 
199     void InitializeCommon(size_t numMaxWaitObjects, size_t numWorkerThreads, void* workBuffer);
200     void StartWaitThread();
201     void StartExecuteThread(size_t i, uptr stackBottom, s32 priority);
202 
203 };
204 
ThreadPool()205 inline ThreadPool::ThreadPool() : m_Buffer(0) {}
206 
ThreadPool(void * workBuffer,size_t numMaxWaitObjects,size_t numWorkerThreads,uptr workerStackBottoms[],s32 workerPriority)207 inline ThreadPool::ThreadPool(void* workBuffer, size_t numMaxWaitObjects, size_t numWorkerThreads, uptr workerStackBottoms[], s32 workerPriority)
208     : m_Buffer(0)
209 {
210     Initialize(workBuffer, numMaxWaitObjects, numWorkerThreads, workerStackBottoms, workerPriority);
211 }
212 
213 #if NN_PLATFORM_HAS_MMU
ThreadPool(void * workBuffer,size_t numMaxWaitObjects,size_t numWorkerThreads,nn::os::StackMemoryBlock workerStacks[],s32 workerPriority)214 inline ThreadPool::ThreadPool(void* workBuffer, size_t numMaxWaitObjects, size_t numWorkerThreads, nn::os::StackMemoryBlock workerStacks[], s32 workerPriority)
215     : m_Buffer(0)
216 {
217     Initialize(workBuffer, numMaxWaitObjects, numWorkerThreads, workerStacks, workerPriority);
218 }
219 #endif  // if NN_PLATFORM_HAS_MMU
220 
221 
222 class SingleThreadPool : public ITaskInvoker, private nn::util::NonCopyable<SingleThreadPool>
223 {
224 public:
SingleThreadPool()225     SingleThreadPool() : m_Finalizing(false) {}
~SingleThreadPool()226     virtual ~SingleThreadPool() { Finalize(); }
227     void Initialize(uptr workerThreadStackBottom, s32 workerPriority = DEFAULT_THREAD_PRIORITY);
228     void Finalize();
229 
AddTask(QueueableTask * task)230     virtual void AddTask(QueueableTask* task) { AddToExecuteQueue(task); }
231 
232     static void ExecuteThreadFunc(SingleThreadPool* pInstance);
233     void ExecuteThreadFunc();
234 
235 private:
236     nn::os::Thread m_WorkerThread;
237     nn::fnd::IntrusiveQueue<QueueableTask> m_ExecuteQueue;
238     nn::os::CriticalSection m_ExecuteLock;
239     nn::os::LightEvent m_ExecuteEvent;
240 
241     bool    m_Finalizing;
242     NN_PADDING3;
243 
244     void AddToExecuteQueue(QueueableTask*);
245 };
246 
247 
248 }}
249 
250 #endif // __cplusplus
251 
252 // 以下、C 用宣言
253 
254 
255 #ifdef __cplusplus
256 
257 namespace nn { namespace os {
258 
259 namespace detail
260 {
261     class ThreadPoolTaskForC : public QueueableTask
262     {
263     public:
ThreadPoolTaskForC(void (* f)(uptr),uptr param)264         ThreadPoolTaskForC(void (*f)(uptr), uptr param) : m_F(f), m_Param(param) {}
Invoke()265         virtual void Invoke() { m_F(m_Param); }
266     private:
267         void (*m_F)(uptr);
268         uptr m_Param;
269     };
270 
271     class ThreadPoolWaitTaskForC : public QueueableWaitTask
272     {
273     public:
ThreadPoolWaitTaskForC(nnosWaitObject * waitObject,void (* f)(uptr),uptr param)274         ThreadPoolWaitTaskForC(nnosWaitObject* waitObject, void (*f)(uptr), uptr param) : m_WaitObject(waitObject), m_F(f), m_Param(param) {}
GetWaitObject()275         virtual nn::os::WaitObject* GetWaitObject() { return reinterpret_cast<nn::os::WaitObject*&>(m_WaitObject); }
Invoke()276         virtual void Invoke() { m_F(m_Param); }
277     private:
278         nnosWaitObject* m_WaitObject;
279         void (*m_F)(uptr);
280         uptr m_Param;
281     };
282 }
283 
284 }}
285 
286 #endif
287 
288 #include <nn/util/detail/util_CLibImpl.h>
289 
290 /*!
291   @addtogroup   nn_os                   os
292   @{
293 
294   @defgroup     nn_os_ThreadPool_c          ThreadPool (C)
295 
296   @brief        @ref nn::os::ThreadPool の C インタフェースモジュールです。
297 
298   :private
299 
300   @{
301 */
302 
303 /*!
304   :private
305 
306   @struct       nnosThreadPoolTask
307   @brief        スレッドプールで処理するタスクを表す C の構造体です。
308 
309   @brief 対応するクラス @ref nn::os::QueueableTask を参照してください。
310 */
311 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosThreadPoolTask, nn::os::detail::ThreadPoolTaskForC, 16, u32);
312 
313 /*!
314   @brief タスクを初期化します。
315 */
316 NN_EXTERN_C void nnosThreadPoolTaskInitialize(nnosThreadPoolTask* this_, void (*f)(uptr), uptr param);
317 
318 /*!
319   @brief タスクを破棄します。
320 */
321 
322 NN_EXTERN_C void nnosThreadPoolTaskFinalize(nnosThreadPoolTask* this_);
323 
324 
325 /*!
326   :private
327 
328   @struct       nnosThreadPoolWaitTask
329   @brief        スレッドプールで処理する同期タスクを表す C の構造体です。
330 
331   @brief 対応するクラス @ref nn::os::QueueableWaitTask を参照してください。
332 */
333 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosThreadPoolWaitTask, nn::os::detail::ThreadPoolWaitTaskForC, 20, u32);
334 
335 /*!
336   @brief 同期タスクを初期化します。
337 */
338 NN_EXTERN_C void nnosThreadPoolWaitTaskInitialize(nnosThreadPoolWaitTask* this_, nnosWaitObject* waitObject, void (*f)(uptr), uptr param);
339 
340 /*!
341   @brief 同期タスクを破棄します。
342 */
343 NN_EXTERN_C void nnosThreadPoolWaitTaskFinalize(nnosThreadPoolWaitTask* this_);
344 
345 
346 /*!
347   :private
348 
349   @struct       nnosThreadPool
350   @brief        スレッドプールを表す C の構造体です。
351 
352   @brief 対応するクラス @ref nn::os::ThreadPool を参照してください。
353 */
354 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosThreadPool, nn::os::ThreadPool, 472, bit64);
355 
356 /*!
357   @brief 対応する C++ 関数 @ref nn::os::ThreadPool::Initialize を参照してください。
358 */
359 NN_EXTERN_C void nnosThreadPoolInitialize(nnosThreadPool* this_, void* workBuffer, size_t numMaxWaitObjects, size_t numWorkerThreads, uptr workerStackBottoms[], s32 workerPriority);
360 #if NN_PLATFORM_HAS_MMU
361 NN_EXTERN_C void nnosThreadPoolInitializeWithStackMemoryBlock(nnosThreadPool* this_, void* workBuffer, size_t numMaxWaitObjects, size_t numWorkerThreads, nnosStackMemoryBlock workerStacks[], s32 workerPriority);
362 #endif  // if NN_PLATFORM_HAS_MMU
363 
364 /*!
365   @brief 対応する C++ 関数 @ref nn::os::ThreadPool::Finalize を参照してください。
366 */
367 NN_EXTERN_C void nnosThreadPoolFinalize(nnosThreadPool* this_);
368 
369 /*!
370   @brief 対応する C++ 関数 @ref nn::os::ThreadPool::AddWaitTask を参照してください。
371 */
372 NN_EXTERN_C void nnosThreadPoolAddWaitTask(nnosThreadPool* this_, nnosThreadPoolWaitTask* task);
373 
374 /*!
375   @brief 対応する C++ 関数 @ref nn::os::ThreadPool::AddTask を参照してください。
376 */
377 NN_EXTERN_C void nnosThreadPoolAddTask(nnosThreadPool* this_, nnosThreadPoolTask* task);
378 
379 /*!
380   @brief 対応する C++ 関数 @ref nn::os::ThreadPool::GetWorkBufferSize を参照してください。
381 */
382 NN_EXTERN_C size_t nnosThreadPoolGetWorkBufferSize(size_t numMaxWaitObjects, size_t numWorkerThreads);
383 
384 /*!
385   @}
386 
387   @}
388 */
389 
390 #endif // NN_OS_OS_THREADPOOL_H_
391