static void* bdberl_tpool_main(void* arg) { TPool* tpool = (TPool*)arg; LOCK(tpool); tpool->active_threads++; while(1) { // Check for shutdown... if (tpool->shutdown) { tpool->active_threads--; erl_drv_cond_broadcast(tpool->work_cv); UNLOCK(tpool); return 0; } // Get the next job TPoolJob* job = next_job(tpool); if (job) { // Unlock to avoid blocking others UNLOCK(tpool); // Invoke the function (*(job->main_fn))(job->arg); // Relock LOCK(tpool); // Mark the job as not running (important for cancellation to know it's done) job->running = 0; // If the job was cancelled, signal the cancellation cv so that anyone waiting on the // job knows it's complete if (job->canceled) { erl_drv_cond_broadcast(tpool->cancel_cv); } // Cleanup the job (remove from active list, free, etc.) cleanup_job(tpool, job); } else { // Wait for a job to come available then jump back to top of loop erl_drv_cond_wait(tpool->work_cv, tpool->lock); } } return 0; }
void bdberl_tpool_run(TPool* tpool, TPoolJobFunc main_fn, void* arg, TPoolJobFunc cancel_fn, TPoolJob** job_ptr) { // Allocate and fill a new job structure TPoolJob* job = *job_ptr = driver_alloc(sizeof(TPoolJob)); memset(job, '\0', sizeof(TPoolJob)); job->main_fn = main_fn; job->arg = arg; job->cancel_fn = cancel_fn; // Sync up with the tpool and add the job to the pending queue LOCK(tpool); if (tpool->pending_jobs) { // Make sure the current last job points to this one next tpool->last_pending_job->next = job; } else { // No pending jobs; this is the first tpool->pending_jobs = job; } tpool->last_pending_job = job; tpool->pending_job_count++; // Generate a notification that there is work todo. // TODO: I think this may not be necessary, in the case where there are already other // pending jobs. Not sure ATM, however, so will be on safe side erl_drv_cond_broadcast(tpool->work_cv); UNLOCK(tpool); }
void bdberl_tpool_stop(TPool* tpool) { LOCK(tpool); // Set the shutdown flag and broadcast a notification tpool->shutdown = 1; erl_drv_cond_broadcast(tpool->work_cv); // Clean out the queue of pending jobs -- invoke their cleanup function // Wait for until active_threads hits zero while (tpool->active_threads > 0) { erl_drv_cond_wait(tpool->work_cv, tpool->lock); } // Join up with all the workers int i = 0; for (i = 0; i < tpool->thread_count; i++) { erl_drv_thread_join(tpool->threads[i], 0); } // Cleanup erl_drv_cond_destroy(tpool->work_cv); erl_drv_cond_destroy(tpool->cancel_cv); driver_free(tpool->threads); UNLOCK(tpool); erl_drv_mutex_destroy(tpool->lock); driver_free(tpool); }
void enif_cond_broadcast(ErlNifCond *cnd) { erl_drv_cond_broadcast(cnd); }
int erts_cond_broadcast(erl_cond_t cnd) { erl_drv_cond_broadcast((ErlDrvCond *) cnd); return 0; }