/** * Add spare threads (called by main thread) * @return added threads number */ static int tpool_add_thread(tpool_t *tp, int num) { TP_LOCK(); if (tp->cur_spare > 0) num = 0; else if (num > (tp->max_total - tp->cur_total)) num = tp->max_total - tp->cur_total; TP_UNLOCK(); // create new threads if (num > 0) { int i, j = 0; // find deactived threads & create them for (i = 0; num > 0 && i < tp->max_total; i++) { if (tp->threads[i].status & TPOOL_THREAD_ACTIVED) continue; if (pthread_create(&tp->threads[i].tid, NULL, tpool_thread_start, &tp->threads[i]) != 0) break; tp->threads[i].status |= TPOOL_THREAD_ACTIVED; j++; num--; debug_printf("Thread[%d] was created (TID:%p)\n", i, tp->threads[i].tid); } // save new number TP_LOCK(); tp->cur_total += j; tp->cur_spare += j; TP_UNLOCK(); debug_printf("Thread pool number status (TOTAL:%d, SPARE:%d)\n", tp->cur_total, tp->cur_spare); return j; } // none of threads created return 0; }
/** * Cancel all idle thread & notify working thread quit after finishing job */ void tpool_do_cancel(tpool_t *tp, int wait) { if (!tp || !(tp->status & TPOOL_STATUS_INITED)) return; debug_printf("send cancel signal to all threads"); tp->status |= TPOOL_STATUS_CANCELED; pthread_cond_broadcast(&tp->cond); if (wait == 1) { debug_printf("waiting for the end of all threads ..."); TP_LOCK(); while (tp->cur_total > 0) TP_WAIT(); TP_UNLOCK(); debug_printf("ok, all threads exited"); } }
/** * Cancel all idle thread & notify working thread quit after finishing job */ void tpool_do_cancel(tpool_t *tp, int wait) { if (!tp || !(tp->status & TPOOL_STATUS_INITED)) return; debug_printf("Send cancel signal to all threads\n"); tp->status |= TPOOL_STATUS_CANCELED; pthread_cond_broadcast(&tp->cond); if (wait == 1) { debug_printf("Waiting for all threads to end...\n"); TP_LOCK(); while (tp->cur_total > 0) TP_WAIT(); TP_UNLOCK(); debug_printf("OK, all work threads exited\n"); } }
/** * Submit task to thread pool */ void tpool_exec(tpool_t *tp, tpool_func_t func, tpool_func_t cancel, void *arg) { struct tpool_task *task; task = (struct tpool_task *) malloc(sizeof(struct tpool_task)); if (task == NULL) { debug_printf("failed to allocate memory for new task (SIZE:%d)", (int) sizeof(struct tpool_task)); return; } memset(task, 0, sizeof(struct tpool_task)); task->task_func = func; task->cancel_func = cancel; task->arg = arg; // save to task list TP_LOCK(); if (tp->task_list == NULL) tp->task_list = task; else { struct tpool_task *tail = tp->task_list; while (tail->next != NULL) tail = tail->next; tail->next = task; } TP_UNLOCK(); debug_printf("add new task to thread pool (SPARE:%d, TOTAL:%d)", tp->cur_spare, tp->cur_total); // check spare threads if (tp->cur_spare == 0) { debug_printf("try to add some new threads (NUM:%d)", tp->min_spare); tpool_add_thread(tp, tp->min_spare); } // notify pthread_cond_signal(&tp->cond); }
/** * Cleanup function called when the thread was canceld during task execution * @param arg struct tpool_thread */ static void tpool_thread_cleanup(void *arg) { struct tpool_thread *me = (struct tpool_thread *) arg; tpool_t *tp = me->tp; debug_printf("thread[%d] is canceled, run cleanup function (TID:%p, TOTAL:%d)", me->index, me->tid, tp->cur_total - 1); // call cancel handler of task if (me->task->cancel_func != NULL) (*me->task->cancel_func)(me->task->arg); // free task free(me->task); me->status = TPOOL_THREAD_NONE; TP_LOCK(); tp->cur_total--; if (me->tp->cur_total == 0) pthread_cond_signal(&tp->cond); TP_UNLOCK(); }
/** * Thread start point */ static void *tpool_thread_start(void *arg) { struct tpool_thread *me = (struct tpool_thread *) arg; tpool_t *tp = me->tp; // init the thread tpool_thread_init(me); // loop to execute task while (1) { // waiting for task TP_LOCK(); tp->cur_spare++; me->status ^= TPOOL_THREAD_BUSY; while ((me->task = tpool_get_task(tp)) == NULL && !TP_CANCELED()) TP_WAIT(); me->status |= TPOOL_THREAD_BUSY; tp->cur_spare--; TP_UNLOCK(); // empty task (cancled) if (me->task == NULL) { TP_LOCK(); me->status = TPOOL_THREAD_NONE; tp->cur_total--; TP_UNLOCK(); debug_printf("thread[%d] get empty task(NULL), forced to cancel (TID:%p, CALLS:%d, TOTAL:%d)", me->index, me->tid, me->calls, tp->cur_total); break; } // task accepted debug_printf("thread[%d] accept new task (TID:%p, FUNC:%p, ARG:%p)", me->index, me->tid, me->task->task_func, me->task->arg); time(&me->task->begin); me->calls++; me->status |= TPOOL_THREAD_TASK; // call task function with cleanup function & cancelstate pthread_cleanup_push(tpool_thread_cleanup, me); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); (*me->task->task_func)(me->task->arg); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_cleanup_pop(0); me->status ^= TPOOL_THREAD_TASK; free(me->task); debug_printf("thread[%d] finished the task (TID:%p, CALLS:%d)", me->index, me->tid, me->calls); // check the number of spare threads if (tp->cur_spare >= tp->max_spare) { TP_LOCK(); me->status = TPOOL_THREAD_NONE; tp->cur_total--; TP_UNLOCK(); debug_printf("thread[%d] suicided due to too many spare threads (TID:%p, SPARE:%d, TOTAL:%d)", me->index, me->tid, tp->cur_spare, tp->cur_total); break; } } // notify the thread that is waiting for canceling if (tp->cur_total == 0) pthread_cond_signal(&tp->cond); return NULL; }