WEAK void *halide_worker_thread(void *void_arg) { work *owned_job = (work *)void_arg; // Grab the lock pthread_mutex_lock(&halide_work_queue.mutex); // If I'm a job owner, then I was the thread that called // do_par_for, and I should only stay in this function until my // job is complete. If I'm a lowly worker thread, I should stay in // this function as long as the work queue is running. while (owned_job != NULL ? owned_job->running() : halide_work_queue.running()) { if (halide_work_queue.jobs == NULL) { // There are no jobs pending, though some tasks may still // be in flight from the last job. Release the lock and // wait for something new to happen. pthread_cond_wait(&halide_work_queue.state_change, &halide_work_queue.mutex); } else { // There are jobs still to do. Grab the next one. work *job = halide_work_queue.jobs; // Claim a task from it. work myjob = *job; job->next++; // If there were no more tasks pending for this job, or if // it has failed, remove it from the stack. if (job->next == job->max) { halide_work_queue.jobs = job->next_job; } // Increment the active_worker count so that other threads // are aware that this job is still in progress even // though there are no outstanding tasks for it. job->active_workers++; // Release the lock and do the task. pthread_mutex_unlock(&halide_work_queue.mutex); int result = halide_do_task(myjob.f, myjob.next, myjob.closure); pthread_mutex_lock(&halide_work_queue.mutex); // If this task failed, set the exit status on the job. if (result) { job->exit_status = result; } // We are no longer active on this job job->active_workers--; // If the job is done and I'm not the owner of it, wake up // the owner. if (!job->running() && job != owned_job) { pthread_cond_broadcast(&halide_work_queue.state_change); } } } pthread_mutex_unlock(&halide_work_queue.mutex); return NULL; }
int halide_do_par_for(void *user_context, halide_task_t f, int min, int size, uint8_t *closure) { for (int x = min; x < min + size; x++) { int result = halide_do_task(user_context, f, x, closure); if (result) { return result; } } return 0; }
WEAK void halide_do_par_for(void (*f)(int, uint8_t *), int min, int size, uint8_t *closure) { if (halide_custom_do_par_for) { (*halide_custom_do_par_for)(f, min, size, closure); return; } for (int x = min; x < min + size; x++) { halide_do_task(f, x, closure); } }
WEAK void *halide_worker_thread(void *void_arg) { /* int id = -1; for (int i = 0; i < threads-1; i++) { if (halide_work_queue.threads[i] == pthread_self()) id = i; } */ worker_arg *arg = (worker_arg *)void_arg; //fprintf(stderr, "Worker %d: thread running\n", id); while (1) { //fprintf(stderr, "Worker %d: About to lock mutex\n", id); pthread_mutex_lock(&halide_work_queue.mutex); //fprintf(stderr, "Worker %d: Mutex locked, checking for work\n", id); // we're master, and there's no more work if (arg && arg->job->id != arg->id) { // wait until other workers are done if (arg->job->active_workers) { pthread_mutex_unlock(&halide_work_queue.mutex); while (true) { //fprintf(stderr, "Master %d: waiting for workers to finish\n", arg->id); pthread_mutex_lock(&halide_work_queue.mutex); //fprintf(stderr, "Master %d: mutex grabbed. %d workers still going\n", arg->id, arg->job->active_workers); if (!arg->job->active_workers) break; pthread_mutex_unlock(&halide_work_queue.mutex); } } // job is actually done pthread_mutex_unlock(&halide_work_queue.mutex); //fprintf(stderr, "Master %d: This task is done.\n", arg->id); return NULL; } if (halide_work_queue.shutdown) { pthread_mutex_unlock(&halide_work_queue.mutex); //fprintf(stderr, "Worker %d: quitting\n", id); return NULL; } if (halide_work_queue.head == halide_work_queue.tail) { //assert(!arg); // the master should never get here //fprintf(stderr, "Worker %d: Going to sleep.\n", id); fflush(stderr); pthread_cond_wait(&halide_work_queue.not_empty, &halide_work_queue.mutex); pthread_mutex_unlock(&halide_work_queue.mutex); //fprintf(stderr, "Worker %d: Waking up.\n", id); fflush(stderr); continue; } //fprintf(stderr, "Worker %d: There is work\n", id); work *job = halide_work_queue.jobs + halide_work_queue.head; if (job->next == job->max) { //fprintf(stderr, "Worker %d: Found a finished job. Removing it\n", id); halide_work_queue.head = (halide_work_queue.head + 1) % MAX_JOBS; job->id = 0; // mark the job done pthread_mutex_unlock(&halide_work_queue.mutex); } else { // Claim some tasks //int claimed = (remaining + threads - 1)/threads; int claimed = 1; //fprintf(stderr, "Worker %d: Claiming %d tasks\n", id, claimed); work myjob = *job; job->next += claimed; myjob.max = job->next; job->active_workers++; pthread_mutex_unlock(&halide_work_queue.mutex); for (; myjob.next < myjob.max; myjob.next++) { //fprintf(stderr, "Worker %d: Doing job %d\n", id, myjob.next); halide_do_task(myjob.f, myjob.next, myjob.closure); //fprintf(stderr, "Worker %d: Done with job %d\n", id, myjob.next); } pthread_mutex_lock(&halide_work_queue.mutex); job->active_workers--; pthread_mutex_unlock(&halide_work_queue.mutex); } } }
// Take a call from grand-central-dispatch's parallel for loop, and // make a call to Halide's do task WEAK void halide_do_gcd_task(void *job, size_t idx) { halide_gcd_job *j = (halide_gcd_job *)job; j->exit_status = halide_do_task(j->user_context, j->f, j->min + (int)idx, j->closure); }
// Take a call from grand-central-dispatch's parallel for loop, and // make a call to Halide's do task WEAK void halide_do_gcd_task(void *job, size_t idx) { halide_gcd_job *j = (halide_gcd_job *)job; halide_do_task(j->f, j->min + (int)idx, j->closure); }
WEAK void *halide_worker_thread(void *void_arg) { work *owned_job = (work *)void_arg; // Grab the lock pthread_mutex_lock(&halide_work_queue.mutex); // If I'm a job owner, then I was the thread that called // do_par_for, and I should only stay in this function until my // job is complete. If I'm a lowly worker thread, I should stay in // this function as long as the work queue is running. while (owned_job != NULL ? owned_job->running() : halide_work_queue.running()) { if (halide_work_queue.jobs == NULL) { if (owned_job) { // There are no jobs pending. Wait for the last worker // to signal that the job is finished. pthread_cond_wait(&halide_work_queue.wakeup_owners, &halide_work_queue.mutex); } else if (halide_work_queue.a_team_size <= halide_work_queue.target_a_team_size) { // There are no jobs pending. Wait until more jobs are enqueued. pthread_cond_wait(&halide_work_queue.wakeup_a_team, &halide_work_queue.mutex); } else { // There are no jobs pending, and there are too many // threads in the A team. Transition to the B team // until the wakeup_b_team condition is fired. halide_work_queue.a_team_size--; pthread_cond_wait(&halide_work_queue.wakeup_b_team, &halide_work_queue.mutex); halide_work_queue.a_team_size++; } } else { // Grab the next job. work *job = halide_work_queue.jobs; // Claim a task from it. work myjob = *job; job->next++; // If there were no more tasks pending for this job, // remove it from the stack. if (job->next == job->max) { halide_work_queue.jobs = job->next_job; } // Increment the active_worker count so that other threads // are aware that this job is still in progress even // though there are no outstanding tasks for it. job->active_workers++; // Release the lock and do the task. pthread_mutex_unlock(&halide_work_queue.mutex); int result = halide_do_task(myjob.user_context, myjob.f, myjob.next, myjob.closure); pthread_mutex_lock(&halide_work_queue.mutex); // If this task failed, set the exit status on the job. if (result) { job->exit_status = result; } // We are no longer active on this job job->active_workers--; // If the job is done and I'm not the owner of it, wake up // the owner. if (!job->running() && job != owned_job) { pthread_cond_broadcast(&halide_work_queue.wakeup_owners); } } } pthread_mutex_unlock(&halide_work_queue.mutex); return NULL; }