work.c revision 285830
1250661Sdavidcs/* Copyright (C) 2005 Free Software Foundation, Inc. 2250661Sdavidcs Contributed by Richard Henderson <rth@redhat.com>. 3250661Sdavidcs 4250661Sdavidcs This file is part of the GNU OpenMP Library (libgomp). 5250661Sdavidcs 6250661Sdavidcs Libgomp is free software; you can redistribute it and/or modify it 7250661Sdavidcs under the terms of the GNU Lesser General Public License as published by 8250661Sdavidcs the Free Software Foundation; either version 2.1 of the License, or 9250661Sdavidcs (at your option) any later version. 10250661Sdavidcs 11250661Sdavidcs Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY 12250661Sdavidcs WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13250661Sdavidcs FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 14250661Sdavidcs more details. 15250661Sdavidcs 16250661Sdavidcs You should have received a copy of the GNU Lesser General Public License 17250661Sdavidcs along with libgomp; see the file COPYING.LIB. If not, write to the 18250661Sdavidcs Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 19250661Sdavidcs MA 02110-1301, USA. */ 20250661Sdavidcs 21250661Sdavidcs/* As a special exception, if you link this library with other files, some 22250661Sdavidcs of which are compiled with GCC, to produce an executable, this library 23250661Sdavidcs does not by itself cause the resulting executable to be covered by the 24250661Sdavidcs GNU General Public License. This exception does not however invalidate 25250661Sdavidcs any other reasons why the executable file might be covered by the GNU 26250661Sdavidcs General Public License. */ 27250661Sdavidcs 28250661Sdavidcs/* This file contains routines to manage the work-share queue for a team 29250661Sdavidcs of threads. */ 30250661Sdavidcs 31250661Sdavidcs#include "libgomp.h" 32250661Sdavidcs#include <stdlib.h> 33250661Sdavidcs#include <string.h> 34250661Sdavidcs 35250661Sdavidcs 36250661Sdavidcs/* Create a new work share structure. */ 37250661Sdavidcs 38250661Sdavidcsstruct gomp_work_share * 39250661Sdavidcsgomp_new_work_share (bool ordered, unsigned nthreads) 40250661Sdavidcs{ 41250661Sdavidcs struct gomp_work_share *ws; 42250661Sdavidcs size_t size; 43250661Sdavidcs 44250661Sdavidcs size = sizeof (*ws); 45250661Sdavidcs if (ordered) 46250661Sdavidcs size += nthreads * sizeof (ws->ordered_team_ids[0]); 47250661Sdavidcs 48250661Sdavidcs ws = gomp_malloc_cleared (size); 49250661Sdavidcs gomp_mutex_init (&ws->lock); 50250661Sdavidcs ws->ordered_owner = -1; 51250661Sdavidcs 52250661Sdavidcs return ws; 53250661Sdavidcs} 54250661Sdavidcs 55250661Sdavidcs 56250661Sdavidcs/* Free a work share structure. */ 57250661Sdavidcs 58250661Sdavidcsstatic void 59250661Sdavidcsfree_work_share (struct gomp_work_share *ws) 60250661Sdavidcs{ 61250661Sdavidcs gomp_mutex_destroy (&ws->lock); 62250661Sdavidcs free (ws); 63250661Sdavidcs} 64250661Sdavidcs 65250661Sdavidcs 66250661Sdavidcs/* The current thread is ready to begin the next work sharing construct. 67250661Sdavidcs In all cases, thr->ts.work_share is updated to point to the new 68250661Sdavidcs structure. In all cases the work_share lock is locked. Return true 69250661Sdavidcs if this was the first thread to reach this point. */ 70250661Sdavidcs 71250661Sdavidcsbool 72250661Sdavidcsgomp_work_share_start (bool ordered) 73250661Sdavidcs{ 74250661Sdavidcs struct gomp_thread *thr = gomp_thread (); 75250661Sdavidcs struct gomp_team *team = thr->ts.team; 76250661Sdavidcs struct gomp_work_share *ws; 77250661Sdavidcs unsigned ws_index, ws_gen; 78250661Sdavidcs 79250661Sdavidcs /* Work sharing constructs can be orphaned. */ 80250661Sdavidcs if (team == NULL) 81250661Sdavidcs { 82250661Sdavidcs ws = gomp_new_work_share (ordered, 1); 83250661Sdavidcs thr->ts.work_share = ws; 84250661Sdavidcs thr->ts.static_trip = 0; 85250661Sdavidcs gomp_mutex_lock (&ws->lock); 86250661Sdavidcs return true; 87250661Sdavidcs } 88250661Sdavidcs 89250661Sdavidcs gomp_mutex_lock (&team->work_share_lock); 90250661Sdavidcs 91250661Sdavidcs /* This thread is beginning its next generation. */ 92250661Sdavidcs ws_gen = ++thr->ts.work_share_generation; 93250661Sdavidcs 94250661Sdavidcs /* If this next generation is not newer than any other generation in 95250661Sdavidcs the team, then simply reference the existing construct. */ 96250661Sdavidcs if (ws_gen - team->oldest_live_gen < team->num_live_gen) 97250661Sdavidcs { 98250661Sdavidcs ws_index = ws_gen & team->generation_mask; 99250661Sdavidcs ws = team->work_shares[ws_index]; 100250661Sdavidcs thr->ts.work_share = ws; 101250661Sdavidcs thr->ts.static_trip = 0; 102250661Sdavidcs 103250661Sdavidcs gomp_mutex_lock (&ws->lock); 104250661Sdavidcs gomp_mutex_unlock (&team->work_share_lock); 105250661Sdavidcs 106250661Sdavidcs return false; 107250661Sdavidcs } 108250661Sdavidcs 109250661Sdavidcs /* Resize the work shares queue if we've run out of space. */ 110250661Sdavidcs if (team->num_live_gen++ == team->generation_mask) 111250661Sdavidcs { 112250661Sdavidcs team->work_shares = gomp_realloc (team->work_shares, 113250661Sdavidcs 2 * team->num_live_gen 114250661Sdavidcs * sizeof (*team->work_shares)); 115250661Sdavidcs 116250661Sdavidcs /* Unless oldest_live_gen is zero, the sequence of live elements 117250661Sdavidcs wraps around the end of the array. If we do nothing, we break 118250661Sdavidcs lookup of the existing elements. Fix that by unwrapping the 119250661Sdavidcs data from the front to the end. */ 120250661Sdavidcs if (team->oldest_live_gen > 0) 121250661Sdavidcs memcpy (team->work_shares + team->num_live_gen, 122250661Sdavidcs team->work_shares, 123250661Sdavidcs (team->oldest_live_gen & team->generation_mask) 124250661Sdavidcs * sizeof (*team->work_shares)); 125250661Sdavidcs 126250661Sdavidcs team->generation_mask = team->generation_mask * 2 + 1; 127250661Sdavidcs } 128250661Sdavidcs 129250661Sdavidcs ws_index = ws_gen & team->generation_mask; 130250661Sdavidcs ws = gomp_new_work_share (ordered, team->nthreads); 131250661Sdavidcs thr->ts.work_share = ws; 132250661Sdavidcs thr->ts.static_trip = 0; 133250661Sdavidcs team->work_shares[ws_index] = ws; 134250661Sdavidcs 135250661Sdavidcs gomp_mutex_lock (&ws->lock); 136250661Sdavidcs gomp_mutex_unlock (&team->work_share_lock); 137250661Sdavidcs 138250661Sdavidcs return true; 139250661Sdavidcs} 140250661Sdavidcs 141250661Sdavidcs 142250661Sdavidcs/* The current thread is done with its current work sharing construct. 143250661Sdavidcs This version does imply a barrier at the end of the work-share. */ 144250661Sdavidcs 145250661Sdavidcsvoid 146250661Sdavidcsgomp_work_share_end (void) 147250661Sdavidcs{ 148250661Sdavidcs struct gomp_thread *thr = gomp_thread (); 149250661Sdavidcs struct gomp_team *team = thr->ts.team; 150250661Sdavidcs struct gomp_work_share *ws = thr->ts.work_share; 151250661Sdavidcs bool last; 152250661Sdavidcs 153250661Sdavidcs thr->ts.work_share = NULL; 154250661Sdavidcs 155250661Sdavidcs /* Work sharing constructs can be orphaned. */ 156250661Sdavidcs if (team == NULL) 157250661Sdavidcs { 158250661Sdavidcs free_work_share (ws); 159250661Sdavidcs return; 160250661Sdavidcs } 161250661Sdavidcs 162250661Sdavidcs last = gomp_barrier_wait_start (&team->barrier); 163273736Shselasky 164250661Sdavidcs if (last) 165250661Sdavidcs { 166250661Sdavidcs unsigned ws_index; 167250661Sdavidcs 168250661Sdavidcs ws_index = thr->ts.work_share_generation & team->generation_mask; 169250661Sdavidcs team->work_shares[ws_index] = NULL; 170250661Sdavidcs team->oldest_live_gen++; 171250661Sdavidcs team->num_live_gen = 0; 172250661Sdavidcs 173250661Sdavidcs free_work_share (ws); 174250661Sdavidcs } 175250661Sdavidcs 176250661Sdavidcs gomp_barrier_wait_end (&team->barrier, last); 177250661Sdavidcs} 178250661Sdavidcs 179250661Sdavidcs 180250661Sdavidcs/* The current thread is done with its current work sharing construct. 181250661Sdavidcs This version does NOT imply a barrier at the end of the work-share. */ 182250661Sdavidcs 183250661Sdavidcsvoid 184250661Sdavidcsgomp_work_share_end_nowait (void) 185250661Sdavidcs{ 186250661Sdavidcs struct gomp_thread *thr = gomp_thread (); 187250661Sdavidcs struct gomp_team *team = thr->ts.team; 188250661Sdavidcs struct gomp_work_share *ws = thr->ts.work_share; 189250661Sdavidcs unsigned completed; 190250661Sdavidcs 191250661Sdavidcs thr->ts.work_share = NULL; 192250661Sdavidcs 193250661Sdavidcs /* Work sharing constructs can be orphaned. */ 194250661Sdavidcs if (team == NULL) 195250661Sdavidcs { 196250661Sdavidcs free_work_share (ws); 197250661Sdavidcs return; 198250661Sdavidcs } 199250661Sdavidcs 200250661Sdavidcs#ifdef HAVE_SYNC_BUILTINS 201250661Sdavidcs completed = __sync_add_and_fetch (&ws->threads_completed, 1); 202250661Sdavidcs#else 203250661Sdavidcs gomp_mutex_lock (&ws->lock); 204250661Sdavidcs completed = ++ws->threads_completed; 205250661Sdavidcs gomp_mutex_unlock (&ws->lock); 206250661Sdavidcs#endif 207250661Sdavidcs 208250661Sdavidcs if (completed == team->nthreads) 209250661Sdavidcs { 210250661Sdavidcs unsigned ws_index; 211250661Sdavidcs 212250661Sdavidcs gomp_mutex_lock (&team->work_share_lock); 213250661Sdavidcs 214250661Sdavidcs ws_index = thr->ts.work_share_generation & team->generation_mask; 215250661Sdavidcs team->work_shares[ws_index] = NULL; 216250661Sdavidcs team->oldest_live_gen++; 217250661Sdavidcs team->num_live_gen--; 218250661Sdavidcs 219250661Sdavidcs gomp_mutex_unlock (&team->work_share_lock); 220250661Sdavidcs 221250661Sdavidcs free_work_share (ws); 222250661Sdavidcs } 223250661Sdavidcs} 224250661Sdavidcs