1/* Copyright (C) 2005-2017 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