Home | History | Annotate | Line # | Download | only in libgomp
work.c revision 1.1
      1  1.1  mrg /* Copyright (C) 2005, 2008, 2009 Free Software Foundation, Inc.
      2  1.1  mrg    Contributed by Richard Henderson <rth (at) redhat.com>.
      3  1.1  mrg 
      4  1.1  mrg    This file is part of the GNU OpenMP Library (libgomp).
      5  1.1  mrg 
      6  1.1  mrg    Libgomp is free software; you can redistribute it and/or modify it
      7  1.1  mrg    under the terms of the GNU General Public License as published by
      8  1.1  mrg    the Free Software Foundation; either version 3, or (at your option)
      9  1.1  mrg    any later version.
     10  1.1  mrg 
     11  1.1  mrg    Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
     12  1.1  mrg    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     13  1.1  mrg    FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     14  1.1  mrg    more details.
     15  1.1  mrg 
     16  1.1  mrg    Under Section 7 of GPL version 3, you are granted additional
     17  1.1  mrg    permissions described in the GCC Runtime Library Exception, version
     18  1.1  mrg    3.1, as published by the Free Software Foundation.
     19  1.1  mrg 
     20  1.1  mrg    You should have received a copy of the GNU General Public License and
     21  1.1  mrg    a copy of the GCC Runtime Library Exception along with this program;
     22  1.1  mrg    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     23  1.1  mrg    <http://www.gnu.org/licenses/>.  */
     24  1.1  mrg 
     25  1.1  mrg /* This file contains routines to manage the work-share queue for a team
     26  1.1  mrg    of threads.  */
     27  1.1  mrg 
     28  1.1  mrg #include "libgomp.h"
     29  1.1  mrg #include <stddef.h>
     30  1.1  mrg #include <stdlib.h>
     31  1.1  mrg #include <string.h>
     32  1.1  mrg 
     33  1.1  mrg 
     34  1.1  mrg /* Allocate a new work share structure, preferably from current team's
     35  1.1  mrg    free gomp_work_share cache.  */
     36  1.1  mrg 
     37  1.1  mrg static struct gomp_work_share *
     38  1.1  mrg alloc_work_share (struct gomp_team *team)
     39  1.1  mrg {
     40  1.1  mrg   struct gomp_work_share *ws;
     41  1.1  mrg   unsigned int i;
     42  1.1  mrg 
     43  1.1  mrg   /* This is called in a critical section.  */
     44  1.1  mrg   if (team->work_share_list_alloc != NULL)
     45  1.1  mrg     {
     46  1.1  mrg       ws = team->work_share_list_alloc;
     47  1.1  mrg       team->work_share_list_alloc = ws->next_free;
     48  1.1  mrg       return ws;
     49  1.1  mrg     }
     50  1.1  mrg 
     51  1.1  mrg #ifdef HAVE_SYNC_BUILTINS
     52  1.1  mrg   ws = team->work_share_list_free;
     53  1.1  mrg   /* We need atomic read from work_share_list_free,
     54  1.1  mrg      as free_work_share can be called concurrently.  */
     55  1.1  mrg   __asm ("" : "+r" (ws));
     56  1.1  mrg 
     57  1.1  mrg   if (ws && ws->next_free)
     58  1.1  mrg     {
     59  1.1  mrg       struct gomp_work_share *next = ws->next_free;
     60  1.1  mrg       ws->next_free = NULL;
     61  1.1  mrg       team->work_share_list_alloc = next->next_free;
     62  1.1  mrg       return next;
     63  1.1  mrg     }
     64  1.1  mrg #else
     65  1.1  mrg   gomp_mutex_lock (&team->work_share_list_free_lock);
     66  1.1  mrg   ws = team->work_share_list_free;
     67  1.1  mrg   if (ws)
     68  1.1  mrg     {
     69  1.1  mrg       team->work_share_list_alloc = ws->next_free;
     70  1.1  mrg       team->work_share_list_free = NULL;
     71  1.1  mrg       gomp_mutex_unlock (&team->work_share_list_free_lock);
     72  1.1  mrg       return ws;
     73  1.1  mrg     }
     74  1.1  mrg   gomp_mutex_unlock (&team->work_share_list_free_lock);
     75  1.1  mrg #endif
     76  1.1  mrg 
     77  1.1  mrg   team->work_share_chunk *= 2;
     78  1.1  mrg   ws = gomp_malloc (team->work_share_chunk * sizeof (struct gomp_work_share));
     79  1.1  mrg   ws->next_alloc = team->work_shares[0].next_alloc;
     80  1.1  mrg   team->work_shares[0].next_alloc = ws;
     81  1.1  mrg   team->work_share_list_alloc = &ws[1];
     82  1.1  mrg   for (i = 1; i < team->work_share_chunk - 1; i++)
     83  1.1  mrg     ws[i].next_free = &ws[i + 1];
     84  1.1  mrg   ws[i].next_free = NULL;
     85  1.1  mrg   return ws;
     86  1.1  mrg }
     87  1.1  mrg 
     88  1.1  mrg /* Initialize an already allocated struct gomp_work_share.
     89  1.1  mrg    This shouldn't touch the next_alloc field.  */
     90  1.1  mrg 
     91  1.1  mrg void
     92  1.1  mrg gomp_init_work_share (struct gomp_work_share *ws, bool ordered,
     93  1.1  mrg 		      unsigned nthreads)
     94  1.1  mrg {
     95  1.1  mrg   gomp_mutex_init (&ws->lock);
     96  1.1  mrg   if (__builtin_expect (ordered, 0))
     97  1.1  mrg     {
     98  1.1  mrg #define INLINE_ORDERED_TEAM_IDS_CNT \
     99  1.1  mrg   ((sizeof (struct gomp_work_share) \
    100  1.1  mrg     - offsetof (struct gomp_work_share, inline_ordered_team_ids)) \
    101  1.1  mrg    / sizeof (((struct gomp_work_share *) 0)->inline_ordered_team_ids[0]))
    102  1.1  mrg 
    103  1.1  mrg       if (nthreads > INLINE_ORDERED_TEAM_IDS_CNT)
    104  1.1  mrg 	ws->ordered_team_ids
    105  1.1  mrg 	  = gomp_malloc (nthreads * sizeof (*ws->ordered_team_ids));
    106  1.1  mrg       else
    107  1.1  mrg 	ws->ordered_team_ids = ws->inline_ordered_team_ids;
    108  1.1  mrg       memset (ws->ordered_team_ids, '\0',
    109  1.1  mrg 	      nthreads * sizeof (*ws->ordered_team_ids));
    110  1.1  mrg       ws->ordered_num_used = 0;
    111  1.1  mrg       ws->ordered_owner = -1;
    112  1.1  mrg       ws->ordered_cur = 0;
    113  1.1  mrg     }
    114  1.1  mrg   else
    115  1.1  mrg     ws->ordered_team_ids = NULL;
    116  1.1  mrg   gomp_ptrlock_init (&ws->next_ws, NULL);
    117  1.1  mrg   ws->threads_completed = 0;
    118  1.1  mrg }
    119  1.1  mrg 
    120  1.1  mrg /* Do any needed destruction of gomp_work_share fields before it
    121  1.1  mrg    is put back into free gomp_work_share cache or freed.  */
    122  1.1  mrg 
    123  1.1  mrg void
    124  1.1  mrg gomp_fini_work_share (struct gomp_work_share *ws)
    125  1.1  mrg {
    126  1.1  mrg   gomp_mutex_destroy (&ws->lock);
    127  1.1  mrg   if (ws->ordered_team_ids != ws->inline_ordered_team_ids)
    128  1.1  mrg     free (ws->ordered_team_ids);
    129  1.1  mrg   gomp_ptrlock_destroy (&ws->next_ws);
    130  1.1  mrg }
    131  1.1  mrg 
    132  1.1  mrg /* Free a work share struct, if not orphaned, put it into current
    133  1.1  mrg    team's free gomp_work_share cache.  */
    134  1.1  mrg 
    135  1.1  mrg static inline void
    136  1.1  mrg free_work_share (struct gomp_team *team, struct gomp_work_share *ws)
    137  1.1  mrg {
    138  1.1  mrg   gomp_fini_work_share (ws);
    139  1.1  mrg   if (__builtin_expect (team == NULL, 0))
    140  1.1  mrg     free (ws);
    141  1.1  mrg   else
    142  1.1  mrg     {
    143  1.1  mrg       struct gomp_work_share *next_ws;
    144  1.1  mrg #ifdef HAVE_SYNC_BUILTINS
    145  1.1  mrg       do
    146  1.1  mrg 	{
    147  1.1  mrg 	  next_ws = team->work_share_list_free;
    148  1.1  mrg 	  ws->next_free = next_ws;
    149  1.1  mrg 	}
    150  1.1  mrg       while (!__sync_bool_compare_and_swap (&team->work_share_list_free,
    151  1.1  mrg 					    next_ws, ws));
    152  1.1  mrg #else
    153  1.1  mrg       gomp_mutex_lock (&team->work_share_list_free_lock);
    154  1.1  mrg       next_ws = team->work_share_list_free;
    155  1.1  mrg       ws->next_free = next_ws;
    156  1.1  mrg       team->work_share_list_free = ws;
    157  1.1  mrg       gomp_mutex_unlock (&team->work_share_list_free_lock);
    158  1.1  mrg #endif
    159  1.1  mrg     }
    160  1.1  mrg }
    161  1.1  mrg 
    162  1.1  mrg /* The current thread is ready to begin the next work sharing construct.
    163  1.1  mrg    In all cases, thr->ts.work_share is updated to point to the new
    164  1.1  mrg    structure.  In all cases the work_share lock is locked.  Return true
    165  1.1  mrg    if this was the first thread to reach this point.  */
    166  1.1  mrg 
    167  1.1  mrg bool
    168  1.1  mrg gomp_work_share_start (bool ordered)
    169  1.1  mrg {
    170  1.1  mrg   struct gomp_thread *thr = gomp_thread ();
    171  1.1  mrg   struct gomp_team *team = thr->ts.team;
    172  1.1  mrg   struct gomp_work_share *ws;
    173  1.1  mrg 
    174  1.1  mrg   /* Work sharing constructs can be orphaned.  */
    175  1.1  mrg   if (team == NULL)
    176  1.1  mrg     {
    177  1.1  mrg       ws = gomp_malloc (sizeof (*ws));
    178  1.1  mrg       gomp_init_work_share (ws, ordered, 1);
    179  1.1  mrg       thr->ts.work_share = ws;
    180  1.1  mrg       return ws;
    181  1.1  mrg     }
    182  1.1  mrg 
    183  1.1  mrg   ws = thr->ts.work_share;
    184  1.1  mrg   thr->ts.last_work_share = ws;
    185  1.1  mrg   ws = gomp_ptrlock_get (&ws->next_ws);
    186  1.1  mrg   if (ws == NULL)
    187  1.1  mrg     {
    188  1.1  mrg       /* This thread encountered a new ws first.  */
    189  1.1  mrg       struct gomp_work_share *ws = alloc_work_share (team);
    190  1.1  mrg       gomp_init_work_share (ws, ordered, team->nthreads);
    191  1.1  mrg       thr->ts.work_share = ws;
    192  1.1  mrg       return true;
    193  1.1  mrg     }
    194  1.1  mrg   else
    195  1.1  mrg     {
    196  1.1  mrg       thr->ts.work_share = ws;
    197  1.1  mrg       return false;
    198  1.1  mrg     }
    199  1.1  mrg }
    200  1.1  mrg 
    201  1.1  mrg /* The current thread is done with its current work sharing construct.
    202  1.1  mrg    This version does imply a barrier at the end of the work-share.  */
    203  1.1  mrg 
    204  1.1  mrg void
    205  1.1  mrg gomp_work_share_end (void)
    206  1.1  mrg {
    207  1.1  mrg   struct gomp_thread *thr = gomp_thread ();
    208  1.1  mrg   struct gomp_team *team = thr->ts.team;
    209  1.1  mrg   gomp_barrier_state_t bstate;
    210  1.1  mrg 
    211  1.1  mrg   /* Work sharing constructs can be orphaned.  */
    212  1.1  mrg   if (team == NULL)
    213  1.1  mrg     {
    214  1.1  mrg       free_work_share (NULL, thr->ts.work_share);
    215  1.1  mrg       thr->ts.work_share = NULL;
    216  1.1  mrg       return;
    217  1.1  mrg     }
    218  1.1  mrg 
    219  1.1  mrg   bstate = gomp_barrier_wait_start (&team->barrier);
    220  1.1  mrg 
    221  1.1  mrg   if (gomp_barrier_last_thread (bstate))
    222  1.1  mrg     {
    223  1.1  mrg       if (__builtin_expect (thr->ts.last_work_share != NULL, 1))
    224  1.1  mrg 	free_work_share (team, thr->ts.last_work_share);
    225  1.1  mrg     }
    226  1.1  mrg 
    227  1.1  mrg   gomp_team_barrier_wait_end (&team->barrier, bstate);
    228  1.1  mrg   thr->ts.last_work_share = NULL;
    229  1.1  mrg }
    230  1.1  mrg 
    231  1.1  mrg /* The current thread is done with its current work sharing construct.
    232  1.1  mrg    This version does NOT imply a barrier at the end of the work-share.  */
    233  1.1  mrg 
    234  1.1  mrg void
    235  1.1  mrg gomp_work_share_end_nowait (void)
    236  1.1  mrg {
    237  1.1  mrg   struct gomp_thread *thr = gomp_thread ();
    238  1.1  mrg   struct gomp_team *team = thr->ts.team;
    239  1.1  mrg   struct gomp_work_share *ws = thr->ts.work_share;
    240  1.1  mrg   unsigned completed;
    241  1.1  mrg 
    242  1.1  mrg   /* Work sharing constructs can be orphaned.  */
    243  1.1  mrg   if (team == NULL)
    244  1.1  mrg     {
    245  1.1  mrg       free_work_share (NULL, ws);
    246  1.1  mrg       thr->ts.work_share = NULL;
    247  1.1  mrg       return;
    248  1.1  mrg     }
    249  1.1  mrg 
    250  1.1  mrg   if (__builtin_expect (thr->ts.last_work_share == NULL, 0))
    251  1.1  mrg     return;
    252  1.1  mrg 
    253  1.1  mrg #ifdef HAVE_SYNC_BUILTINS
    254  1.1  mrg   completed = __sync_add_and_fetch (&ws->threads_completed, 1);
    255  1.1  mrg #else
    256  1.1  mrg   gomp_mutex_lock (&ws->lock);
    257  1.1  mrg   completed = ++ws->threads_completed;
    258  1.1  mrg   gomp_mutex_unlock (&ws->lock);
    259  1.1  mrg #endif
    260  1.1  mrg 
    261  1.1  mrg   if (completed == team->nthreads)
    262  1.1  mrg     free_work_share (team, thr->ts.last_work_share);
    263  1.1  mrg   thr->ts.last_work_share = NULL;
    264  1.1  mrg }
    265