blob: 601466324a12bc0054f9a8869f4ee4b3ffb60b03
1 | /* |
2 | * Copyright (c) 2014 Amlogic, Inc. All rights reserved. |
3 | * |
4 | * This source code is subject to the terms and conditions defined in the |
5 | * file 'LICENSE' which is part of this source code package. |
6 | * |
7 | * Description: |
8 | */ |
9 | |
10 | |
11 | #include <amthreadpool.h> |
12 | #include <itemlist.h> |
13 | |
14 | #define LOG_TAG "amthreadpool" |
15 | #include <utils/Log.h> |
16 | |
17 | #include <string.h> |
18 | #include <stdlib.h> |
19 | #include <fcntl.h> |
20 | |
21 | |
22 | static struct itemlist threadpool_list; |
23 | static struct itemlist threadpool_threadlist; |
24 | #define MAX_THREAD_DEPTH 8 |
25 | |
26 | #define T_ASSERT_TRUE(x)\ |
27 | do {\ |
28 | if (!(x))\ |
29 | ALOGE("amthreadpool error at %d\n",__LINE__);\ |
30 | } while(0) |
31 | |
32 | #define T_ASSERT_NO_NULL(p) T_ASSERT_TRUE((p)!=NULL) |
33 | |
34 | typedef struct threadpool { |
35 | pthread_t pid; |
36 | struct itemlist threadlist; |
37 | } threadpool_t; |
38 | |
39 | typedef struct threadpool_thread_data { |
40 | pthread_t pid; |
41 | void * (*start_routine)(void *); |
42 | void * arg; |
43 | pthread_t ppid[MAX_THREAD_DEPTH]; |
44 | threadpool_t *pool; |
45 | pthread_mutex_t pthread_mutex; |
46 | pthread_cond_t pthread_cond; |
47 | int on_requred_exit; |
48 | int thread_inited; |
49 | } threadpool_thread_data_t; |
50 | #define POOL_OF_ITEM(item) ((threadpool_t *)(item)->extdata[0]) |
51 | #define THREAD_OF_ITEM(item) ((threadpool_thread_data_t *)(item)->extdata[0]) |
52 | |
53 | static int amthreadpool_release(pthread_t pid); |
54 | static threadpool_t * amthreadpool_create_pool(pthread_t pid); |
55 | |
56 | static threadpool_t * amthreadpool_findthead_pool(pthread_t pid) |
57 | { |
58 | struct item *item; |
59 | item = itemlist_find_match_item(&threadpool_list, pid); |
60 | if (item) { |
61 | return (threadpool_t *)item->extdata[0]; |
62 | } |
63 | return NULL; |
64 | } |
65 | |
66 | static threadpool_thread_data_t * amthreadpool_findthead_thread_data(pthread_t pid) |
67 | { |
68 | struct item *item; |
69 | item = itemlist_find_match_item(&threadpool_threadlist, pid); |
70 | if (item) { |
71 | return (threadpool_thread_data_t *)item->extdata[0]; |
72 | } |
73 | return NULL; |
74 | } |
75 | |
76 | /*creat thread pool for main thread*/ |
77 | static threadpool_t * amthreadpool_create_pool(pthread_t pid) |
78 | { |
79 | struct item *poolitem; |
80 | threadpool_t *pool; |
81 | int ret = -1; |
82 | unsigned long exdata[2]; |
83 | pool = malloc(sizeof(threadpool_t)); |
84 | if (!pool) { |
85 | ALOGE("malloc pool data failed\n"); |
86 | return NULL; |
87 | } |
88 | memset(pool, 0, sizeof(threadpool_t)); |
89 | pool->pid = pid; |
90 | if (pid == 0) { |
91 | pool->pid = pthread_self(); |
92 | } |
93 | pool->threadlist.max_items = 0; |
94 | pool->threadlist.item_ext_buf_size = 0; |
95 | pool->threadlist.muti_threads_access = 1; |
96 | pool->threadlist.reject_same_item_data = 1; |
97 | itemlist_init(&pool->threadlist); |
98 | exdata[0] = (unsigned long)pool; |
99 | itemlist_add_tail_data_ext(&threadpool_list, pool->pid, 1, exdata); |
100 | |
101 | return pool; |
102 | } |
103 | |
104 | static int amthreadpool_pool_add_thread(threadpool_t *pool, unsigned long pid, threadpool_thread_data_t* thread) |
105 | { |
106 | int ret; |
107 | unsigned long exdata[2]; |
108 | exdata[0] = (unsigned long)thread; |
109 | if (pool) { |
110 | ret = itemlist_add_tail_data_ext(&pool->threadlist, thread->pid, 1, exdata); |
111 | } else { |
112 | pool = amthreadpool_create_pool(pid); |
113 | thread->pool = pool; |
114 | } |
115 | ret |= itemlist_add_tail_data_ext(&threadpool_threadlist, thread->pid, 1, exdata); |
116 | return ret; |
117 | } |
118 | static int amthreadpool_pool_del_thread(pthread_t pid) |
119 | { |
120 | threadpool_t *pool; |
121 | threadpool_thread_data_t* t1, *t2; |
122 | int ret = 0; |
123 | struct item *item; |
124 | item = itemlist_get_match_item(&threadpool_threadlist, pid); |
125 | if (!item) { |
126 | return -2; /*freed before*/ |
127 | } |
128 | t1 = THREAD_OF_ITEM(item); |
129 | item_free(item); |
130 | T_ASSERT_NO_NULL(t1); |
131 | pool = t1->pool; |
132 | T_ASSERT_NO_NULL(pool); |
133 | item = itemlist_get_match_item(&pool->threadlist, pid); |
134 | if (item) { |
135 | T_ASSERT_NO_NULL(item); |
136 | t2 = THREAD_OF_ITEM(item); |
137 | T_ASSERT_NO_NULL(t2); |
138 | if (t1 != t2) { |
139 | ALOGE("%d thread data not mached, %p!=%p\n", (int)pid, t1, t2); |
140 | } |
141 | item_free(item); |
142 | } |
143 | pthread_cond_destroy(&t1->pthread_cond); |
144 | pthread_mutex_destroy(&t1->pthread_mutex); |
145 | free(t1); |
146 | if (pool->pid == pid) { |
147 | amthreadpool_release(pid); |
148 | } |
149 | return ret; |
150 | } |
151 | |
152 | |
153 | static int amthreadpool_thread_wake_t(threadpool_thread_data_t*t, int trycancel) |
154 | { |
155 | int ret; |
156 | pthread_mutex_lock(&t->pthread_mutex); |
157 | t->on_requred_exit = trycancel; |
158 | ret = pthread_cond_signal(&t->pthread_cond); |
159 | pthread_mutex_unlock(&t->pthread_mutex); |
160 | return ret; |
161 | } |
162 | static int64_t amthreadpool_gettime(void) |
163 | { |
164 | struct timeval tv; |
165 | gettimeofday(&tv, NULL); |
166 | return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; |
167 | } |
168 | |
169 | int amthreadpool_thread_usleep_in_monotonic(int us) |
170 | { |
171 | pthread_t pid = pthread_self(); |
172 | struct timespec pthread_ts, tnow; |
173 | int64_t us64 = us; |
174 | threadpool_thread_data_t *t = amthreadpool_findthead_thread_data(pid); |
175 | int ret = 0; |
176 | /* 64bit compiler do not have pthread_cond_timedwait_monotonic_np */ |
177 | #ifndef __aarch64__ |
178 | if (!t) { |
179 | ///ALOGE("%lu thread sleep data not found!!!\n", pid); |
180 | usleep(us);//for not deadlock. |
181 | return 0; |
182 | } |
183 | if (t->on_requred_exit > 1) { |
184 | if (us64 < 100 * 1000) { |
185 | us64 = 100 * 1000; |
186 | } |
187 | t->on_requred_exit--; /*if on_requred_exit,do less sleep till 1.*/ |
188 | } |
189 | clock_gettime(CLOCK_MONOTONIC, &tnow); |
190 | pthread_ts.tv_sec = tnow.tv_sec + (us64 + tnow.tv_nsec / 1000) / 1000000; |
191 | pthread_ts.tv_nsec = (us64 * 1000 + tnow.tv_nsec) % 1000000000; |
192 | pthread_mutex_lock(&t->pthread_mutex); |
193 | ret = pthread_cond_timedwait_monotonic_np(&t->pthread_cond, &t->pthread_mutex, &pthread_ts); |
194 | |
195 | pthread_mutex_unlock(&t->pthread_mutex); |
196 | return ret; |
197 | #else |
198 | usleep(us);//for not deadlock. |
199 | return 0; |
200 | #endif |
201 | |
202 | } |
203 | |
204 | |
205 | int amthreadpool_thread_usleep_in(int us) |
206 | { |
207 | pthread_t pid = pthread_self(); |
208 | struct timespec pthread_ts; |
209 | struct timeval now; |
210 | int64_t us64 = us; |
211 | threadpool_thread_data_t *t = amthreadpool_findthead_thread_data(pid); |
212 | int ret = 0; |
213 | |
214 | if (!t) { |
215 | ///ALOGE("%lu thread sleep data not found!!!\n", pid); |
216 | usleep(us);//for not deadlock. |
217 | return 0; |
218 | } |
219 | if (t->on_requred_exit > 1) { |
220 | if (us64 < 100 * 1000) { |
221 | us64 = 100 * 1000; |
222 | } |
223 | t->on_requred_exit--; /*if on_requred_exit,do less sleep till 1.*/ |
224 | } |
225 | ret = gettimeofday(&now, NULL); |
226 | pthread_ts.tv_sec = now.tv_sec + (us64 + now.tv_usec) / 1000000; |
227 | pthread_ts.tv_nsec = ((us64 + now.tv_usec) * 1000) % 1000000000; |
228 | pthread_mutex_lock(&t->pthread_mutex); |
229 | ret = pthread_cond_timedwait(&t->pthread_cond, &t->pthread_mutex, &pthread_ts); |
230 | pthread_mutex_unlock(&t->pthread_mutex); |
231 | return ret; |
232 | } |
233 | int amthreadpool_thread_usleep_debug(int us, const char *func, int line) |
234 | { |
235 | |
236 | int64_t starttime = amthreadpool_gettime(); |
237 | int64_t endtime; |
238 | int ret; |
239 | |
240 | #ifdef AMTHREADPOOL_SLEEP_US_MONOTONIC |
241 | ret = amthreadpool_thread_usleep_in_monotonic(us); |
242 | #else |
243 | ret = amthreadpool_thread_usleep_in(us); |
244 | #endif |
245 | endtime = amthreadpool_gettime(); |
246 | if ((endtime - starttime - us) > 100 * 1000) { |
247 | ALOGE("***amthreadpool_thread_usleep wast more time wait %d us, real %lld us\n", us, (int64_t)(endtime - starttime)); |
248 | } |
249 | return ret; |
250 | } |
251 | |
252 | int amthreadpool_thread_wake(pthread_t pid) |
253 | { |
254 | threadpool_thread_data_t *t = amthreadpool_findthead_thread_data(pid); |
255 | if (!t) { |
256 | ALOGE("%lu wake thread data not found!!!\n", pid); |
257 | return -1; |
258 | } |
259 | return amthreadpool_thread_wake_t(t, t->on_requred_exit); |
260 | } |
261 | int amthreadpool_on_requare_exit(pthread_t pid) |
262 | { |
263 | unsigned long rpid = pid != 0 ? pid : pthread_self(); |
264 | threadpool_thread_data_t *t = amthreadpool_findthead_thread_data(rpid); |
265 | if (!t) { |
266 | return 0; |
267 | } |
268 | if (t->on_requred_exit) { |
269 | ///ALOGI("%lu name on try exit.\n", pid); |
270 | } |
271 | return !!t->on_requred_exit; |
272 | } |
273 | |
274 | static int amthreadpool_pool_thread_cancel_l1(pthread_t pid, int cancel, int allthreads) |
275 | { |
276 | struct itemlist *itemlist; |
277 | threadpool_thread_data_t *t, *t1; |
278 | struct item *item = NULL; |
279 | threadpool_t *pool; |
280 | t = amthreadpool_findthead_thread_data(pid); |
281 | if (!t) { |
282 | ALOGE("%lu pool data not found!!!\n", pid); |
283 | return 0; |
284 | } |
285 | pool = t->pool; |
286 | if (allthreads && pool && pool->pid == pid) { |
287 | itemlist = &pool->threadlist; |
288 | FOR_EACH_ITEM_IN_ITEMLIST(itemlist, item) |
289 | t1 = THREAD_OF_ITEM(item); |
290 | amthreadpool_thread_wake_t(t1, cancel); |
291 | FOR_ITEM_END(itemlist); |
292 | } |
293 | amthreadpool_thread_wake_t(t, cancel); |
294 | ///amthreadpool_system_dump_info(); |
295 | return 0; |
296 | } |
297 | |
298 | int amthreadpool_pool_thread_cancel(pthread_t pid) |
299 | { |
300 | return amthreadpool_pool_thread_cancel_l1(pid, 3, 1); |
301 | } |
302 | |
303 | int amthreadpool_pool_thread_uncancel(pthread_t pid) |
304 | { |
305 | return amthreadpool_pool_thread_cancel_l1(pid, 0, 1); |
306 | } |
307 | int amthreadpool_thread_cancel(pthread_t pid) |
308 | { |
309 | return amthreadpool_pool_thread_cancel_l1(pid, 3, 0); |
310 | } |
311 | int amthreadpool_thread_uncancel(pthread_t pid) |
312 | { |
313 | return amthreadpool_pool_thread_cancel_l1(pid, 0, 0); |
314 | } |
315 | |
316 | static int amthreadpool_release(pthread_t pid) |
317 | { |
318 | struct item *poolitem; |
319 | threadpool_t *pool; |
320 | poolitem = itemlist_get_match_item(&threadpool_list, pid); |
321 | if (poolitem) { |
322 | pool = POOL_OF_ITEM(poolitem); |
323 | itemlist_deinit(&pool->threadlist); |
324 | free((void *)pool); |
325 | item_free(poolitem); |
326 | } |
327 | return 0; |
328 | } |
329 | |
330 | |
331 | void * amthreadpool_start_thread(void *arg) |
332 | { |
333 | void *ret; |
334 | threadpool_thread_data_t *t = (threadpool_thread_data_t *)arg; |
335 | { |
336 | threadpool_thread_data_t *thread_p; |
337 | threadpool_t *pool = NULL; |
338 | int i; |
339 | t->pid = pthread_self(); |
340 | thread_p = amthreadpool_findthead_thread_data(t->ppid[0]); |
341 | if (thread_p) { |
342 | pool = thread_p->pool; |
343 | for (i = 0; i < MAX_THREAD_DEPTH - 1; i++) { |
344 | if (!thread_p->ppid[i]) { |
345 | break; |
346 | } |
347 | t->ppid[i + 1] = thread_p->ppid[i]; |
348 | } |
349 | t->pool = pool; |
350 | |
351 | } |
352 | amthreadpool_pool_add_thread(pool, t->pid, t); |
353 | } |
354 | pthread_mutex_lock(&t->pthread_mutex); |
355 | t->thread_inited = 1; |
356 | pthread_cond_signal(&t->pthread_cond); |
357 | pthread_mutex_unlock(&t->pthread_mutex); |
358 | ret = t->start_routine(t->arg); |
359 | return ret; |
360 | } |
361 | |
362 | int amthreadpool_pthread_create_name(pthread_t * newthread, |
363 | __const pthread_attr_t * attr, |
364 | void * (*start_routine)(void *), |
365 | void * arg, const char *name) |
366 | { |
367 | pthread_t pid = pthread_self(); |
368 | pthread_t subpid; |
369 | int ret; |
370 | |
371 | threadpool_thread_data_t *t = malloc(sizeof(threadpool_thread_data_t)); |
372 | if (!t) { |
373 | ALOGE("malloc threadpool_thread_data_t data failed\n"); |
374 | return -100; |
375 | } |
376 | memset(t, 0, sizeof(threadpool_thread_data_t)); |
377 | t->start_routine = start_routine; |
378 | t->arg = arg; |
379 | t->ppid[0] = pid; |
380 | t->thread_inited = 0; |
381 | pthread_mutex_init(&t->pthread_mutex, NULL); |
382 | pthread_cond_init(&t->pthread_cond, NULL); |
383 | ret = pthread_create(&subpid, attr, amthreadpool_start_thread, (void *)t); |
384 | if (ret == 0) { |
385 | *newthread = subpid; |
386 | if (name) { |
387 | pthread_setname_np(pid, name); |
388 | } |
389 | pthread_mutex_lock(&t->pthread_mutex); |
390 | while (t->thread_inited == 0) |
391 | pthread_cond_wait(&t->pthread_cond, &t->pthread_mutex); |
392 | pthread_mutex_unlock(&t->pthread_mutex); |
393 | } |
394 | return ret; |
395 | } |
396 | |
397 | |
398 | |
399 | int amthreadpool_pthread_create(pthread_t * newthread, |
400 | __const pthread_attr_t * attr, |
401 | void * (*start_routine)(void *), |
402 | void * arg) |
403 | { |
404 | return amthreadpool_pthread_create_name(newthread, attr, start_routine, arg, NULL); |
405 | } |
406 | |
407 | int amthreadpool_pthread_join(pthread_t thid, void ** ret_val) |
408 | { |
409 | int ret; |
410 | ret = pthread_join(thid, ret_val); |
411 | amthreadpool_pool_del_thread(thid); |
412 | return ret; |
413 | } |
414 | |
415 | |
416 | |
417 | |
418 | |
419 | /*creat thread pool system init*/ |
420 | int amthreadpool_system_init(void) |
421 | { |
422 | static int inited = 0; |
423 | if (inited) { |
424 | return 0; |
425 | } |
426 | inited ++; |
427 | threadpool_list.max_items = 0; |
428 | threadpool_list.item_ext_buf_size = 0; |
429 | threadpool_list.muti_threads_access = 1; |
430 | threadpool_list.reject_same_item_data = 1; |
431 | itemlist_init(&threadpool_list); |
432 | |
433 | threadpool_threadlist.max_items = 0; |
434 | threadpool_threadlist.item_ext_buf_size = 0; |
435 | threadpool_threadlist.muti_threads_access = 1; |
436 | threadpool_threadlist.reject_same_item_data = 1; |
437 | itemlist_init(&threadpool_threadlist); |
438 | return 0; |
439 | } |
440 | |
441 | |
442 | int amthreadpool_system_dump_info(void) |
443 | { |
444 | threadpool_thread_data_t *t; |
445 | threadpool_t *pool; |
446 | struct item *item = NULL; |
447 | struct item *item1 = NULL; |
448 | ALOGI("------------amthreadpool_system_dump_info----------START\n"); |
449 | ALOGI("pool & threads:\n"); |
450 | FOR_EACH_ITEM_IN_ITEMLIST(&threadpool_list, item) { |
451 | pool = POOL_OF_ITEM(item); |
452 | ALOGI("pool:%p\n", pool); |
453 | ALOGI("--tpid:%lu\n", pool->pid); |
454 | FOR_EACH_ITEM_IN_ITEMLIST(&pool->threadlist, item1) { |
455 | t = THREAD_OF_ITEM(item1); |
456 | ALOGI("--tpid:%lu\n", t->pid); |
457 | //ALOGI("----name=%p\n",amthreadpool_thread_name(t->pid)); |
458 | ALOGI("----ppid=%lu,%lu,%lu,%lu,%lu", t->ppid[0], t->ppid[1], t->ppid[2], t->ppid[3], t->ppid[4]); |
459 | ALOGI("----pool:%p\n", t->pool); |
460 | ALOGI("----on_requred_exit:%d\n", t->on_requred_exit); |
461 | } |
462 | FOR_ITEM_END(&pool->threadlist); |
463 | } |
464 | FOR_ITEM_END(&threadpool_list); |
465 | ALOGI("all threads:\n"); |
466 | FOR_EACH_ITEM_IN_ITEMLIST(&threadpool_threadlist, item) { |
467 | t = THREAD_OF_ITEM(item); |
468 | ALOGI("--tpid:%lu\n", t->pid); |
469 | //ALOGI("----name=%p\n",amthreadpool_thread_name(t->pid)); |
470 | ALOGI("----ppid=%lu,%lu,%lu,%lu,%lu", t->ppid[0], t->ppid[1], t->ppid[2], t->ppid[3], t->ppid[4]); |
471 | ALOGI("----pool:%p\n", t->pool); |
472 | ALOGI("----on_requred_exit:%d\n", t->on_requred_exit); |
473 | } |
474 | FOR_ITEM_END(&threadpool_threadlist); |
475 | ALOGI("------------amthreadpool_system_dump_info----------END\n"); |
476 | return 0; |
477 | } |
478 |