1/* Copyright (C) 2005-2020 Free Software Foundation, Inc.
2   Contributed by Richard Henderson <rth@redhat.com>.
3
4   This file is part of the GNU Offloading and Multi Processing Library
5   (libgomp).
6
7   Libgomp is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3, or (at your option)
10   any later version.
11
12   Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15   more details.
16
17   Under Section 7 of GPL version 3, you are granted additional
18   permissions described in the GCC Runtime Library Exception, version
19   3.1, as published by the Free Software Foundation.
20
21   You should have received a copy of the GNU General Public License and
22   a copy of the GCC Runtime Library Exception along with this program;
23   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24   <http://www.gnu.org/licenses/>.  */
25
26/* This is a Linux specific implementation of the public OpenMP locking
27   primitives.  This implementation uses atomic instructions and the futex
28   syscall.  */
29
30#include <string.h>
31#include <unistd.h>
32#include <sys/syscall.h>
33#include "wait.h"
34
35/* Reuse the generic implementation in terms of gomp_mutex_t.  */
36#include "../../lock.c"
37
38#ifdef LIBGOMP_GNU_SYMBOL_VERSIONING
39/* gomp_mutex_* can be safely locked in one thread and
40   unlocked in another thread, so the OpenMP 2.5 and OpenMP 3.0
41   non-nested locks can be the same.  */
42strong_alias (gomp_init_lock_30, gomp_init_lock_25)
43strong_alias (gomp_destroy_lock_30, gomp_destroy_lock_25)
44strong_alias (gomp_set_lock_30, gomp_set_lock_25)
45strong_alias (gomp_unset_lock_30, gomp_unset_lock_25)
46strong_alias (gomp_test_lock_30, gomp_test_lock_25)
47
48/* The external recursive omp_nest_lock_25_t form requires additional work.  */
49
50/* We need an integer to uniquely identify this thread.  Most generally
51   this is the thread's TID, which ideally we'd get this straight from
52   the TLS block where glibc keeps it.  Unfortunately, we can't get at
53   that directly.
54
55   If we don't support (or have disabled) TLS, one function call is as
56   good (or bad) as any other.  Use the syscall all the time.
57
58   On an ILP32 system (defined here as not LP64), we can make do with
59   any thread-local pointer.  Ideally we'd use the TLS base address,
60   since that requires the least amount of arithmetic, but that's not
61   always available directly.  Make do with the gomp_thread pointer
62   since it's handy.  */
63
64# if !defined (HAVE_TLS)
65static inline int gomp_tid (void)
66{
67  return syscall (SYS_gettid);
68}
69# elif !defined(__LP64__)
70static inline int gomp_tid (void)
71{
72  return (int) gomp_thread ();
73}
74# else
75static __thread int tid_cache;
76static inline int gomp_tid (void)
77{
78  int tid = tid_cache;
79  if (__builtin_expect (tid == 0, 0))
80    tid_cache = tid = syscall (SYS_gettid);
81  return tid;
82}
83# endif
84
85
86void
87gomp_init_nest_lock_25 (omp_nest_lock_25_t *lock)
88{
89  memset (lock, 0, sizeof (*lock));
90}
91
92void
93gomp_destroy_nest_lock_25 (omp_nest_lock_25_t *lock)
94{
95}
96
97void
98gomp_set_nest_lock_25 (omp_nest_lock_25_t *lock)
99{
100  int otid, tid = gomp_tid ();
101
102  while (1)
103    {
104      otid = 0;
105      if (__atomic_compare_exchange_n (&lock->owner, &otid, tid, false,
106				       MEMMODEL_ACQUIRE, MEMMODEL_RELAXED))
107	{
108	  lock->count = 1;
109	  return;
110	}
111      if (otid == tid)
112	{
113	  lock->count++;
114	  return;
115	}
116
117      do_wait (&lock->owner, otid);
118    }
119}
120
121void
122gomp_unset_nest_lock_25 (omp_nest_lock_25_t *lock)
123{
124  /* ??? Validate that we own the lock here.  */
125
126  if (--lock->count == 0)
127    {
128      __atomic_store_n (&lock->owner, 0, MEMMODEL_RELEASE);
129      futex_wake (&lock->owner, 1);
130    }
131}
132
133int
134gomp_test_nest_lock_25 (omp_nest_lock_25_t *lock)
135{
136  int otid, tid = gomp_tid ();
137
138  otid = 0;
139  if (__atomic_compare_exchange_n (&lock->owner, &otid, tid, false,
140				   MEMMODEL_ACQUIRE, MEMMODEL_RELAXED))
141    {
142      lock->count = 1;
143      return 1;
144    }
145  if (otid == tid)
146    return ++lock->count;
147
148  return 0;
149}
150
151omp_lock_symver (omp_init_lock)
152omp_lock_symver (omp_destroy_lock)
153omp_lock_symver (omp_set_lock)
154omp_lock_symver (omp_unset_lock)
155omp_lock_symver (omp_test_lock)
156omp_lock_symver (omp_init_nest_lock)
157omp_lock_symver (omp_destroy_nest_lock)
158omp_lock_symver (omp_set_nest_lock)
159omp_lock_symver (omp_unset_nest_lock)
160omp_lock_symver (omp_test_nest_lock)
161
162#else
163
164ialias (omp_init_lock)
165ialias (omp_init_nest_lock)
166ialias (omp_destroy_lock)
167ialias (omp_destroy_nest_lock)
168ialias (omp_set_lock)
169ialias (omp_set_nest_lock)
170ialias (omp_unset_lock)
171ialias (omp_unset_nest_lock)
172ialias (omp_test_lock)
173ialias (omp_test_nest_lock)
174
175#endif
176