Merged branch 'master' of https://github.com/Nemo1369/libcds
[libcds.git] / cds / compiler / vc / amd64 / 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_AMD64_CXX11_ATOMIC_H
32 #define CDSLIB_COMPILER_VC_AMD64_CXX11_ATOMIC_H
33
34 #include <intrin.h>
35 #include <emmintrin.h>  // for 128bit 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 )
42 #pragma intrinsic( _InterlockedCompareExchange16 )
43 #pragma intrinsic( _InterlockedCompareExchange64 )
44 #pragma intrinsic( _InterlockedExchange )
45 #pragma intrinsic( _InterlockedExchange64 )
46 #pragma intrinsic( _InterlockedExchangePointer )
47 #pragma intrinsic( _InterlockedExchangeAdd )
48 #pragma intrinsic( _InterlockedExchangeAdd64 )
49 //#pragma intrinsic( _InterlockedAnd )
50 //#pragma intrinsic( _InterlockedOr )
51 //#pragma intrinsic( _InterlockedXor )
52 //#pragma intrinsic( _InterlockedAnd64 )
53 //#pragma intrinsic( _InterlockedOr64 )
54 //#pragma intrinsic( _InterlockedXor64 )
55 #pragma intrinsic( _interlockedbittestandset )
56 #if _MSC_VER >= 1600
57 #   pragma intrinsic( _InterlockedCompareExchange8 )
58 #   pragma intrinsic( _InterlockedExchange8 )
59 #   pragma intrinsic( _InterlockedExchange16 )
60 #endif
61
62 //@cond
63 namespace cds { namespace cxx11_atomic {
64     namespace platform { CDS_CXX11_INLINE_NAMESPACE namespace vc { CDS_CXX11_INLINE_NAMESPACE namespace amd64 {
65
66         static inline void fence_before( memory_order order ) CDS_NOEXCEPT
67         {
68             switch(order) {
69             case memory_order_relaxed:
70             case memory_order_acquire:
71             case memory_order_consume:
72                 break;
73             case memory_order_release:
74             case memory_order_acq_rel:
75                 CDS_COMPILER_RW_BARRIER;
76                 break;
77             case memory_order_seq_cst:
78                 CDS_COMPILER_RW_BARRIER;
79                 break;
80             }
81         }
82
83         static inline void fence_after( memory_order order ) CDS_NOEXCEPT
84         {
85             switch(order) {
86             case memory_order_acquire:
87             case memory_order_acq_rel:
88                 CDS_COMPILER_RW_BARRIER;
89                 break;
90             case memory_order_relaxed:
91             case memory_order_consume:
92             case memory_order_release:
93                 break;
94             case memory_order_seq_cst:
95                 CDS_COMPILER_RW_BARRIER;
96                 break;
97             }
98         }
99
100         static inline void full_fence()
101         {
102             // MS VC does not support inline assembler in C code.
103             // So, we use InterlockedExchange for full fence instead of mfence inst
104             long t;
105             _InterlockedExchange( &t, 0 );
106         }
107
108         static inline void fence_after_load(memory_order order) CDS_NOEXCEPT
109         {
110             switch(order) {
111             case memory_order_relaxed:
112             case memory_order_release:
113                 break;
114             case memory_order_acquire:
115             case memory_order_acq_rel:
116                 CDS_COMPILER_RW_BARRIER;
117                 break;
118             case memory_order_consume:
119                 break;
120             case memory_order_seq_cst:
121                 full_fence();
122                 break;
123             default:;
124             }
125         }
126
127         //-----------------------------------------------------------------------------
128         // fences
129         //-----------------------------------------------------------------------------
130         static inline void thread_fence(memory_order order) CDS_NOEXCEPT
131         {
132             switch(order)
133             {
134                 case memory_order_relaxed:
135                 case memory_order_consume:
136                     break;
137                 case memory_order_release:
138                 case memory_order_acquire:
139                 case memory_order_acq_rel:
140                     CDS_COMPILER_RW_BARRIER;
141                     break;
142                 case memory_order_seq_cst:
143                     full_fence();
144                     break;
145                 default:;
146             }
147         }
148
149         static inline void signal_fence(memory_order order) CDS_NOEXCEPT
150         {
151             // C++11: 29.8.8: only compiler optimization, no hardware instructions
152             switch(order)
153             {
154                 case memory_order_relaxed:
155                     break;
156                 case memory_order_consume:
157                 case memory_order_release:
158                 case memory_order_acquire:
159                 case memory_order_acq_rel:
160                 case memory_order_seq_cst:
161                     CDS_COMPILER_RW_BARRIER;
162                     break;
163                 default:;
164             }
165         }
166
167         //-----------------------------------------------------------------------------
168         // atomic flag primitives
169         //-----------------------------------------------------------------------------
170
171         typedef unsigned char atomic_flag_type;
172         static inline bool atomic_flag_tas( atomic_flag_type volatile * pFlag, memory_order /*order*/ ) CDS_NOEXCEPT
173         {
174             return _interlockedbittestandset( (long volatile *) pFlag, 0 ) != 0;
175         }
176
177         static inline void atomic_flag_clear( atomic_flag_type volatile * pFlag, memory_order order ) CDS_NOEXCEPT
178         {
179             assert( order != memory_order_acquire
180                 && order != memory_order_acq_rel
181                 );
182
183             fence_before( order );
184             *pFlag = 0;
185             fence_after( order );
186         }
187
188         //-----------------------------------------------------------------------------
189         // 8bit primitives
190         //-----------------------------------------------------------------------------
191
192 #if _MSC_VER >= 1600
193 #   pragma warning(push)
194         // Disable warning C4800: 'char' : forcing value to bool 'true' or 'false' (performance warning)
195 #   pragma warning( disable: 4800 )
196 #endif
197         template <typename T>
198         static inline bool cas8_strong( T volatile * pDest, T& expected, T desired, memory_order /*mo_success*/, memory_order /*mo_fail*/ ) CDS_NOEXCEPT
199         {
200             static_assert( sizeof(T) == 1, "Illegal size of operand" );
201
202 #       if _MSC_VER >= 1600
203             // VC 2010 +
204             T prev = expected;
205             expected = (T) _InterlockedCompareExchange8( (char volatile*) pDest, (char) desired, (char) expected );
206             return expected == prev;
207 #       else
208             // VC 2008
209             unsigned int *  pnDest = (unsigned int *)( ((unsigned __int64) pDest) & ~(unsigned __int64(3)));
210             unsigned int    nOffset = ((unsigned __int64) pDest) & 3;
211             unsigned int    nExpected;
212             unsigned int    nDesired;
213
214             for (;;) {
215                 nExpected =
216                     nDesired = *pnDest;
217                 memcpy( reinterpret_cast<T *>(&nExpected) + nOffset, &expected, sizeof(T));
218                 memcpy( reinterpret_cast<T *>(&nDesired) + nOffset, &desired, sizeof(T));
219
220                 unsigned int nPrev = (unsigned int) _InterlockedCompareExchange( (long *) pnDest, (long) nDesired, (long) nExpected );
221                 if ( nPrev == nExpected )
222                     return true;
223                 T nByte;
224                 memcpy( &nByte, reinterpret_cast<T *>(&nPrev) + nOffset, sizeof(T));
225                 if ( nByte != expected ) {
226                     expected = nByte;
227                     return false;
228                 }
229             }
230 #       endif
231         }
232 #if _MSC_VER >= 1600
233 #   pragma warning(pop)
234 #endif
235
236         template <typename T>
237         static inline bool cas8_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
238         {
239             return cas8_strong( pDest, expected, desired, mo_success, mo_fail );
240         }
241
242 #if _MSC_VER >= 1600
243 #   pragma warning(push)
244         // Disable warning C4800: 'char' : forcing value to bool 'true' or 'false' (performance warning)
245 #   pragma warning( disable: 4800 )
246 #endif
247         template <typename T>
248         static inline T exchange8( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
249         {
250             static_assert( sizeof(T) == 1, "Illegal size of operand" );
251
252 #       if _MSC_VER >= 1600
253             CDS_UNUSED(order);
254             return (T) _InterlockedExchange8( (char volatile *) pDest, (char) v );
255 #       else
256             T expected = *pDest;
257             do {} while ( !cas8_strong( pDest, expected, v, order, memory_order_relaxed ));
258             return expected;
259 #       endif
260         }
261 #if _MSC_VER >= 1600
262 #   pragma warning(pop)
263 #endif
264
265         template <typename T>
266         static inline void store8( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
267         {
268             static_assert( sizeof(T) == 1, "Illegal size of operand" );
269             assert( order ==  memory_order_relaxed
270                 || order ==  memory_order_release
271                 || order == memory_order_seq_cst
272                 );
273             assert( pDest );
274
275             if ( order != memory_order_seq_cst ) {
276                 fence_before( order );
277                 *pDest = src;
278             }
279             else {
280                 exchange8( pDest, src, order );
281             }
282         }
283
284         template <typename T>
285         static inline T load8( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
286         {
287             static_assert( sizeof(T) == 1, "Illegal size of operand" );
288             assert( order ==  memory_order_relaxed
289                 || order ==  memory_order_consume
290                 || order ==  memory_order_acquire
291                 || order == memory_order_seq_cst
292                 );
293             assert( pSrc );
294
295             T v = *pSrc;
296             fence_after_load( order );
297             return v;
298         }
299
300         //-----------------------------------------------------------------------------
301         // 16bit primitives
302         //-----------------------------------------------------------------------------
303
304         template <typename T>
305         static inline bool cas16_strong( T volatile * pDest, T& expected, T desired, memory_order /*mo_success*/, memory_order /*mo_fail*/ ) CDS_NOEXCEPT
306         {
307             static_assert( sizeof(T) == 2, "Illegal size of operand" );
308             assert( cds::details::is_aligned( pDest, 2 ));
309
310             // _InterlockedCompareExchange behave as read-write memory barriers
311             T prev = expected;
312             expected = (T) _InterlockedCompareExchange16( (short *) pDest, (short) desired, (short) expected );
313             return expected == prev;
314         }
315
316         template <typename T>
317         static inline bool cas16_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
318         {
319             return cas16_strong( pDest, expected, desired, mo_success, mo_fail );
320         }
321
322         template <typename T>
323         static inline T exchange16( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
324         {
325             static_assert( sizeof(T) == 2, "Illegal size of operand" );
326             assert( cds::details::is_aligned( pDest, 2 ));
327
328 #       if _MSC_VER >= 1600
329             order;
330             return (T) _InterlockedExchange16( (short volatile *) pDest, (short) v );
331 #       else
332             T expected = *pDest;
333             do {} while ( !cas16_strong( pDest, expected, v, order, memory_order_relaxed ));
334             return expected;
335 #       endif
336         }
337
338         template <typename T>
339         static inline void store16( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
340         {
341             static_assert( sizeof(T) == 2, "Illegal size of operand" );
342             assert( order ==  memory_order_relaxed
343                 || order ==  memory_order_release
344                 || order == memory_order_seq_cst
345                 );
346             assert( pDest );
347             assert( cds::details::is_aligned( pDest, 2 ));
348
349             if ( order != memory_order_seq_cst ) {
350                 fence_before( order );
351                 *pDest = src;
352             }
353             else {
354                 exchange16( pDest, src, order );
355             }
356         }
357
358         template <typename T>
359         static inline T load16( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
360         {
361             static_assert( sizeof(T) == 2, "Illegal size of operand" );
362             assert( order ==  memory_order_relaxed
363                 || order ==  memory_order_consume
364                 || order ==  memory_order_acquire
365                 || order ==  memory_order_seq_cst
366                 );
367             assert( pSrc );
368             assert( cds::details::is_aligned( pSrc, 2 ));
369
370             T v = *pSrc;
371             fence_after_load( order );
372             return v;
373         }
374
375         //-----------------------------------------------------------------------------
376         // 32bit primitives
377         //-----------------------------------------------------------------------------
378
379         template <typename T>
380         static inline T exchange32( T volatile * pDest, T v, memory_order /*order*/ ) CDS_NOEXCEPT
381         {
382             static_assert( sizeof(T) == 4, "Illegal size of operand" );
383             assert( cds::details::is_aligned( pDest, 4 ));
384
385             return (T) _InterlockedExchange( (long *) pDest, (long) v );
386         }
387
388         template <typename T>
389         static inline void store32( T volatile * pDest, T src, memory_order order ) CDS_NOEXCEPT
390         {
391             static_assert( sizeof(T) == 4, "Illegal size of operand" );
392             assert( order ==  memory_order_relaxed
393                 || order ==  memory_order_release
394                 || order == memory_order_seq_cst
395                 );
396             assert( pDest );
397             assert( cds::details::is_aligned( pDest, 4 ));
398
399             if ( order != memory_order_seq_cst ) {
400                 fence_before( order );
401                 *pDest = src;
402             }
403             else {
404                 exchange32( pDest, src, order );
405             }
406         }
407
408         template <typename T>
409         static inline T load32( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
410         {
411             static_assert( sizeof(T) == 4, "Illegal size of operand" );
412             assert( order ==  memory_order_relaxed
413                 || order ==  memory_order_consume
414                 || order ==  memory_order_acquire
415                 || order ==  memory_order_seq_cst
416                 );
417             assert( pSrc );
418             assert( cds::details::is_aligned( pSrc, 4 ));
419
420             T v = *pSrc;
421             fence_after_load( order );
422             return v;
423         }
424
425         template <typename T>
426         static inline bool cas32_strong( T volatile * pDest, T& expected, T desired, memory_order /*mo_success*/, memory_order /*mo_fail*/ ) CDS_NOEXCEPT
427         {
428             static_assert( sizeof(T) == 4, "Illegal size of operand" );
429             assert( cds::details::is_aligned( pDest, 4 ));
430
431             // _InterlockedCompareExchange behave as read-write memory barriers
432             T prev = expected;
433             expected = (T) _InterlockedCompareExchange( (long *) pDest, (long) desired, (long) expected );
434             return expected == prev;
435         }
436
437         template <typename T>
438         static inline bool cas32_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
439         {
440             return cas32_strong( pDest, expected, desired, mo_success, mo_fail );
441         }
442
443         // fetch_xxx may be emulated via cas32
444         // If the platform has special fetch_xxx instruction
445         // then it should define CDS_ATOMIC_fetch32_xxx_defined macro
446
447 #       define CDS_ATOMIC_fetch32_add_defined
448         template <typename T>
449         static inline T fetch32_add( T volatile * pDest, T v, memory_order /*order*/) CDS_NOEXCEPT
450         {
451             static_assert( sizeof(T) == 4, "Illegal size of operand" );
452             assert( cds::details::is_aligned( pDest, 4 ));
453
454             // _InterlockedExchangeAdd behave as read-write memory barriers
455             return (T) _InterlockedExchangeAdd( (long *) pDest, (long) v );
456         }
457
458         //-----------------------------------------------------------------------------
459         // 64bit primitives
460         //-----------------------------------------------------------------------------
461
462         template <typename T>
463         static inline bool cas64_strong( T volatile * pDest, T& expected, T desired, memory_order /*mo_success*/, memory_order /*mo_fail*/ ) CDS_NOEXCEPT
464         {
465             static_assert( sizeof(T) == 8, "Illegal size of operand" );
466             assert( cds::details::is_aligned( pDest, 8 ));
467
468             // _InterlockedCompareExchange behave as read-write memory barriers
469             T prev = expected;
470             expected = (T) _InterlockedCompareExchange64( (__int64 *) pDest, (__int64) desired, (__int64) expected );
471             return expected == prev;
472         }
473
474         template <typename T>
475         static inline bool cas64_weak( T volatile * pDest, T& expected, T desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
476         {
477             return cas64_strong( pDest, expected, desired, mo_success, mo_fail );
478         }
479
480         template <typename T>
481         static inline T load64( T volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
482         {
483             static_assert( sizeof(T) == 8, "Illegal size of operand" );
484             assert( order ==  memory_order_relaxed
485                 || order ==  memory_order_consume
486                 || order ==  memory_order_acquire
487                 || order ==  memory_order_seq_cst
488                 );
489             assert( pSrc );
490             assert( cds::details::is_aligned( pSrc, 8 ));
491
492             T v = *pSrc;
493             fence_after_load( order );
494             return v;
495         }
496
497
498         template <typename T>
499         static inline T exchange64( T volatile * pDest, T v, memory_order order ) CDS_NOEXCEPT
500         {
501             static_assert( sizeof(T) == 8, "Illegal size of operand" );
502
503             T cur = load64( pDest, memory_order_relaxed );
504             do {
505             } while (!cas64_weak( pDest, cur, v, order, memory_order_relaxed ));
506             return cur;
507         }
508
509         template <typename T>
510         static inline void store64( T volatile * pDest, T val, memory_order order ) CDS_NOEXCEPT
511         {
512             static_assert( sizeof(T) == 8, "Illegal size of operand" );
513             assert( order ==  memory_order_relaxed
514                 || order ==  memory_order_release
515                 || order == memory_order_seq_cst
516                 );
517             assert( pDest );
518             assert( cds::details::is_aligned( pDest, 8 ));
519
520             if ( order != memory_order_seq_cst ) {
521                 fence_before( order );
522                 *pDest = val;
523             }
524             else {
525                 exchange64( pDest, val, order );
526             }
527         }
528
529 #       define CDS_ATOMIC_fetch64_add_defined
530         template <typename T>
531         static inline T fetch64_add( T volatile * pDest, T v, memory_order /*order*/) CDS_NOEXCEPT
532         {
533             static_assert( sizeof(T) == 8, "Illegal size of operand" );
534             assert( cds::details::is_aligned( pDest, 8 ));
535
536             // _InterlockedExchangeAdd64 behave as read-write memory barriers
537             return (T) _InterlockedExchangeAdd64( (__int64 *) pDest, (__int64) v );
538         }
539
540         //-----------------------------------------------------------------------------
541         // pointer primitives
542         //-----------------------------------------------------------------------------
543
544         template <typename T>
545         static inline T * exchange_ptr( T * volatile * pDest, T * v, memory_order /*order*/ ) CDS_NOEXCEPT
546         {
547             static_assert( sizeof(T *) == sizeof(void *), "Illegal size of operand" );
548             return (T *) _InterlockedExchangePointer( (void * volatile *) pDest, reinterpret_cast<void *>(v));
549         }
550
551         template <typename T>
552         static inline void store_ptr( T * volatile * pDest, T * src, memory_order order ) CDS_NOEXCEPT
553         {
554             static_assert( sizeof(T *) == sizeof(void *), "Illegal size of operand" );
555             assert( order ==  memory_order_relaxed
556                 || order ==  memory_order_release
557                 || order == memory_order_seq_cst
558                 );
559             assert( pDest );
560
561             if ( order != memory_order_seq_cst ) {
562                 fence_before( order );
563                 *pDest = src;
564             }
565             else {
566                 exchange_ptr( pDest, src, order );
567             }
568         }
569
570         template <typename T>
571         static inline T * load_ptr( T * volatile const * pSrc, memory_order order ) CDS_NOEXCEPT
572         {
573             static_assert( sizeof(T *) == sizeof(void *), "Illegal size of operand" );
574             assert( order ==  memory_order_relaxed
575                 || order ==  memory_order_consume
576                 || order ==  memory_order_acquire
577                 || order ==  memory_order_seq_cst
578                 );
579             assert( pSrc );
580
581             T * v = *pSrc;
582             fence_after_load( order );
583             return v;
584         }
585
586         template <typename T>
587         static inline bool cas_ptr_strong( T * volatile * pDest, T *& expected, T * desired, memory_order /*mo_success*/, memory_order /*mo_fail*/ ) CDS_NOEXCEPT
588         {
589             static_assert( sizeof(T *) == sizeof(void *), "Illegal size of operand" );
590
591             // _InterlockedCompareExchangePointer behave as read-write memory barriers
592             T * prev = expected;
593             expected = (T *) _InterlockedCompareExchangePointer( (void * volatile *) pDest, (void *) desired, (void *) expected );
594             return expected == prev;
595         }
596
597         template <typename T>
598         static inline bool cas_ptr_weak( T * volatile * pDest, T *& expected, T * desired, memory_order mo_success, memory_order mo_fail ) CDS_NOEXCEPT
599         {
600             return cas_ptr_strong( pDest, expected, desired, mo_success, mo_fail );
601         }
602
603     }} // namespace vc::amd64
604
605 #ifndef CDS_CXX11_INLINE_NAMESPACE_SUPPORT
606     using namespace vc::amd64;
607 #endif
608     } // namespace platform
609 }}  // namespace cds::cxx11_atomic
610 //@endcond
611
612 #endif // #ifndef CDSLIB_COMPILER_VC_AMD64_CXX11_ATOMIC_H