1/* Copyright (C) 2005-2015 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
36/* The internal gomp_mutex_t and the external non-recursive omp_lock_t
37   have the same form.  Re-use it.  */
38
39void
40gomp_init_lock_30 (omp_lock_t *lock)
41{
42  gomp_mutex_init (lock);
43}
44
45void
46gomp_destroy_lock_30 (omp_lock_t *lock)
47{
48  gomp_mutex_destroy (lock);
49}
50
51void
52gomp_set_lock_30 (omp_lock_t *lock)
53{
54  gomp_mutex_lock (lock);
55}
56
57void
58gomp_unset_lock_30 (omp_lock_t *lock)
59{
60  gomp_mutex_unlock (lock);
61}
62
63int
64gomp_test_lock_30 (omp_lock_t *lock)
65{
66  int oldval = 0;
67
68  return __atomic_compare_exchange_n (lock, &oldval, 1, false,
69				      MEMMODEL_ACQUIRE, MEMMODEL_RELAXED);
70}
71
72void
73gomp_init_nest_lock_30 (omp_nest_lock_t *lock)
74{
75  memset (lock, '\0', sizeof (*lock));
76}
77
78void
79gomp_destroy_nest_lock_30 (omp_nest_lock_t *lock)
80{
81}
82
83void
84gomp_set_nest_lock_30 (omp_nest_lock_t *lock)
85{
86  void *me = gomp_icv (true);
87
88  if (lock->owner != me)
89    {
90      gomp_mutex_lock (&lock->lock);
91      lock->owner = me;
92    }
93
94  lock->count++;
95}
96
97void
98gomp_unset_nest_lock_30 (omp_nest_lock_t *lock)
99{
100  if (--lock->count == 0)
101    {
102      lock->owner = NULL;
103      gomp_mutex_unlock (&lock->lock);
104    }
105}
106
107int
108gomp_test_nest_lock_30 (omp_nest_lock_t *lock)
109{
110  void *me = gomp_icv (true);
111  int oldval;
112
113  if (lock->owner == me)
114    return ++lock->count;
115
116  oldval = 0;
117  if (__atomic_compare_exchange_n (&lock->lock, &oldval, 1, false,
118				   MEMMODEL_ACQUIRE, MEMMODEL_RELAXED))
119    {
120      lock->owner = me;
121      lock->count = 1;
122      return 1;
123    }
124
125  return 0;
126}
127
128#ifdef LIBGOMP_GNU_SYMBOL_VERSIONING
129/* gomp_mutex_* can be safely locked in one thread and
130   unlocked in another thread, so the OpenMP 2.5 and OpenMP 3.0
131   non-nested locks can be the same.  */
132strong_alias (gomp_init_lock_30, gomp_init_lock_25)
133strong_alias (gomp_destroy_lock_30, gomp_destroy_lock_25)
134strong_alias (gomp_set_lock_30, gomp_set_lock_25)
135strong_alias (gomp_unset_lock_30, gomp_unset_lock_25)
136strong_alias (gomp_test_lock_30, gomp_test_lock_25)
137
138/* The external recursive omp_nest_lock_25_t form requires additional work.  */
139
140/* We need an integer to uniquely identify this thread.  Most generally
141   this is the thread's TID, which ideally we'd get this straight from
142   the TLS block where glibc keeps it.  Unfortunately, we can't get at
143   that directly.
144
145   If we don't support (or have disabled) TLS, one function call is as
146   good (or bad) as any other.  Use the syscall all the time.
147
148   On an ILP32 system (defined here as not LP64), we can make do with
149   any thread-local pointer.  Ideally we'd use the TLS base address,
150   since that requires the least amount of arithmetic, but that's not
151   always available directly.  Make do with the gomp_thread pointer
152   since it's handy.  */
153
154# if !defined (HAVE_TLS)
155static inline int gomp_tid (void)
156{
157  return syscall (SYS_gettid);
158}
159# elif !defined(__LP64__)
160static inline int gomp_tid (void)
161{
162  return (int) gomp_thread ();
163}
164# else
165static __thread int tid_cache;
166static inline int gomp_tid (void)
167{
168  int tid = tid_cache;
169  if (__builtin_expect (tid == 0, 0))
170    tid_cache = tid = syscall (SYS_gettid);
171  return tid;
172}
173# endif
174
175
176void
177gomp_init_nest_lock_25 (omp_nest_lock_25_t *lock)
178{
179  memset (lock, 0, sizeof (*lock));
180}
181
182void
183gomp_destroy_nest_lock_25 (omp_nest_lock_25_t *lock)
184{
185}
186
187void
188gomp_set_nest_lock_25 (omp_nest_lock_25_t *lock)
189{
190  int otid, tid = gomp_tid ();
191
192  while (1)
193    {
194      otid = 0;
195      if (__atomic_compare_exchange_n (&lock->owner, &otid, tid, false,
196				       MEMMODEL_ACQUIRE, MEMMODEL_RELAXED))
197	{
198	  lock->count = 1;
199	  return;
200	}
201      if (otid == tid)
202	{
203	  lock->count++;
204	  return;
205	}
206
207      do_wait (&lock->owner, otid);
208    }
209}
210
211void
212gomp_unset_nest_lock_25 (omp_nest_lock_25_t *lock)
213{
214  /* ??? Validate that we own the lock here.  */
215
216  if (--lock->count == 0)
217    {
218      __atomic_store_n (&lock->owner, 0, MEMMODEL_RELEASE);
219      futex_wake (&lock->owner, 1);
220    }
221}
222
223int
224gomp_test_nest_lock_25 (omp_nest_lock_25_t *lock)
225{
226  int otid, tid = gomp_tid ();
227
228  otid = 0;
229  if (__atomic_compare_exchange_n (&lock->owner, &otid, tid, false,
230				   MEMMODEL_ACQUIRE, MEMMODEL_RELAXED))
231    {
232      lock->count = 1;
233      return 1;
234    }
235  if (otid == tid)
236    return ++lock->count;
237
238  return 0;
239}
240
241omp_lock_symver (omp_init_lock)
242omp_lock_symver (omp_destroy_lock)
243omp_lock_symver (omp_set_lock)
244omp_lock_symver (omp_unset_lock)
245omp_lock_symver (omp_test_lock)
246omp_lock_symver (omp_init_nest_lock)
247omp_lock_symver (omp_destroy_nest_lock)
248omp_lock_symver (omp_set_nest_lock)
249omp_lock_symver (omp_unset_nest_lock)
250omp_lock_symver (omp_test_nest_lock)
251
252#else
253
254ialias (omp_init_lock)
255ialias (omp_init_nest_lock)
256ialias (omp_destroy_lock)
257ialias (omp_destroy_nest_lock)
258ialias (omp_set_lock)
259ialias (omp_set_nest_lock)
260ialias (omp_unset_lock)
261ialias (omp_unset_nest_lock)
262ialias (omp_test_lock)
263ialias (omp_test_nest_lock)
264
265#endif
266