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