Line data Source code
1 : /*
2 : Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3 :
4 : This file is part of libzmq, the ZeroMQ core engine in C++.
5 :
6 : libzmq is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU Lesser General Public License (LGPL) as published
8 : by the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : As a special exception, the Contributors give you permission to link
12 : this library with independent modules to produce an executable,
13 : regardless of the license terms of these independent modules, and to
14 : copy and distribute the resulting executable under terms of your choice,
15 : provided that you also meet, for each linked independent module, the
16 : terms and conditions of the license of that module. An independent
17 : module is a module which is not derived from or based on this library.
18 : If you modify this library, you must extend this exception to your
19 : version of the library.
20 :
21 : libzmq is distributed in the hope that it will be useful, but WITHOUT
22 : ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24 : License for more details.
25 :
26 : You should have received a copy of the GNU Lesser General Public License
27 : along with this program. If not, see <http://www.gnu.org/licenses/>.
28 : */
29 :
30 : #ifndef __ZMQ_ATOMIC_PTR_HPP_INCLUDED__
31 : #define __ZMQ_ATOMIC_PTR_HPP_INCLUDED__
32 :
33 : #include "platform.hpp"
34 :
35 : #if defined ZMQ_FORCE_MUTEXES
36 : #define ZMQ_ATOMIC_PTR_MUTEX
37 : #elif defined ZMQ_HAVE_ATOMIC_INTRINSICS
38 : #define ZMQ_ATOMIC_PTR_INTRINSIC
39 : #elif (defined ZMQ_CXX11 && defined __cplusplus && __cplusplus >= 201103L)
40 : #define ZMQ_ATOMIC_PTR_CXX11
41 : #elif (defined __i386__ || defined __x86_64__) && defined __GNUC__
42 : #define ZMQ_ATOMIC_PTR_X86
43 : #elif defined __ARM_ARCH_7A__ && defined __GNUC__
44 : #define ZMQ_ATOMIC_PTR_ARM
45 : #elif defined __tile__
46 : #define ZMQ_ATOMIC_PTR_TILE
47 : #elif defined ZMQ_HAVE_WINDOWS
48 : #define ZMQ_ATOMIC_PTR_WINDOWS
49 : #elif (defined ZMQ_HAVE_SOLARIS || defined ZMQ_HAVE_NETBSD || defined ZMQ_HAVE_GNU)
50 : #define ZMQ_ATOMIC_PTR_ATOMIC_H
51 : #else
52 : #define ZMQ_ATOMIC_PTR_MUTEX
53 : #endif
54 :
55 : #if defined ZMQ_ATOMIC_PTR_MUTEX
56 : #include "mutex.hpp"
57 : #elif defined ZMQ_ATOMIC_PTR_CXX11
58 : #include <atomic>
59 : #elif defined ZMQ_ATOMIC_PTR_WINDOWS
60 : #include "windows.hpp"
61 : #elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
62 : #include <atomic.h>
63 : #elif defined ZMQ_ATOMIC_PTR_TILE
64 : #include <arch/atomic.h>
65 : #endif
66 :
67 : namespace zmq
68 : {
69 :
70 : // This class encapsulates several atomic operations on pointers.
71 :
72 : template <typename T> class atomic_ptr_t
73 : {
74 : public:
75 :
76 : // Initialise atomic pointer
77 : inline atomic_ptr_t ()
78 : {
79 56686 : ptr = NULL;
80 : }
81 :
82 : // Destroy atomic pointer
83 : inline ~atomic_ptr_t ()
84 : {
85 : }
86 :
87 : // Set value of atomic pointer in a non-threadsafe way
88 : // Use this function only when you are sure that at most one
89 : // thread is accessing the pointer at the moment.
90 : inline void set (T *ptr_)
91 : {
92 78516 : this->ptr = ptr_;
93 : }
94 :
95 : // Perform atomic 'exchange pointers' operation. Pointer is set
96 : // to the 'val' value. Old value is returned.
97 : inline T *xchg (T *val_)
98 : {
99 : #if defined ZMQ_ATOMIC_PTR_WINDOWS
100 : return (T*) InterlockedExchangePointer ((PVOID*) &ptr, val_);
101 : #elif defined ZMQ_ATOMIC_PTR_INTRINSIC
102 52843 : return (T*) __atomic_exchange_n (&ptr, val_, __ATOMIC_ACQ_REL);
103 : #elif defined ZMQ_ATOMIC_PTR_CXX11
104 : return ptr.exchange(val_, std::memory_order_acq_rel);
105 : #elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
106 : return (T*) atomic_swap_ptr (&ptr, val_);
107 : #elif defined ZMQ_ATOMIC_PTR_TILE
108 : return (T*) arch_atomic_exchange (&ptr, val_);
109 : #elif defined ZMQ_ATOMIC_PTR_X86
110 : T *old;
111 : __asm__ volatile (
112 : "lock; xchg %0, %2"
113 : : "=r" (old), "=m" (ptr)
114 : : "m" (ptr), "0" (val_));
115 : return old;
116 : #elif defined ZMQ_ATOMIC_PTR_ARM
117 : T* old;
118 : unsigned int flag;
119 : __asm__ volatile (
120 : " dmb sy\n\t"
121 : "1: ldrex %1, [%3]\n\t"
122 : " strex %0, %4, [%3]\n\t"
123 : " teq %0, #0\n\t"
124 : " bne 1b\n\t"
125 : " dmb sy\n\t"
126 : : "=&r"(flag), "=&r"(old), "+Qo"(ptr)
127 : : "r"(&ptr), "r"(val_)
128 : : "cc");
129 : return old;
130 : #elif defined ZMQ_ATOMIC_PTR_MUTEX
131 : sync.lock ();
132 : T *old = (T*) ptr;
133 : ptr = val_;
134 : sync.unlock ();
135 : return old;
136 : #else
137 : #error atomic_ptr is not implemented for this platform
138 : #endif
139 : }
140 :
141 : // Perform atomic 'compare and swap' operation on the pointer.
142 : // The pointer is compared to 'cmp' argument and if they are
143 : // equal, its value is set to 'val'. Old value of the pointer
144 : // is returned.
145 : inline T *cas (T *cmp_, T *val_)
146 : {
147 : #if defined ZMQ_ATOMIC_PTR_WINDOWS
148 : return (T*) InterlockedCompareExchangePointer (
149 : (volatile PVOID*) &ptr, val_, cmp_);
150 : #elif defined ZMQ_ATOMIC_PTR_INTRINSIC
151 1171046 : T *old = cmp_;
152 1171046 : __atomic_compare_exchange_n (&ptr, (volatile T**) &old, val_, false,
153 1171046 : __ATOMIC_RELEASE, __ATOMIC_ACQUIRE);
154 1171046 : return old;
155 : #elif defined ZMQ_ATOMIC_PTR_CXX11
156 : ptr.compare_exchange_strong(cmp_, val_, std::memory_order_acq_rel);
157 : return cmp_;
158 : #elif defined ZMQ_ATOMIC_PTR_ATOMIC_H
159 : return (T*) atomic_cas_ptr (&ptr, cmp_, val_);
160 : #elif defined ZMQ_ATOMIC_PTR_TILE
161 : return (T*) arch_atomic_val_compare_and_exchange (&ptr, cmp_, val_);
162 : #elif defined ZMQ_ATOMIC_PTR_X86
163 : T *old;
164 : __asm__ volatile (
165 : "lock; cmpxchg %2, %3"
166 : : "=a" (old), "=m" (ptr)
167 : : "r" (val_), "m" (ptr), "0" (cmp_)
168 : : "cc");
169 : return old;
170 : #elif defined ZMQ_ATOMIC_PTR_ARM
171 : T *old;
172 : unsigned int flag;
173 : __asm__ volatile (
174 : " dmb sy\n\t"
175 : "1: ldrex %1, [%3]\n\t"
176 : " mov %0, #0\n\t"
177 : " teq %1, %4\n\t"
178 : " it eq\n\t"
179 : " strexeq %0, %5, [%3]\n\t"
180 : " teq %0, #0\n\t"
181 : " bne 1b\n\t"
182 : " dmb sy\n\t"
183 : : "=&r"(flag), "=&r"(old), "+Qo"(ptr)
184 : : "r"(&ptr), "r"(cmp_), "r"(val_)
185 : : "cc");
186 : return old;
187 : #elif defined ZMQ_ATOMIC_PTR_MUTEX
188 : sync.lock ();
189 : T *old = (T*) ptr;
190 : if (ptr == cmp_)
191 : ptr = val_;
192 : sync.unlock ();
193 : return old;
194 : #else
195 : #error atomic_ptr is not implemented for this platform
196 : #endif
197 : }
198 :
199 : private:
200 :
201 : #if defined ZMQ_ATOMIC_PTR_CXX11
202 : std::atomic<T*> ptr;
203 : #else
204 : volatile T *ptr;
205 : #endif
206 :
207 : #if defined ZMQ_ATOMIC_PTR_MUTEX
208 : mutex_t sync;
209 : #endif
210 :
211 : #if ! defined ZMQ_ATOMIC_PTR_CXX11
212 : atomic_ptr_t (const atomic_ptr_t&);
213 : const atomic_ptr_t &operator = (const atomic_ptr_t&);
214 : #endif
215 : };
216 :
217 : }
218 :
219 : // Remove macros local to this file.
220 : #undef ZMQ_ATOMIC_PTR_MUTEX
221 : #undef ZMQ_ATOMIC_PTR_INTRINSIC
222 : #undef ZMQ_ATOMIC_PTR_CXX11
223 : #undef ZMQ_ATOMIC_PTR_X86
224 : #undef ZMQ_ATOMIC_PTR_ARM
225 : #undef ZMQ_ATOMIC_PTR_TILE
226 : #undef ZMQ_ATOMIC_PTR_WINDOWS
227 : #undef ZMQ_ATOMIC_PTR_ATOMIC_H
228 :
229 : #endif
|