work.c

Go to the documentation of this file.
00001 /* Copyright (C) 2005, 2008, 2009 Free Software Foundation, Inc.
00002    Contributed by Richard Henderson <rth@redhat.com>.
00003 
00004    This file is part of the GNU OpenMP Library (libgomp).
00005 
00006    Libgomp is free software; you can redistribute it and/or modify it
00007    under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 3, or (at your option)
00009    any later version.
00010 
00011    Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
00012    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00013    FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00014    more details.
00015 
00016    Under Section 7 of GPL version 3, you are granted additional
00017    permissions described in the GCC Runtime Library Exception, version
00018    3.1, as published by the Free Software Foundation.
00019 
00020    You should have received a copy of the GNU General Public License and
00021    a copy of the GCC Runtime Library Exception along with this program;
00022    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
00023    <http://www.gnu.org/licenses/>.  */
00024 
00025 /* This file contains routines to manage the work-share queue for a team
00026    of threads.  */
00027 
00028 #include "libgomp.h"
00029 #include <stddef.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 
00033 
00034 /* Allocate a new work share structure, preferably from current team's
00035    free gomp_work_share cache.  */
00036 
00037 static struct gomp_work_share *
00038 alloc_work_share (struct gomp_team *team)
00039 {
00040   struct gomp_work_share *ws;
00041   unsigned int i;
00042 
00043   /* This is called in a critical section.  */
00044   if (team->work_share_list_alloc != NULL)
00045     {
00046       ws = team->work_share_list_alloc;
00047       team->work_share_list_alloc = ws->next_free;
00048       return ws;
00049     }
00050 
00051 #ifdef HAVE_SYNC_BUILTINS
00052   ws = team->work_share_list_free;
00053   /* We need atomic read from work_share_list_free,
00054      as free_work_share can be called concurrently.  */
00055   __asm ("" : "+r" (ws));
00056 
00057   if (ws && ws->next_free)
00058     {
00059       struct gomp_work_share *next = ws->next_free;
00060       ws->next_free = NULL;
00061       team->work_share_list_alloc = next->next_free;
00062       return next;
00063     }
00064 #else
00065   gomp_mutex_lock (&team->work_share_list_free_lock);
00066   ws = team->work_share_list_free;
00067   if (ws)
00068     {
00069       team->work_share_list_alloc = ws->next_free;
00070       team->work_share_list_free = NULL;
00071       gomp_mutex_unlock (&team->work_share_list_free_lock);
00072       return ws;
00073     }
00074   gomp_mutex_unlock (&team->work_share_list_free_lock);
00075 #endif
00076 
00077   team->work_share_chunk *= 2;
00078   ws = gomp_malloc (team->work_share_chunk * sizeof (struct gomp_work_share));
00079   ws->next_alloc = team->work_shares[0].next_alloc;
00080   team->work_shares[0].next_alloc = ws;
00081   team->work_share_list_alloc = &ws[1];
00082   for (i = 1; i < team->work_share_chunk - 1; i++)
00083     ws[i].next_free = &ws[i + 1];
00084   ws[i].next_free = NULL;
00085   return ws;
00086 }
00087 
00088 /* Initialize an already allocated struct gomp_work_share.
00089    This shouldn't touch the next_alloc field.  */
00090 
00091 void
00092 gomp_init_work_share (struct gomp_work_share *ws, bool ordered,
00093               unsigned nthreads)
00094 {
00095   gomp_mutex_init (&ws->lock);
00096   if (__builtin_expect (ordered, 0))
00097     {
00098 #define INLINE_ORDERED_TEAM_IDS_CNT \
00099   ((sizeof (struct gomp_work_share) \
00100     - offsetof (struct gomp_work_share, inline_ordered_team_ids)) \
00101    / sizeof (((struct gomp_work_share *) 0)->inline_ordered_team_ids[0]))
00102 
00103       if (nthreads > INLINE_ORDERED_TEAM_IDS_CNT)
00104     ws->ordered_team_ids
00105       = gomp_malloc (nthreads * sizeof (*ws->ordered_team_ids));
00106       else
00107     ws->ordered_team_ids = ws->inline_ordered_team_ids;
00108       memset (ws->ordered_team_ids, '\0',
00109           nthreads * sizeof (*ws->ordered_team_ids));
00110       ws->ordered_num_used = 0;
00111       ws->ordered_owner = -1;
00112       ws->ordered_cur = 0;
00113     }
00114   else
00115     ws->ordered_team_ids = NULL;
00116   gomp_ptrlock_init (&ws->next_ws, NULL);
00117   ws->threads_completed = 0;
00118 }
00119 
00120 /* Do any needed destruction of gomp_work_share fields before it
00121    is put back into free gomp_work_share cache or freed.  */
00122 
00123 void
00124 gomp_fini_work_share (struct gomp_work_share *ws)
00125 {
00126   gomp_mutex_destroy (&ws->lock);
00127   if (ws->ordered_team_ids != ws->inline_ordered_team_ids)
00128     free (ws->ordered_team_ids);
00129   gomp_ptrlock_destroy (&ws->next_ws);
00130 }
00131 
00132 /* Free a work share struct, if not orphaned, put it into current
00133    team's free gomp_work_share cache.  */
00134 
00135 static inline void
00136 free_work_share (struct gomp_team *team, struct gomp_work_share *ws)
00137 {
00138   gomp_fini_work_share (ws);
00139   if (__builtin_expect (team == NULL, 0))
00140     free (ws);
00141   else
00142     {
00143       struct gomp_work_share *next_ws;
00144 #ifdef HAVE_SYNC_BUILTINS
00145       do
00146     {
00147       next_ws = team->work_share_list_free;
00148       ws->next_free = next_ws;
00149     }
00150       while (!__sync_bool_compare_and_swap (&team->work_share_list_free,
00151                         next_ws, ws));
00152 #else
00153       gomp_mutex_lock (&team->work_share_list_free_lock);
00154       next_ws = team->work_share_list_free;
00155       ws->next_free = next_ws;
00156       team->work_share_list_free = ws;
00157       gomp_mutex_unlock (&team->work_share_list_free_lock);
00158 #endif
00159     }
00160 }
00161 
00162 /* The current thread is ready to begin the next work sharing construct.
00163    In all cases, thr->ts.work_share is updated to point to the new
00164    structure.  In all cases the work_share lock is locked.  Return true
00165    if this was the first thread to reach this point.  */
00166 
00167 bool
00168 gomp_work_share_start (bool ordered)
00169 {
00170   struct gomp_thread *thr = gomp_thread ();
00171   struct gomp_team *team = thr->ts.team;
00172   struct gomp_work_share *ws;
00173 
00174   /* Work sharing constructs can be orphaned.  */
00175   if (team == NULL)
00176     {
00177       ws = gomp_malloc (sizeof (*ws));
00178       gomp_init_work_share (ws, ordered, 1);
00179       thr->ts.work_share = ws;
00180       return ws;
00181     }
00182 
00183   ws = thr->ts.work_share;
00184   thr->ts.last_work_share = ws;
00185   ws = gomp_ptrlock_get (&ws->next_ws);
00186   if (ws == NULL)
00187     {
00188       /* This thread encountered a new ws first.  */
00189       struct gomp_work_share *ws = alloc_work_share (team);
00190       gomp_init_work_share (ws, ordered, team->nthreads);
00191       thr->ts.work_share = ws;
00192       return true;
00193     }
00194   else
00195     {
00196       thr->ts.work_share = ws;
00197       return false;
00198     }
00199 }
00200 
00201 /* The current thread is done with its current work sharing construct.
00202    This version does imply a barrier at the end of the work-share.  */
00203 
00204 void
00205 gomp_work_share_end (void)
00206 {
00207   struct gomp_thread *thr = gomp_thread ();
00208   struct gomp_team *team = thr->ts.team;
00209   gomp_barrier_state_t bstate;
00210 
00211   /* Work sharing constructs can be orphaned.  */
00212   if (team == NULL)
00213     {
00214       free_work_share (NULL, thr->ts.work_share);
00215       thr->ts.work_share = NULL;
00216       return;
00217     }
00218 
00219   bstate = gomp_barrier_wait_start (&team->barrier);
00220 
00221   if (gomp_barrier_last_thread (bstate))
00222     {
00223       if (__builtin_expect (thr->ts.last_work_share != NULL, 1))
00224     free_work_share (team, thr->ts.last_work_share);
00225     }
00226 
00227   gomp_team_barrier_wait_end (&team->barrier, bstate);
00228   thr->ts.last_work_share = NULL;
00229 }
00230 
00231 /* The current thread is done with its current work sharing construct.
00232    This version does NOT imply a barrier at the end of the work-share.  */
00233 
00234 void
00235 gomp_work_share_end_nowait (void)
00236 {
00237   struct gomp_thread *thr = gomp_thread ();
00238   struct gomp_team *team = thr->ts.team;
00239   struct gomp_work_share *ws = thr->ts.work_share;
00240   unsigned completed;
00241 
00242   /* Work sharing constructs can be orphaned.  */
00243   if (team == NULL)
00244     {
00245       free_work_share (NULL, ws);
00246       thr->ts.work_share = NULL;
00247       return;
00248     }
00249 
00250   if (__builtin_expect (thr->ts.last_work_share == NULL, 0))
00251     return;
00252 
00253 #ifdef HAVE_SYNC_BUILTINS
00254   completed = __sync_add_and_fetch (&ws->threads_completed, 1);
00255 #else
00256   gomp_mutex_lock (&ws->lock);
00257   completed = ++ws->threads_completed;
00258   gomp_mutex_unlock (&ws->lock);
00259 #endif
00260 
00261   if (completed == team->nthreads)
00262     free_work_share (team, thr->ts.last_work_share);
00263   thr->ts.last_work_share = NULL;
00264 }

Generated on Fri Apr 5 05:38:10 2013 for Libgomp by  doxygen 1.4.7