/** * This callback function does the following steps: * 1. It raises a flag that notifies the worker threads to stop working. * 2. It waits until all worker threads are done with their execution. * 3. It frees all the allocated memory of the threadpool struct. * * @param ptr The pool to stop its worker threads. * @return 0. */ static void *stop_worker_thr_routines_cb(void *ptr) { int i; struct threadpool *pool = (struct threadpool*)ptr; if (pthread_mutex_lock(&(pool->mutex))) { perror("pthread_mutex_lock: "); REPORT_ERROR("Warning: Memory was not released."); REPORT_ERROR("Warning: Some of the worker threads may have failed to exit."); return NULL; } pool->stop_flag = 1; while (!threadpool_queue_is_empty(&(pool->tasks_queue))) { /* Block until all tasks have been executed. */ if (pthread_cond_wait(&(pool->cond),&(pool->mutex))) { perror("pthread_cond_wait: "); if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); } return NULL; } } /* Wakeup all worker threads (broadcast operation). */ if (pthread_cond_broadcast(&(pool->cond))) { perror("pthread_cond_broadcast: "); REPORT_ERROR("Warning: Memory was not released."); REPORT_ERROR("Warning: Some of the worker threads may have failed to exit."); return NULL; } if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); REPORT_ERROR("Warning: Memory was not released."); REPORT_ERROR("Warning: Some of the worker threads may have failed to exit."); return NULL; } /* Wait until all worker threads are done. */ for (i = 0; i < pool->num_of_threads; i++) { if (pthread_join(pool->thr_arr[i],NULL)) { perror("pthread_join: "); } } /* Free all allocated memory. */ free(pool->thr_arr); free(pool); return NULL; }
static void *stop_worker_thr_routines_cb(void *ptr) { int i; struct threadpool *pool = (struct threadpool*)ptr; if (pthread_mutex_lock(&(pool->mutex))) { perror("pthread_mutex_lock: "); DEMO_THREAD_Print("Warning: Memory was not released."); DEMO_THREAD_Print("Warning: Some of the worker threads may have failed to exit."); return NULL; } pool->stop_flag = 1; while (!threadpool_queue_is_empty(&(pool->tasks_queue))) { if (pthread_cond_wait(&(pool->cond),&(pool->mutex))) { perror("pthread_cond_wait: "); if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); } return NULL; } } /* 唤醒 */ if (pthread_cond_broadcast(&(pool->cond))) { perror("pthread_cond_broadcast: "); DEMO_THREAD_Print("Warning: Memory was not released."); DEMO_THREAD_Print("Warning: Some of the worker threads may have failed to exit."); return NULL; } if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); DEMO_THREAD_Print("Warning: Memory was not released."); DEMO_THREAD_Print("Warning: Some of the worker threads may have failed to exit."); return NULL; } for (i = 0; i < pool->num_of_threads; i++) { if (pthread_join(pool->thr_arr[i],NULL)) { perror("pthread_join: "); } } /* 释放 */ free(pool->thr_arr); free(pool); return NULL; }
/** * This function obtains a queued task from the pool and returns it. * If no such task is available the operation blocks. * * @param pool The thread pool structure. * @return A task or NULL on error (or if thread pool should shut down). */ static struct threadpool_task* threadpool_task_get_task(struct threadpool *pool) { struct threadpool_task* task; /* Obtain a task */ if (pthread_mutex_lock(&(pool->mutex))) { perror("pthread_mutex_lock: "); return NULL; } while (threadpool_queue_is_empty(&(pool->tasks_queue))) { if (pool->stop_flag) { if (pthread_cond_broadcast(&(pool->cond))) { perror("pthread_cond_broadcast: "); } if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); return NULL; } return NULL; } /* Block until a new task arrives. */ if (pthread_cond_wait(&(pool->cond),&(pool->mutex))) { perror("pthread_cond_wait: "); if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); } return NULL; } } if ((task = (struct threadpool_task*)threadpool_queue_dequeue(&(pool->tasks_queue))) == NULL) { /* Since task is NULL returning task will return NULL as required. */ REPORT_ERROR("Failed to obtain a task from the jobs queue."); } if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); return NULL; } return task; }
static struct threadpool_task* threadpool_task_get_task(struct threadpool *pool) { struct threadpool_task* task; if (pthread_mutex_lock(&(pool->mutex))) { perror("pthread_mutex_lock: "); return NULL; } while (threadpool_queue_is_empty(&(pool->tasks_queue))) { if (pool->stop_flag) { if (pthread_cond_broadcast(&(pool->cond))) { perror("pthread_cond_broadcast: "); } if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); return NULL; } return NULL; } if (pthread_cond_wait(&(pool->cond),&(pool->mutex))) { perror("pthread_cond_wait: "); if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); } return NULL; } } if ((task = (struct threadpool_task*)threadpool_queue_dequeue(&(pool->tasks_queue))) == NULL) { DEMO_THREAD_Print("Failed to obtain a task from the jobs queue."); } if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); return NULL; } return task; }
int threadpool_add_task(struct threadpool *pool, void (*routine)(void*), void *data, int blocking) { struct threadpool_task *task; if (pool == NULL) { REPORT_ERROR("The threadpool received as argument is NULL."); return -1; } if (pthread_mutex_lock(&(pool->free_tasks_mutex))) { perror("pthread_mutex_lock: "); return -1; } /* Check if the free task queue is empty. */ while (threadpool_queue_is_empty(&(pool->free_tasks_queue))) { if (!blocking) { /* Return immediately if the command is non blocking. */ if (pthread_mutex_unlock(&(pool->free_tasks_mutex))) { perror("pthread_mutex_unlock: "); return -1; } return -2; } /* blocking is set to 1, wait until free_tasks queue has a task to obtain. */ if (pthread_cond_wait(&(pool->free_tasks_cond),&(pool->free_tasks_mutex))) { perror("pthread_cond_wait: "); if (pthread_mutex_unlock(&(pool->free_tasks_mutex))) { perror("pthread_mutex_unlock: "); } return -1; } } /* Obtain an empty task. */ if ((task = (struct threadpool_task*)threadpool_queue_dequeue(&(pool->free_tasks_queue))) == NULL) { REPORT_ERROR("Failed to obtain an empty task from the free tasks queue."); if (pthread_mutex_unlock(&(pool->free_tasks_mutex))) { perror("pthread_mutex_unlock: "); } return -1; } if (pthread_mutex_unlock(&(pool->free_tasks_mutex))) { perror("pthread_mutex_unlock: "); return -1; } task->data = data; task->routine_cb = routine; /* Add the task, to the tasks queue. */ if (pthread_mutex_lock(&(pool->mutex))) { perror("pthread_mutex_lock: "); return -1; } if (threadpool_queue_enqueue(&(pool->tasks_queue),task)) { REPORT_ERROR("Failed to add a new task to the tasks queue."); if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); } return -1; } if (threadpool_queue_getsize(&(pool->tasks_queue)) == 1) { /* Notify all worker threads that there are new jobs. */ if (pthread_cond_broadcast(&(pool->cond))) { perror("pthread_cond_broadcast: "); if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); } return -1; } } if (pthread_mutex_unlock(&(pool->mutex))) { perror("pthread_mutex_unlock: "); return -1; } return 0; }