Merged branch 'master' of https://github.com/Nemo1369/libcds
[libcds.git] / cds / compiler / vc / x86 / cxx11_atomic.h
1 /*
2     This file is a part of libcds - Concurrent Data Structures library
3
4     (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
5
6     Source code repo: http://github.com/khizmax/libcds/
7     Download: http://sourceforge.net/projects/libcds/files/
8
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions are met:
11
12     * Redistributions of source code must retain the above copyright notice, this
13       list of conditions and the following disclaimer.
14
15     * Redistributions in binary form must reproduce the above copyright notice,
16       this list of conditions and the following disclaimer in the documentation
17       and/or other materials provided with the distribution.
18
19     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #ifndef CDSLIB_COMPILER_VC_X86_CXX11_ATOMIC_H
32 #define CDSLIB_COMPILER_VC_X86_CXX11_ATOMIC_H
33
34 #include <intrin.h>
35 #include <emmintrin.h>  // for 64bit atomic load/store
36 #include <cds/details/is_aligned.h>
37
38 #pragma intrinsic( _InterlockedIncrement )
39 #pragma intrinsic( _InterlockedDecrement )
40 #pragma intrinsic( _InterlockedCompareExchange )
41 //#pragma intrinsic( _InterlockedCompareExchangePointer )   // On the x86 architecture, _InterlockedCompareExchangePointer is a macro that calls _InterlockedCompareExchange
42 #pragma intrinsic( _InterlockedCompareExchange16 )
43 #pragma intrinsic( _InterlockedCompareExchange64 )
44 #pragma intrinsic( _InterlockedExchange )
45 //#pragma intrinsic( _InterlockedExchangePointer )  // On the x86 architecture, _InterlockedExchangePointer is a macro that calls _InterlockedExchange
46 #pragma intrinsic( _InterlockedExchangeAdd )
47 #pragma intrinsic( _InterlockedXor )
48 #pragma intrinsic( _InterlockedOr )
49 #pragma intrinsic( _InterlockedAnd )
50 #pragma intrinsic( _interlockedbittestandset )
51 #if _MSC_VER >= 1600
52 #   pragma intrinsic( _InterlockedCompareExchange8 )
53 #   pragma intrinsic( _InterlockedExchange8 )
54 #   pragma intrinsic( _InterlockedExchange16 )
55 #endif
56
57 //@cond
58 namespace cds { namespace cxx11_atomic {
59     namespace platform { CDS_CXX11_INLINE_NAMESPACE namespace vc { CDS_CXX11_INLINE_NAMESPACE namespace x86 {
60
61         static inline void fence_before( memory_order order ) CDS_NOEXCEPT
62         {
63             switch(order) {
64             case memory_order_relaxed:
65             case memory_order_acquire:
66             case memory_order_consume:
67                 break;
68             case memory_order_release:
69             case memory_order_acq_rel:
70                 CDS_COMPILER_RW_BARRIER;
71                 break;
72             case memory_order_seq_cst:
73                 CDS_COMPILER_RW_BARRIER;
74                 break;
75             }
76         }
77
78         static inline void fence_after( memory_order order ) CDS_NOEXCEPT
79         {
80             switch(order) {
81             case memory_order_acquire:
82             case memory_order_acq_rel:
83                 CDS_COMPILER_RW_BARRIER;
84                 break;
85             case memory_order_relaxed:
86             case memory_order_consume:
87             case memory_order_release:
88                 break;
89             case memory_order_seq_cst:
90                 CDS_COMPILER_RW_BARRIER;
91                 break;
92             }
93         }
94
95
96         static inline void fence_after_load(memory_order order) CDS_NOEXCEPT
97         {
98             switch(order) {
99             case memory_order_relaxed:
100             case memory_order_release:
101                 break;
102             case memory_order_acquire:
103             case memory_order_acq_rel:
104                 CDS_COMPILER_RW_BARRIER;
105                 break;
106             case memory_order_consume:
107                 break;
108             case memory_order_seq_cst:
109                 __asm { mfence };
110                 break;
111             default:;
112             }
113         }
114
115         //-----------------------------------------------------------------------------
116         // fences
117         //-----------------------------------------------------------------------------
118         static inline void thread_fence(memory_order order) CDS_NOEXCEPT
119         {
120             switch(order)
121             {
122                 case memory_order_relaxed:
123                 case memory_order_consume:
124                     break;
125                 case memory_order_release:
126                 case memory_order_acquire:
127                 case memory_order_acq_rel:
128                     CDS_COMPILER_RW_BARRIER;
129                     break;
130                 case memory_order_seq_cst:
131                     __asm { mfence };
132                     break;
133                 default:;
134             }
135         }
136
137         static inline void signal_fence(memory_order order) CDS_NOEXCEPT
138         {
139             // C++11: 29.8.8: only compiler optimization, no hardware instructions
140             switch(order)
141             {
142                 case memory_order_relaxed:
143                     break;
144                 case memory_order_consume:
145                 case memory_order_release:
146                 case memory_order_acquire:
147                 case memory_order_acq_rel:
148                 case memory_order_seq_cst:
149                     CDS_COMPILER_RW_BARRIER;
150                     break;
151                 default:;
152             }
153         }
154
155         //-----------------------------------------------------------------------------
156         // atomic flag primitives
157         //-----------------------------------------------------------------------------
158
159         typedef unsigned char atomic_flag_type;
160         static inline bool atomic_flag_tas( atomic_flag_type volatile * pFlag, memory_order /*order*/ ) CDS_NOEXCEPT
161         {
162             return _interlockedbittestandset( (long volatile *) pFlag, 0 ) != 0;
163         }
164
165         static inline void atomic_flag_clear( atomic_flag_type volatile * pFlag, memory_order order ) CDS_NOEXCEPT
166         {
167             assert( order != memory_order_acquire
168                 && order != memory_order_acq_rel
169                 );
170
171             fence_before( order );
172             *pFlag = 0;
173             fence_after( order );
174         }
175
176
177         //-----------------------------------------------------------------------------
178         // 8bit primitives
179         //-----------------------------------------------------------------------------
180
181 #if _MSC_VER >= 1600
182 #   pragma warning(push)
183         // Disable warning C4800: 'char' : forcing value to bool 'true' or 'false' (performance warning)
184 #   pragma warning( disable: 4800 )
185 #endif
186         template <typename T>
187         static inline bool cas8_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
188         {
189             static_assert( sizeof(T) == 1, "Illegal operand size"  );
190
191 #       if _MSC_VER >= 1600
192             T prev = expected;
193             expected = (T) _InterlockedCompareExchange8( reinterpret_cast<char volatile*>(pDest), (char) desired, (char) expected );
194             return expected == prev;
195 #       else
196             bool bRet = false;
197             __asm {
198                 mov ecx, pDest;
199                 mov edx, expected;
200                 mov al, byte ptr [edx];
201                 mov ah, desired;
202                 lock cmpxchg byte ptr [ecx], ah;
203                 mov byte ptr [edx], al;
204                 setz bRet;
205             }
206             return bRet;
207 #       endif
208         }
209 #if _MSC_VER >= 1600
210 #   pragma warning(pop)
211 #endif
212
213         template <typename T>
214         static inline bool cas8_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
215         {
216             return cas8_strong( pDest, expected, desired, mo_success, mo_fail );
217         }
218
219 #if _MSC_VER >= 1600
220 #   pragma warning(push)
221     // Disable warning C4800: 'char' : forcing value to bool 'true' or 'false' (performance warning)
222 #   pragma warning( disable: 4800 )
223 #endif
224         template <typename T>
225         static inline T exchange8( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
226         {
227             static_assert( sizeof(T) == 1, "Illegal operand size" );
228
229 #       if _MSC_VER >= 1600
230             return (T) _InterlockedExchange8( reinterpret_cast<char volatile *>(pDest), (char) v );
231 #       else
232             __asm {
233                 mov al, v;
234                 mov ecx, pDest;
235                 lock xchg byte ptr [ecx], al;
236             }
237 #       endif
238         }
239 #if _MSC_VER >= 1600
240 #   pragma warning(pop)
241 #endif
242
243         template <typename T>
244         static inline void store8( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
245         {
246             static_assert( sizeof(T) == 1, "Illegal operand size" );
247             assert( order ==  memory_order_relaxed
248                 || order ==  memory_order_release
249                 || order == memory_order_seq_cst
250                 );
251             assert( pDest );
252
253             if ( order != memory_order_seq_cst ) {
254                 fence_before( order );
255                 *pDest = src;
256             }
257             else {
258                 exchange8( pDest, src, order );
259             }
260         }
261
262         template <typename T>
263         static inline T load8( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
264         {
265             static_assert( sizeof(T) == 1, "Illegal operand size" );
266             assert( order ==  memory_order_relaxed
267                 || order ==  memory_order_consume
268                 || order ==  memory_order_acquire
269                 || order == memory_order_seq_cst
270                 );
271             assert( pSrc );
272
273             T v = *pSrc;
274             fence_after_load( order );
275             return v;
276         }
277
278         //-----------------------------------------------------------------------------
279         // 16bit primitives
280         //-----------------------------------------------------------------------------
281
282         template <typename T>
283         static inline T exchange16( T volatile * pDest, T v, memory_order /*order*/ ) CDS_NOEXCEPT
284         {
285             static_assert( sizeof(T) == 2, "Illegal operand size" );
286             assert( cds::details::is_aligned( pDest, 2 ));
287
288 #       if _MSC_VER >= 1600
289             return (T) _InterlockedExchange16( (short volatile *) pDest, (short) v );
290 #       else
291             __asm {
292                 mov ax, v;
293                 mov ecx, pDest;
294                 lock xchg word ptr [ecx], ax;
295             }
296 #       endif
297         }
298
299         template <typename T>
300         static inline void store16( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
301         {
302             static_assert( sizeof(T) == 2, "Illegal operand size" );
303             assert( order ==  memory_order_relaxed
304                 || order ==  memory_order_release
305                 || order == memory_order_seq_cst
306                 );
307             assert( pDest );
308             assert( cds::details::is_aligned( pDest, 2 ));
309
310             if ( order != memory_order_seq_cst ) {
311                 fence_before( order );
312                 *pDest = src;
313             }
314             else {
315                 exchange16( pDest, src, order );
316             }
317         }
318
319         template <typename T>
320         static inline T load16( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
321         {
322             static_assert( sizeof(T) == 2, "Illegal operand size" );
323             assert( order ==  memory_order_relaxed
324                 || order ==  memory_order_consume
325                 || order ==  memory_order_acquire
326                 || order ==  memory_order_seq_cst
327                 );
328             assert( pSrc );
329             assert( cds::details::is_aligned( pSrc, 2 ));
330
331             T v = *pSrc;
332             fence_after_load( order );
333             return v;
334         }
335
336         template <typename T>
337         static inline bool cas16_strong( T volatile * pDest, T& expected, T desired, memory_order /*mo_success*/, memory_order /*mo_fail*/ ) CDS_NOEXCEPT
338         {
339             static_assert( sizeof(T) == 2, "Illegal operand size" );
340             assert( cds::details::is_aligned( pDest, 2 ));
341
342             // _InterlockedCompareExchange behave as read-write memory barriers
343             T prev = expected;
344             expected = (T) _InterlockedCompareExchange16( (short *) pDest, (short) desired, (short) expected );
345             return expected == prev;
346         }
347
348         template <typename T>
349         static inline bool cas16_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
350         {
351             return cas16_strong( pDest, expected, desired, mo_success, mo_fail );
352         }
353
354         //-----------------------------------------------------------------------------
355         // 32bit primitives
356         //-----------------------------------------------------------------------------
357
358         template <typename T>
359         static inline T exchange32( T volatile * pDest, T v, memory_order /*order*/ ) CDS_NOEXCEPT
360         {
361             static_assert( sizeof(T) == 4, "Illegal operand size" );
362             assert( cds::details::is_aligned( pDest, 4 ));
363
364             return (T) _InterlockedExchange( (long *) pDest, (long) v );
365         }
366
367         template <typename T>
368         static inline void store32( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
369         {
370             static_assert( sizeof(T) == 4, "Illegal operand size" );
371             assert( order ==  memory_order_relaxed
372                 || order ==  memory_order_release
373                 || order == memory_order_seq_cst
374                 );
375             assert( pDest );
376             assert( cds::details::is_aligned( pDest, 4 ));
377
378             if ( order != memory_order_seq_cst ) {
379                 fence_before( order );
380                 *pDest = src;
381             }
382             else {
383                 exchange32( pDest, src, order );
384             }
385         }
386
387         template <typename T>
388         static inline T load32( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
389         {
390             static_assert( sizeof(T) == 4, "Illegal operand size" );
391             assert( order ==  memory_order_relaxed
392                 || order ==  memory_order_consume
393                 || order ==  memory_order_acquire
394                 || order ==  memory_order_seq_cst
395                 );
396             assert( pSrc );
397             assert( cds::details::is_aligned( pSrc, 4 ));
398
399             T v( *pSrc );
400             fence_after_load( order );
401             return v;
402         }
403
404         template <typename T>
405         static inline bool cas32_strong( T volatile * pDest, T& expected, T desired, memory_order /*mo_success*/, memory_order /*mo_fail*/ ) CDS_NOEXCEPT
406         {
407             static_assert( sizeof(T) == 4, "Illegal operand size" );
408             assert( cds::details::is_aligned( pDest, 4 ));
409
410             // _InterlockedCompareExchange behave as read-write memory barriers
411             T prev = expected;
412             expected = (T) _InterlockedCompareExchange( (long *) pDest, (long) desired, (long) expected );
413             return expected == prev;
414         }
415
416         template <typename T>
417         static inline bool cas32_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
418         {
419             return cas32_strong( pDest, expected, desired, mo_success, mo_fail );
420         }
421
422         // fetch_xxx may be emulated via cas32
423         // If the platform has special fetch_xxx instruction
424         // then it should define CDS_ATOMIC_fetch32_xxx_defined macro
425
426 #       define CDS_ATOMIC_fetch32_add_defined
427         template <typename T>
428         static inline T fetch32_add( T volatile * pDest, T v, memory_order /*order*/) CDS_NOEXCEPT
429         {
430             static_assert( sizeof(T) == 4, "Illegal operand size" );
431             assert( cds::details::is_aligned( pDest, 4 ));
432
433             // _InterlockedExchangeAdd behave as read-write memory barriers
434             return (T) _InterlockedExchangeAdd( (long *) pDest, (long) v );
435         }
436
437         //-----------------------------------------------------------------------------
438         // 64bit primitives
439         //-----------------------------------------------------------------------------
440
441         template <typename T>
442         static inline bool cas64_strong( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
443         {
444             static_assert( sizeof(T) == 8, "Illegal operand size" );
445             assert( cds::details::is_aligned( pDest, 8 ));
446
447             // _InterlockedCompareExchange behave as read-write memory barriers
448             T prev = expected;
449             expected = (T) _InterlockedCompareExchange64( (__int64 *) pDest, (__int64) desired, (__int64) expected );
450             return expected == prev;
451         }
452
453         template <typename T>
454         static inline bool cas64_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
455         {
456             return cas64_strong( pDest, expected, desired, mo_success, mo_fail );
457         }
458
459         template <typename T>
460         static inline T load64( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
461         {
462             static_assert( sizeof(T) == 8, "Illegal operand size" );
463             assert( order ==  memory_order_relaxed
464                 || order ==  memory_order_consume
465                 || order ==  memory_order_acquire
466                 || order ==  memory_order_seq_cst
467                 );
468             assert( pSrc );
469             assert( cds::details::is_aligned( pSrc, 8 ));
470
471             // Atomically loads 64bit value by SSE intrinsics
472             __m128i volatile v = _mm_loadl_epi64( (__m128i const *) pSrc );
473             fence_after_load( order );
474             return (T) v.m128i_i64[0];
475         }
476
477
478         template <typename T>
479         static inline T exchange64( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
480         {
481             static_assert( sizeof(T) == 8, "Illegal operand size" );
482
483             T cur = load64( pDest, memory_order_relaxed );
484             do {
485             } while (!cas64_weak( pDest, cur, v, order, memory_order_relaxed ));
486             return cur;
487         }
488
489         template <typename T>
490         static inline void store64( T volatile * pDest, T val, memory_order order ) CDS_NOEXCEPT
491         {
492             static_assert( sizeof(T) == 8, "Illegal operand size" );
493             assert( order ==  memory_order_relaxed
494                 || order ==  memory_order_release
495                 || order == memory_order_seq_cst
496                 );
497             assert( pDest );
498             assert( cds::details::is_aligned( pDest, 8 ));
499
500             if ( order != memory_order_seq_cst ) {
501                 __m128i v;
502                 v.m128i_i64[0] = val;
503                 fence_before( order );
504                 _mm_storel_epi64( (__m128i *) pDest, v );
505             }
506             else {
507                 exchange64( pDest, val, order );
508             }
509         }
510
511
512         //-----------------------------------------------------------------------------
513         // pointer primitives
514         //-----------------------------------------------------------------------------
515
516         template <typename T>
517         static inline T * exchange_ptr( T * volatile * pDest, T * v, memory_order order ) CDS_NOEXCEPT
518         {
519             static_assert( sizeof(T *) == sizeof(void *), "Illegal operand size" );
520             return (T *) _InterlockedExchange( (long volatile *) pDest, (uintptr_t) v );
521             //return (T *) _InterlockedExchangePointer( (void * volatile *) pDest, reinterpret_cast<void *>(v));
522         }
523
524         template <typename T>
525         static inline void store_ptr( T * volatile * pDest, T * src, memory_order order ) CDS_NOEXCEPT
526         {
527             static_assert( sizeof(T *) == sizeof(void *), "Illegal operand size" );
528             assert( order ==  memory_order_relaxed
529                 || order ==  memory_order_release
530                 || order == memory_order_seq_cst
531                 );
532             assert( pDest );
533
534             if ( order != memory_order_seq_cst ) {
535                 fence_before( order );
536                 *pDest = src;
537             }
538             else {
539                 exchange_ptr( pDest, src, order );
540             }
541         }
542
543         template <typename T>
544         static inline T * load_ptr( T * volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
545         {
546             static_assert( sizeof(T *) == sizeof(void *), "Illegal operand size" );
547             assert( order ==  memory_order_relaxed
548                 || order ==  memory_order_consume
549                 || order ==  memory_order_acquire
550                 || order ==  memory_order_seq_cst
551                 );
552             assert( pSrc );
553
554             T * v = *pSrc;
555             fence_after_load( order );
556             return v;
557         }
558
559         template <typename T>
560         static inline bool cas_ptr_strong( T * volatile * pDest, T *& expected, T * desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
561         {
562             static_assert( sizeof(T *) == sizeof(void *), "Illegal operand size" );
563
564             // _InterlockedCompareExchangePointer behave as read-write memory barriers
565             T * prev = expected;
566             expected = (T *) _InterlockedCompareExchange( (long volatile *) pDest, (uintptr_t) desired, (uintptr_t) prev );
567             return expected == prev;
568         }
569
570         template <typename T>
571         static inline bool cas_ptr_weak( T * volatile * pDest, T *& expected, T * desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
572         {
573             return cas_ptr_strong( pDest, expected, desired, mo_success, mo_fail );
574         }
575     }} // namespace vc::x86
576
577 #ifndef CDS_CXX11_INLINE_NAMESPACE_SUPPORT
578     using namespace vc::x86;
579 #endif
580     } // namespace platform
581 }}  // namespace cds::cxx11_atomic
582 //@endcond
583
584 #endif // #ifndef CDSLIB_COMPILER_VC_X86_CXX11_ATOMIC_H