sem.c revision 1.1.1.9
1169092Sdeischen/* Copyright (C) 2005-2019 Free Software Foundation, Inc.
2169092Sdeischen   Contributed by Richard Henderson <rth@redhat.com>.
3169092Sdeischen
4164190Sjkoshy   This file is part of the GNU Offloading and Multi Processing Library
5164190Sjkoshy   (libgomp).
6164190Sjkoshy
7164190Sjkoshy   Libgomp is free software; you can redistribute it and/or modify it
8164190Sjkoshy   under the terms of the GNU General Public License as published by
9164190Sjkoshy   the Free Software Foundation; either version 3, or (at your option)
10164190Sjkoshy   any later version.
11164190Sjkoshy
12164190Sjkoshy   Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
13164190Sjkoshy   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14164190Sjkoshy   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15164190Sjkoshy   more details.
16164190Sjkoshy
17164190Sjkoshy   Under Section 7 of GPL version 3, you are granted additional
18164190Sjkoshy   permissions described in the GCC Runtime Library Exception, version
19164190Sjkoshy   3.1, as published by the Free Software Foundation.
20164190Sjkoshy
21164190Sjkoshy   You should have received a copy of the GNU General Public License and
22164190Sjkoshy   a copy of the GCC Runtime Library Exception along with this program;
23164190Sjkoshy   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24164190Sjkoshy   <http://www.gnu.org/licenses/>.  */
25164190Sjkoshy
26164190Sjkoshy/* This is a Linux specific implementation of a semaphore synchronization
27164190Sjkoshy   mechanism for libgomp.  This type is private to the library.  This
28164190Sjkoshy   implementation uses atomic instructions and the futex syscall.  */
29164190Sjkoshy
30164190Sjkoshy#include "wait.h"
31164190Sjkoshy
32164190Sjkoshyvoid
33164190Sjkoshygomp_sem_wait_slow (gomp_sem_t *sem, int count)
34164190Sjkoshy{
35164190Sjkoshy  /* First loop spins a while.  */
36164190Sjkoshy  while (count == 0)
37164190Sjkoshy    if (do_spin (sem, 0)
38164190Sjkoshy	/* Spin timeout, nothing changed.  Set waiting flag.  */
39164190Sjkoshy	&& __atomic_compare_exchange_n (sem, &count, SEM_WAIT, false,
40164190Sjkoshy					MEMMODEL_ACQUIRE, MEMMODEL_RELAXED))
41164190Sjkoshy      {
42165535Sjkoshy	futex_wait (sem, SEM_WAIT);
43164190Sjkoshy	count = *sem;
44164190Sjkoshy	break;
45164190Sjkoshy      }
46164190Sjkoshy  /* Something changed.  If it wasn't the wait flag, we're good to go.  */
47164190Sjkoshy    else if (__builtin_expect (((count = *sem) & SEM_WAIT) == 0 && count != 0,
48164190Sjkoshy			       1))
49164190Sjkoshy      {
50164190Sjkoshy	if (__atomic_compare_exchange_n (sem, &count, count - SEM_INC, false,
51164190Sjkoshy					 MEMMODEL_ACQUIRE, MEMMODEL_RELAXED))
52164190Sjkoshy	  return;
53164190Sjkoshy      }
54164190Sjkoshy
55164190Sjkoshy  /* Second loop waits until semaphore is posted.  We always exit this
56164190Sjkoshy     loop with wait flag set, so next post will awaken a thread.  */
57164190Sjkoshy  while (1)
58164190Sjkoshy    {
59164190Sjkoshy      unsigned int wake = count & ~SEM_WAIT;
60164190Sjkoshy      int newval = SEM_WAIT;
61164190Sjkoshy
62164190Sjkoshy      if (wake != 0)
63164190Sjkoshy	newval |= wake - SEM_INC;
64164190Sjkoshy      if (__atomic_compare_exchange_n (sem, &count, newval, false,
65164190Sjkoshy				       MEMMODEL_ACQUIRE, MEMMODEL_RELAXED))
66164190Sjkoshy	{
67164190Sjkoshy	  if (wake != 0)
68164190Sjkoshy	    {
69164190Sjkoshy	      /* If we can wake more threads, do so now.  */
70164190Sjkoshy	      if (wake > SEM_INC)
71164190Sjkoshy		gomp_sem_post_slow (sem);
72164190Sjkoshy	      break;
73164190Sjkoshy	    }
74164190Sjkoshy	  do_wait (sem, SEM_WAIT);
75164190Sjkoshy	  count = *sem;
76164190Sjkoshy	}
77164190Sjkoshy    }
78164190Sjkoshy}
79164190Sjkoshy
80164190Sjkoshyvoid
81164190Sjkoshygomp_sem_post_slow (gomp_sem_t *sem)
82164190Sjkoshy{
83164190Sjkoshy  futex_wake (sem, 1);
84164190Sjkoshy}
85164190Sjkoshy