int main (int argc, char *argv[]) { long i; int res; pthread_t thr[4]; fprintf(stderr, "starting\n"); gomp_barrier_init( &bar, 4 ); for (i = 0; i < 4; i++) { res = pthread_create( &thr[i], NULL, child, (void*)(i+2) ); assert(!res); } for (i = 0; i < 4; i++) { res = pthread_join( thr[i], NULL ); assert(!res); } gomp_barrier_destroy( &bar ); /* And finally here, the root thread can get exclusive ownership back from thread #4, because #4 has exited by this point and so we have a dependency edge back to the write it did. */ fprintf(stderr, "done, result is %ld, should be 88\n", unprotected); return 0; }
struct gomp_team * gomp_new_team (unsigned nthreads) { struct gomp_team *team; int i; team = get_last_team (nthreads); if (team == NULL) { size_t extra = sizeof (team->ordered_release[0]) + sizeof (team->implicit_task[0]); team = gomp_malloc (sizeof (*team) + nthreads * extra); #ifndef HAVE_SYNC_BUILTINS gomp_mutex_init (&team->work_share_list_free_lock); #endif gomp_barrier_init (&team->barrier, nthreads); gomp_mutex_init (&team->task_lock); team->nthreads = nthreads; } team->work_share_chunk = 8; #ifdef HAVE_SYNC_BUILTINS team->single_count = 0; #endif team->work_shares_to_free = &team->work_shares[0]; gomp_init_work_share (&team->work_shares[0], false, nthreads); team->work_shares[0].next_alloc = NULL; team->work_share_list_free = NULL; team->work_share_list_alloc = &team->work_shares[1]; for (i = 1; i < 7; i++) team->work_shares[i].next_free = &team->work_shares[i + 1]; team->work_shares[i].next_free = NULL; gomp_sem_init (&team->master_release, 0); team->ordered_release = (void *) &team->implicit_task[nthreads]; team->ordered_release[0] = &team->master_release; priority_queue_init (&team->task_queue); team->task_count = 0; team->task_queued_count = 0; team->task_running_count = 0; team->work_share_cancelled = 0; team->team_cancelled = 0; return team; }
struct gomp_team * gomp_new_team (unsigned nthreads) { struct gomp_team *team; size_t size; int i; size = sizeof (*team) + nthreads * (sizeof (team->ordered_release[0]) + sizeof (team->implicit_task[0])); team = gomp_malloc (size); team->work_share_chunk = 8; #ifdef HAVE_SYNC_BUILTINS team->single_count = 0; #else gomp_mutex_init (&team->work_share_list_free_lock); #endif gomp_init_work_share (&team->work_shares[0], false, nthreads); team->work_shares[0].next_alloc = NULL; team->work_share_list_free = NULL; team->work_share_list_alloc = &team->work_shares[1]; for (i = 1; i < 7; i++) team->work_shares[i].next_free = &team->work_shares[i + 1]; team->work_shares[i].next_free = NULL; team->nthreads = nthreads; gomp_barrier_init (&team->barrier, nthreads); gomp_sem_init (&team->master_release, 0); team->ordered_release = (void *) &team->implicit_task[nthreads]; team->ordered_release[0] = &team->master_release; gomp_mutex_init (&team->task_lock); team->task_queue = NULL; team->task_count = 0; team->task_running_count = 0; return team; }
static struct gomp_team * new_team (unsigned nthreads, struct gomp_work_share *work_share) { struct gomp_team *team; size_t size; size = sizeof (*team) + nthreads * sizeof (team->ordered_release[0]); team = gomp_malloc (size); gomp_mutex_init (&team->work_share_lock); team->work_shares = gomp_malloc (4 * sizeof (struct gomp_work_share *)); team->generation_mask = 3; team->oldest_live_gen = work_share == NULL; team->num_live_gen = work_share != NULL; team->work_shares[0] = work_share; team->nthreads = nthreads; gomp_barrier_init (&team->barrier, nthreads); gomp_sem_init (&team->master_release, 0); team->ordered_release[0] = &team->master_release; return team; }
void gomp_team_start (void (*fn) (void *), void *data, unsigned nthreads, struct gomp_work_share *work_share) { struct gomp_thread_start_data *start_data; struct gomp_thread *thr, *nthr; struct gomp_team *team; bool nested; unsigned i, n, old_threads_used = 0; pthread_attr_t thread_attr, *attr; thr = gomp_thread (); nested = thr->ts.team != NULL; team = new_team (nthreads, work_share); /* Always save the previous state, even if this isn't a nested team. In particular, we should save any work share state from an outer orphaned work share construct. */ team->prev_ts = thr->ts; thr->ts.team = team; thr->ts.work_share = work_share; thr->ts.team_id = 0; thr->ts.work_share_generation = 0; thr->ts.static_trip = 0; if (nthreads == 1) return; i = 1; /* We only allow the reuse of idle threads for non-nested PARALLEL regions. This appears to be implied by the semantics of threadprivate variables, but perhaps that's reading too much into things. Certainly it does prevent any locking problems, since only the initial program thread will modify gomp_threads. */ if (!nested) { old_threads_used = gomp_threads_used; if (nthreads <= old_threads_used) n = nthreads; else if (old_threads_used == 0) { n = 0; gomp_barrier_init (&gomp_threads_dock, nthreads); } else { n = old_threads_used; /* Increase the barrier threshold to make sure all new threads arrive before the team is released. */ gomp_barrier_reinit (&gomp_threads_dock, nthreads); } /* Not true yet, but soon will be. We're going to release all threads from the dock, and those that aren't part of the team will exit. */ gomp_threads_used = nthreads; /* Release existing idle threads. */ for (; i < n; ++i) { nthr = gomp_threads[i]; nthr->ts.team = team; nthr->ts.work_share = work_share; nthr->ts.team_id = i; nthr->ts.work_share_generation = 0; nthr->ts.static_trip = 0; nthr->fn = fn; nthr->data = data; team->ordered_release[i] = &nthr->release; } if (i == nthreads) goto do_release; /* If necessary, expand the size of the gomp_threads array. It is expected that changes in the number of threads is rare, thus we make no effort to expand gomp_threads_size geometrically. */ if (nthreads >= gomp_threads_size) { gomp_threads_size = nthreads + 1; gomp_threads = gomp_realloc (gomp_threads, gomp_threads_size * sizeof (struct gomp_thread_data *)); } } attr = &gomp_thread_attr; if (gomp_cpu_affinity != NULL) { size_t stacksize; pthread_attr_init (&thread_attr); pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED); if (! pthread_attr_getstacksize (&gomp_thread_attr, &stacksize)) pthread_attr_setstacksize (&thread_attr, stacksize); attr = &thread_attr; } start_data = gomp_alloca (sizeof (struct gomp_thread_start_data) * (nthreads-i)); /* Launch new threads. */ for (; i < nthreads; ++i, ++start_data) { pthread_t pt; int err; start_data->ts.team = team; start_data->ts.work_share = work_share; start_data->ts.team_id = i; start_data->ts.work_share_generation = 0; start_data->ts.static_trip = 0; start_data->fn = fn; start_data->fn_data = data; start_data->nested = nested; if (gomp_cpu_affinity != NULL) gomp_init_thread_affinity (attr); err = pthread_create (&pt, attr, gomp_thread_start, start_data); if (err != 0) gomp_fatal ("Thread creation failed: %s", strerror (err)); } if (gomp_cpu_affinity != NULL) pthread_attr_destroy (&thread_attr); do_release: gomp_barrier_wait (nested ? &team->barrier : &gomp_threads_dock); /* Decrease the barrier threshold to match the number of threads that should arrive back at the end of this team. The extra threads should be exiting. Note that we arrange for this test to never be true for nested teams. */ if (nthreads < old_threads_used) gomp_barrier_reinit (&gomp_threads_dock, nthreads); }