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