xref: /aosp_15_r20/external/libopenapv/src/oapv_tpool.c (revision abb65b4b03b69e1d508d4d9a44dcf199df16e7c3)
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  * All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright notice,
12  *   this list of conditions and the following disclaimer in the documentation
13  *   and/or other materials provided with the distribution.
14  *
15  * - Neither the name of the copyright owner, nor the names of its contributors
16  *   may be used to endorse or promote products derived from this software
17  *   without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include "oapv_tpool.h"
35 #if defined(WIN32) || defined(WIN64)
36 #include <windows.h>
37 #include <process.h>
38 #else
39 #include <pthread.h>
40 #endif
41 
42 #define WINDOWS_MUTEX_SYNC 0
43 
44 #if !defined(WIN32) && !defined(WIN64)
45 
46 typedef struct thread_ctx {
47     // synchronization members
48     pthread_t              t_handle;   // worker thread handle
49     pthread_attr_t         tAttribute; // worker thread attribute
50     pthread_cond_t         w_event;    // wait event for worker thread
51     pthread_cond_t         r_event;    // wait event for main thread
52     pthread_mutex_t        c_section;  // for synchronization
53 
54     // member field to run  a task
55     oapv_fn_thread_entry_t task;
56     void                  *t_arg;
57     tpool_status_t         t_status;
58     tpool_result_t         t_result;
59     int                    thread_id;
60     int                    task_ret; // return value of task function
61 } thread_ctx_t;
62 
63 typedef struct thread_mutex {
64     pthread_mutex_t lmutex;
65 } thread_mutex_t;
66 
tpool_worker_thread(void * arg)67 static void *tpool_worker_thread(void *arg)
68 {
69     /********************* main routine for thread pool worker thread *************************
70     ********************** worker thread can remain in suspended or running state *************
71     ********************* control the synchronization with help of thread context members *****/
72 
73     // member Initialization section
74     thread_ctx_t *t_context = (thread_ctx_t *)arg;
75     int           ret;
76 
77     if(!t_context) {
78         return 0; // error handling, more like a fail safe mechanism
79     }
80 
81     while(1) {
82         // worker thread loop
83         // remains suspended/sleep waiting for an event
84 
85         // get the mutex and check the state
86         pthread_mutex_lock(&t_context->c_section);
87         while(t_context->t_status == TPOOL_SUSPENDED) {
88             // wait for the event
89             pthread_cond_wait(&t_context->w_event, &t_context->c_section);
90         }
91 
92         if(t_context->t_status == TPOOL_TERMINATED) {
93             t_context->t_result = TPOOL_SUCCESS;
94             pthread_mutex_unlock(&t_context->c_section);
95             break; // exit the routine
96         }
97 
98         t_context->t_status = TPOOL_RUNNING;
99         pthread_mutex_unlock(&t_context->c_section);
100 
101         // run the routine
102         // worker thread state is running with entry function and arg set
103         ret = t_context->task(t_context->t_arg);
104 
105         // signal the thread waiting on the result
106         pthread_mutex_lock(&t_context->c_section);
107         t_context->t_status = TPOOL_SUSPENDED;
108         t_context->t_result = TPOOL_SUCCESS;
109         t_context->task_ret = ret;
110         pthread_cond_signal(&t_context->r_event);
111         pthread_mutex_unlock(&t_context->c_section);
112     }
113 
114     return 0;
115 }
116 
tpool_create_thread(oapv_tpool_t * tp,int thread_id)117 static oapv_thread_t tpool_create_thread(oapv_tpool_t *tp, int thread_id)
118 {
119     if(!tp) {
120         return NULL; // error management
121     }
122 
123     thread_ctx_t *tctx = NULL;
124 
125     tctx = (thread_ctx_t *)malloc(sizeof(thread_ctx_t));
126 
127     if(!tctx) {
128         return NULL; // error management, bad alloc
129     }
130 
131     int result = 1;
132 
133     // intialize conditional variable and mutexes
134     result = pthread_mutex_init(&tctx->c_section, NULL);
135     if(result) {
136         goto TERROR; // error handling
137     }
138     result = pthread_cond_init(&tctx->w_event, NULL);
139     if(result) {
140         goto TERROR;
141     }
142     result = pthread_cond_init(&tctx->r_event, NULL);
143     if(result) {
144         goto TERROR;
145     }
146 
147     // initialize the worker thread attribute and set the type to joinable
148     result = pthread_attr_init(&tctx->tAttribute);
149     if(result) {
150         goto TERROR;
151     }
152 
153     result = pthread_attr_setdetachstate(&tctx->tAttribute, PTHREAD_CREATE_JOINABLE);
154     if(result) {
155         goto TERROR;
156     }
157 
158     tctx->task = NULL;
159     tctx->t_arg = NULL;
160     tctx->t_status = TPOOL_SUSPENDED;
161     tctx->t_result = TPOOL_INVALID_STATE;
162     tctx->thread_id = thread_id;
163 
164     // create the worker thread
165     result = pthread_create(&tctx->t_handle, &tctx->tAttribute, tpool_worker_thread, (void *)(tctx));
166     if(result) {
167         goto TERROR;
168     }
169 
170     // deinit the attribue
171     pthread_attr_destroy(&tctx->tAttribute);
172     return (oapv_thread_t)tctx;
173 
174 TERROR:
175     pthread_mutex_destroy(&tctx->c_section);
176     pthread_cond_destroy(&tctx->w_event);
177     pthread_cond_destroy(&tctx->r_event);
178     pthread_attr_destroy(&tctx->tAttribute);
179     free(tctx);
180 
181     return NULL; // error handling, can't create a worker thread with proper initialization
182 }
183 
tpool_assign_task(oapv_thread_t thread_id,oapv_fn_thread_entry_t entry,void * arg)184 static tpool_result_t tpool_assign_task(oapv_thread_t thread_id, oapv_fn_thread_entry_t entry, void *arg)
185 {
186     // assign the task function and argument
187     // worker thread may be in running state or suspended state
188     // if worker thread is in suspended state, it can be waiting for first run or it has finished one task and is waiting again
189     // if worker thread is in running state, it will come to waiting state
190     // in any case, waiting on read event will always work
191 
192     thread_ctx_t *tctx = (thread_ctx_t *)(thread_id);
193     if(!tctx) {
194         return TPOOL_INVALID_ARG;
195     }
196 
197     // lock the mutex and wait on read event
198     pthread_mutex_lock(&tctx->c_section);
199     while(tctx->t_status == TPOOL_RUNNING) {
200         pthread_cond_wait(&tctx->r_event, &tctx->c_section);
201     }
202 
203     // thread is in suspended state
204     tctx->t_status = TPOOL_RUNNING;
205     tctx->task = entry;
206     tctx->t_arg = arg;
207     // signal the worker thread to wake up and run the task
208     pthread_cond_signal(&tctx->w_event);
209     pthread_mutex_unlock(&tctx->c_section); // release the lock
210 
211     return TPOOL_SUCCESS;
212 }
213 
tpool_retrieve_result(oapv_thread_t thread_id,int * ret)214 static tpool_result_t tpool_retrieve_result(oapv_thread_t thread_id, int *ret)
215 {
216     // whatever task has been assigned to worker thread
217     // wait for it to finish get the result
218 
219     thread_ctx_t *t_context = (thread_ctx_t *)(thread_id);
220     if(!t_context) {
221         return TPOOL_INVALID_ARG;
222     }
223 
224     tpool_result_t result = TPOOL_SUCCESS;
225 
226     pthread_mutex_lock(&t_context->c_section);
227     while(TPOOL_RUNNING == t_context->t_status) {
228         pthread_cond_wait(&t_context->r_event, &t_context->c_section);
229     }
230     result = t_context->t_result;
231     if(ret != NULL)
232         *ret = t_context->task_ret;
233     pthread_mutex_unlock(&t_context->c_section);
234     return result;
235 }
236 
tpool_terminate_thread(oapv_thread_t * thread_id)237 static tpool_result_t tpool_terminate_thread(oapv_thread_t *thread_id)
238 {
239     // handler to close the thread
240     // close the thread handle
241     // release all the resource
242     //  delete the thread context object
243 
244     thread_ctx_t *t_context = (thread_ctx_t *)(*thread_id);
245     if(!t_context) {
246         return TPOOL_INVALID_ARG;
247     }
248 
249     // The worker thread might be in suspended state or may be processing a task
250     pthread_mutex_lock(&t_context->c_section);
251     while(TPOOL_RUNNING == t_context->t_status) {
252         pthread_cond_wait(&t_context->r_event, &t_context->c_section);
253     }
254 
255     t_context->t_status = TPOOL_TERMINATED;
256     pthread_cond_signal(&t_context->w_event);
257 
258     pthread_mutex_unlock(&t_context->c_section);
259 
260     // join the worker thread
261     pthread_join(t_context->t_handle, NULL);
262 
263     // clean all the synchronization memebers
264     pthread_mutex_destroy(&t_context->c_section);
265     pthread_cond_destroy(&t_context->w_event);
266     pthread_cond_destroy(&t_context->r_event);
267 
268     // delete the thread context memory
269     free(t_context);
270     (*thread_id) = NULL;
271     return TPOOL_SUCCESS;
272 }
273 
tpool_threadsafe_decrement(oapv_sync_obj_t sobj,volatile int * pcnt)274 static int tpool_threadsafe_decrement(oapv_sync_obj_t sobj, volatile int *pcnt)
275 {
276     thread_mutex_t *imutex = (thread_mutex_t *)(sobj);
277     int             temp = 0;
278 
279     // lock the mutex, decrement the count and release the mutex
280     pthread_mutex_lock(&imutex->lmutex);
281     temp = *pcnt;
282     *pcnt = --temp;
283     pthread_mutex_unlock(&imutex->lmutex);
284 
285     return temp;
286 }
287 
oapv_tpool_sync_obj_create()288 oapv_sync_obj_t oapv_tpool_sync_obj_create()
289 {
290     thread_mutex_t *imutex = (thread_mutex_t *)malloc(sizeof(thread_mutex_t));
291     if(0 == imutex) {
292         return 0; // failure case
293     }
294 
295     // intialize the mutex
296     int result = pthread_mutex_init(&imutex->lmutex, NULL);
297     if(result) {
298         if(imutex) {
299             free(imutex);
300         }
301         imutex = 0;
302     }
303 
304     return imutex;
305 }
306 
oapv_tpool_sync_obj_delete(oapv_sync_obj_t * sobj)307 tpool_result_t oapv_tpool_sync_obj_delete(oapv_sync_obj_t *sobj)
308 {
309 
310     thread_mutex_t *imutex = (thread_mutex_t *)(*sobj);
311 
312     // delete the mutex
313     pthread_mutex_destroy(&imutex->lmutex);
314 
315     // free the memory
316     free(imutex);
317     *sobj = NULL;
318 
319     return TPOOL_SUCCESS;
320 }
321 
oapv_tpool_enter_cs(oapv_sync_obj_t sobj)322 void oapv_tpool_enter_cs(oapv_sync_obj_t sobj)
323 {
324     thread_mutex_t *imutex = (thread_mutex_t *)(sobj);
325     pthread_mutex_lock(&imutex->lmutex);
326 }
327 
oapv_tpool_leave_cs(oapv_sync_obj_t sobj)328 void oapv_tpool_leave_cs(oapv_sync_obj_t sobj)
329 {
330     thread_mutex_t *imutex = (thread_mutex_t *)(sobj);
331     pthread_mutex_unlock(&imutex->lmutex);
332 }
333 
334 #else
335 typedef struct thread_ctx {
336     // synchronization members
337     HANDLE                 t_handle;  // worker thread handle
338     HANDLE                 w_event;   // worker thread waiting event handle
339     HANDLE                 r_event;   // signalling thread read event handle
340     CRITICAL_SECTION       c_section; // critical section for fast synchronization
341 
342     // member field to run  a task
343     oapv_fn_thread_entry_t task;
344     void                  *t_arg;
345     tpool_status_t         t_status;
346     tpool_result_t         t_result;
347     int                    task_ret;
348     int                    thread_id;
349 
350 } thread_ctx_t;
351 
352 typedef struct thread_mutex {
353 #if WINDOWS_MUTEX_SYNC
354     HANDLE lmutex;
355 #else
356     CRITICAL_SECTION c_section; // critical section for fast synchronization
357 #endif
358 
359 } thread_mutex_t;
360 
tpool_worker_thread(void * arg)361 static unsigned int __stdcall tpool_worker_thread(void *arg)
362 {
363     /********************* main routine for thread pool worker thread *************************
364     ********************** worker thread can remain in suspended or running state *************
365     ********************* control the synchronization with help of thread context members *****/
366 
367     // member Initialization section
368     thread_ctx_t *t_context = (thread_ctx_t *)arg;
369     if(!t_context) {
370         return 0; // error handling, more like a fail safe mechanism
371     }
372 
373     while(1) {
374         // worker thread loop
375         // remains suspended/sleep waiting for an event
376         WaitForSingleObject(t_context->w_event, INFINITE);
377 
378         // worker thread has received the event to wake up and perform operation
379         EnterCriticalSection(&t_context->c_section);
380         if(t_context->t_status == TPOOL_TERMINATED) {
381             // received signal to terminate
382             t_context->t_result = TPOOL_SUCCESS;
383             LeaveCriticalSection(&t_context->c_section);
384             break;
385         }
386         LeaveCriticalSection(&t_context->c_section);
387 
388         // worker thread state is running with entry function and arg set
389         t_context->task_ret = t_context->task(t_context->t_arg);
390 
391         // change the state to suspended/waiting
392         EnterCriticalSection(&t_context->c_section);
393         t_context->t_status = TPOOL_SUSPENDED;
394         t_context->t_result = TPOOL_SUCCESS;
395         LeaveCriticalSection(&t_context->c_section);
396 
397         // send an event to thread, waiting for it to finish it's task
398         SetEvent(t_context->r_event);
399     }
400 
401     return 0;
402 }
403 
tpool_create_thread(oapv_tpool_t * tp,int thread_id)404 static oapv_thread_t tpool_create_thread(oapv_tpool_t *tp, int thread_id)
405 {
406     if(!tp) {
407         return NULL; // error management
408     }
409 
410     thread_ctx_t *thread_context = NULL;
411     thread_context = (thread_ctx_t *)malloc(sizeof(thread_ctx_t));
412 
413     if(!thread_context) {
414         return NULL; // error management, bad alloc
415     }
416 
417     // create waiting event
418     // create waiting event as automatic reset, only one thread can come out of waiting state
419     // done intentionally ... signally happens from different thread and only worker thread should be able to respond
420     thread_context->w_event = CreateEvent(NULL, FALSE, FALSE, NULL);
421     if(!thread_context->w_event) {
422         goto TERROR; // error handling, can't create event handler
423     }
424 
425     thread_context->r_event = CreateEvent(NULL, TRUE, TRUE, NULL); // read event is enabled by default
426     if(!thread_context->r_event) {
427         goto TERROR;
428     }
429 
430     InitializeCriticalSection(&(thread_context->c_section)); // This section for fast data retrieval
431 
432     // intialize the state variables for the thread context object
433     thread_context->task = NULL;
434     thread_context->t_arg = NULL;
435     thread_context->t_status = TPOOL_SUSPENDED;
436     thread_context->t_result = TPOOL_INVALID_STATE;
437     thread_context->thread_id = thread_id;
438 
439     thread_context->t_handle = (HANDLE)_beginthreadex(NULL, 0, tpool_worker_thread, (void *)thread_context, 0, NULL); // create a thread store the handle and pass the handle to context
440     if(!thread_context->t_handle) {
441         goto TERROR;
442     }
443 
444     // Everything created and intialized properly
445     // return the created thread_context;
446     return (oapv_thread_t)thread_context;
447 
448 TERROR:
449     if(thread_context->w_event) {
450         CloseHandle(thread_context->w_event);
451     }
452     if(thread_context->r_event) {
453         CloseHandle(thread_context->r_event);
454     }
455     DeleteCriticalSection(&thread_context->c_section);
456     if(thread_context) {
457         free(thread_context);
458     }
459 
460     return NULL; // error handling, can't create a worker thread with proper initialization
461 }
462 
tpool_assign_task(oapv_thread_t thread_id,oapv_fn_thread_entry_t entry,void * arg)463 static tpool_result_t tpool_assign_task(oapv_thread_t thread_id, oapv_fn_thread_entry_t entry, void *arg)
464 {
465     // assign the task function and argument
466     // worker thread may be in running state or suspended state
467     // if worker thread is in suspended state, it can be waiting for first run or it has finished one task and is waiting again
468     // if worker thread is in running state, it will come to waiting state
469     // in any case, waiting on read event will always work
470 
471     thread_ctx_t *t_context = (thread_ctx_t *)(thread_id);
472     if(!t_context) {
473         return TPOOL_INVALID_ARG;
474     }
475 
476     WaitForSingleObject(t_context->r_event, INFINITE);
477 
478     // worker thread is in waiting state
479     EnterCriticalSection(&t_context->c_section);
480     t_context->t_status = TPOOL_RUNNING;
481     t_context->task = entry;
482     t_context->t_arg = arg;
483     // signal the worker thread to wake up and run the task
484     ResetEvent(t_context->r_event);
485     SetEvent(t_context->w_event);
486     LeaveCriticalSection(&t_context->c_section);
487 
488     return TPOOL_SUCCESS;
489 }
490 
tpool_retrieve_result(oapv_thread_t thread_id,int * ret)491 static tpool_result_t tpool_retrieve_result(oapv_thread_t thread_id, int *ret)
492 {
493     // whatever task has been assigned to worker thread
494     // wait for it to finish get the result
495     thread_ctx_t *t_context = (thread_ctx_t *)(thread_id);
496     if(!t_context) {
497         return TPOOL_INVALID_ARG;
498     }
499 
500     tpool_result_t result = TPOOL_SUCCESS;
501 
502     WaitForSingleObject(t_context->r_event, INFINITE);
503 
504     // worker thread has finished it's job and now it is in waiting state
505     EnterCriticalSection(&t_context->c_section);
506     result = t_context->t_result;
507     if(ret != NULL)
508         *ret = t_context->task_ret;
509     LeaveCriticalSection(&t_context->c_section);
510 
511     return result;
512 }
513 
tpool_terminate_thread(oapv_thread_t * thread_id)514 tpool_result_t tpool_terminate_thread(oapv_thread_t *thread_id)
515 {
516     // handler to close the thread
517     // close the thread handle
518     // release all the resource
519     //  delete the thread context object
520 
521     // the thread may be running or it is in suspended state
522     // if it is in suspended state, read event will be active
523     // if it is in running state, read event will be active after sometime
524 
525     thread_ctx_t *t_context = (thread_ctx_t *)(*thread_id);
526     if(!t_context) {
527         return TPOOL_INVALID_ARG;
528     }
529 
530     WaitForSingleObject(t_context->r_event, INFINITE);
531 
532     // worker thread is in waiting state
533     EnterCriticalSection(&t_context->c_section);
534     t_context->t_status = TPOOL_TERMINATED;
535     LeaveCriticalSection(&t_context->c_section);
536 
537     // signal the worker thread to wake up and run the task
538     SetEvent(t_context->w_event);
539 
540     // wait for worker thread to finish it's routine
541     WaitForSingleObject(t_context->t_handle, INFINITE);
542     CloseHandle(t_context->t_handle); // freed all the resources for the thread
543 
544     CloseHandle(t_context->w_event);
545     CloseHandle(t_context->r_event);
546     DeleteCriticalSection(&t_context->c_section);
547 
548     // delete the thread context memory
549     free(t_context);
550     (*thread_id) = NULL;
551 
552     return TPOOL_SUCCESS;
553 }
554 
tpool_threadsafe_decrement(oapv_sync_obj_t sobj,volatile int * pcnt)555 static int tpool_threadsafe_decrement(oapv_sync_obj_t sobj, volatile int *pcnt)
556 {
557     thread_mutex_t *imutex = (thread_mutex_t *)(sobj);
558     int             temp = 0;
559 
560 #if WINDOWS_MUTEX_SYNC
561     // let's lock the mutex
562     DWORD dw_wait_result = WaitForSingleObject(imutex->lmutex, INFINITE); // wait for infinite time
563 
564     switch(dw_wait_result) {
565         // The thread got ownership of the mutex
566     case WAIT_OBJECT_0:
567         temp = *pcnt;
568         *pcnt = --temp;
569         // Release ownership of the mutex object
570         ReleaseMutex(imutex->lmutex);
571         break;
572         // The thread got ownership of an abandoned mutex
573         // The database is in an indeterminate state
574     case WAIT_ABANDONED:
575         temp = *pcnt;
576         temp--;
577         *pcnt = temp;
578         break;
579     }
580 #else
581     EnterCriticalSection(&imutex->c_section);
582     temp = *pcnt;
583     *pcnt = --temp;
584     LeaveCriticalSection(&imutex->c_section);
585 #endif
586     return temp;
587 }
588 
oapv_tpool_sync_obj_create()589 oapv_sync_obj_t oapv_tpool_sync_obj_create()
590 {
591     thread_mutex_t *imutex = (thread_mutex_t *)malloc(sizeof(thread_mutex_t));
592     if(0 == imutex) {
593         return 0; // failure case
594     }
595 
596 #if WINDOWS_MUTEX_SYNC
597     // initialize the created mutex instance
598     imutex->lmutex = CreateMutex(NULL, FALSE, NULL);
599     if(0 == imutex->lmutex) {
600         if(imutex) {
601             free(imutex);
602         }
603         return 0;
604     }
605 #else
606     // initialize the critical section
607     InitializeCriticalSection(&(imutex->c_section));
608 #endif
609     return imutex;
610 }
611 
oapv_tpool_sync_obj_delete(oapv_sync_obj_t * sobj)612 tpool_result_t oapv_tpool_sync_obj_delete(oapv_sync_obj_t *sobj)
613 {
614     thread_mutex_t *imutex = (thread_mutex_t *)(*sobj);
615 #if WINDOWS_MUTEX_SYNC
616     // release the mutex
617     CloseHandle(imutex->lmutex);
618 #else
619     // delete critical section
620     DeleteCriticalSection(&imutex->c_section);
621 #endif
622 
623     // free the memory
624     free(imutex);
625     *sobj = NULL;
626 
627     return TPOOL_SUCCESS;
628 }
629 
oapv_tpool_enter_cs(oapv_sync_obj_t sobj)630 void oapv_tpool_enter_cs(oapv_sync_obj_t sobj)
631 {
632     thread_mutex_t *imutex = (thread_mutex_t *)(sobj);
633     EnterCriticalSection(&imutex->c_section);
634 }
oapv_tpool_leave_cs(oapv_sync_obj_t sobj)635 void oapv_tpool_leave_cs(oapv_sync_obj_t sobj)
636 {
637     thread_mutex_t *imutex = (thread_mutex_t *)(sobj);
638     LeaveCriticalSection(&imutex->c_section);
639 }
640 
641 #endif
642 
oapv_tpool_init(oapv_tpool_t * tp,int maxtask)643 tpool_result_t oapv_tpool_init(oapv_tpool_t *tp, int maxtask)
644 {
645     // assign handles to threadcontroller object
646     // handles for create, run, join and terminate will be given to controller  object
647 
648     tp->create = tpool_create_thread;
649     tp->run = tpool_assign_task;
650     tp->join = tpool_retrieve_result;
651     tp->release = tpool_terminate_thread;
652     tp->max_task_cnt = maxtask;
653 
654     return TPOOL_SUCCESS;
655 }
656 
oapv_tpool_deinit(oapv_tpool_t * tp)657 tpool_result_t oapv_tpool_deinit(oapv_tpool_t *tp)
658 {
659     // reset all the handler to NULL
660     tp->create = NULL;
661     tp->run = NULL;
662     tp->join = NULL;
663     tp->release = NULL;
664     tp->max_task_cnt = 0;
665 
666     return TPOOL_SUCCESS;
667 }
668 
oapv_tpool_spinlock_wait(volatile int * addr,int val)669 int oapv_tpool_spinlock_wait(volatile int *addr, int val)
670 {
671     int temp;
672 
673     while(1) {
674         temp = *addr; // thread safe volatile read
675         if(temp == val || temp == -1) {
676             break;
677         }
678     }
679     return temp;
680 }
681 
threadsafe_assign(volatile int * addr,int val)682 void threadsafe_assign(volatile int *addr, int val)
683 {
684     // thread safe volatile assign
685     *addr = val;
686 }
687