Mercurial > projects > ldc
comparison tango/tango/core/Atomic.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
131:5825d48b27d1 | 132:1700239cab2e |
---|---|
1 /** | |
2 * The atomic module is intended to provide some basic support for lock-free | |
3 * concurrent programming. Some common operations are defined, each of which | |
4 * may be performed using the specified memory barrier or a less granular | |
5 * barrier if the hardware does not support the version requested. This | |
6 * model is based on a design by Alexander Terekhov as outlined in | |
7 * <a href=http://groups.google.com/groups?threadm=3E4820EE.6F408B25%40web.de> | |
8 * this thread</a>. Another useful reference for memory ordering on modern | |
9 * architectures is <a href=http://www.linuxjournal.com/article/8211>this | |
10 * article by Paul McKenney</a>. | |
11 * | |
12 * Copyright: Copyright (C) 2005-2006 Sean Kelly. All rights reserved. | |
13 * License: BSD style: $(LICENSE) | |
14 * Authors: Sean Kelly | |
15 */ | |
16 module tango.core.Atomic; | |
17 | |
18 | |
19 //////////////////////////////////////////////////////////////////////////////// | |
20 // Synchronization Options | |
21 //////////////////////////////////////////////////////////////////////////////// | |
22 | |
23 | |
24 /** | |
25 * Memory synchronization flag. If the supplied option is not available on the | |
26 * current platform then a stronger method will be used instead. | |
27 */ | |
28 enum msync | |
29 { | |
30 raw, /// not sequenced | |
31 hlb, /// hoist-load barrier | |
32 hsb, /// hoist-store barrier | |
33 slb, /// sink-load barrier | |
34 ssb, /// sink-store barrier | |
35 acq, /// hoist-load + hoist-store barrier | |
36 rel, /// sink-load + sink-store barrier | |
37 seq, /// fully sequenced (acq + rel) | |
38 } | |
39 | |
40 | |
41 //////////////////////////////////////////////////////////////////////////////// | |
42 // Internal Type Checking | |
43 //////////////////////////////////////////////////////////////////////////////// | |
44 | |
45 | |
46 private | |
47 { | |
48 version( DDoc ) {} else | |
49 { | |
50 import tango.core.Traits; | |
51 | |
52 | |
53 template isValidAtomicType( T ) | |
54 { | |
55 const bool isValidAtomicType = T.sizeof == byte.sizeof || | |
56 T.sizeof == short.sizeof || | |
57 T.sizeof == int.sizeof || | |
58 T.sizeof == long.sizeof; | |
59 } | |
60 | |
61 | |
62 template isValidNumericType( T ) | |
63 { | |
64 const bool isValidNumericType = isIntegerType!( T ) || | |
65 isPointerType!( T ); | |
66 } | |
67 | |
68 | |
69 template isHoistOp( msync ms ) | |
70 { | |
71 const bool isHoistOp = ms == msync.hlb || | |
72 ms == msync.hsb || | |
73 ms == msync.acq || | |
74 ms == msync.seq; | |
75 } | |
76 | |
77 | |
78 template isSinkOp( msync ms ) | |
79 { | |
80 const bool isSinkOp = ms == msync.slb || | |
81 ms == msync.ssb || | |
82 ms == msync.rel || | |
83 ms == msync.seq; | |
84 } | |
85 } | |
86 } | |
87 | |
88 | |
89 //////////////////////////////////////////////////////////////////////////////// | |
90 // DDoc Documentation for Atomic Functions | |
91 //////////////////////////////////////////////////////////////////////////////// | |
92 | |
93 | |
94 version( DDoc ) | |
95 { | |
96 //////////////////////////////////////////////////////////////////////////// | |
97 // Atomic Load | |
98 //////////////////////////////////////////////////////////////////////////// | |
99 | |
100 | |
101 /** | |
102 * Supported msync values: | |
103 * msync.raw | |
104 * msync.hlb | |
105 * msync.acq | |
106 * msync.seq | |
107 */ | |
108 template atomicLoad( msync ms, T ) | |
109 { | |
110 /** | |
111 * Refreshes the contents of 'val' from main memory. This operation is | |
112 * both lock-free and atomic. | |
113 * | |
114 * Params: | |
115 * val = The value to load. This value must be properly aligned. | |
116 * | |
117 * Returns: | |
118 * The loaded value. | |
119 */ | |
120 T atomicLoad( inout T val ) | |
121 { | |
122 return val; | |
123 } | |
124 } | |
125 | |
126 | |
127 //////////////////////////////////////////////////////////////////////////// | |
128 // Atomic Store | |
129 //////////////////////////////////////////////////////////////////////////// | |
130 | |
131 | |
132 /** | |
133 * Supported msync values: | |
134 * msync.raw | |
135 * msync.ssb | |
136 * msync.acq | |
137 * msync.rel | |
138 * msync.seq | |
139 */ | |
140 template atomicStore( msync ms, T ) | |
141 { | |
142 /** | |
143 * Stores 'newval' to the memory referenced by 'val'. This operation | |
144 * is both lock-free and atomic. | |
145 * | |
146 * Params: | |
147 * val = The destination variable. | |
148 * newval = The value to store. | |
149 */ | |
150 void atomicStore( inout T val, T newval ) | |
151 { | |
152 | |
153 } | |
154 } | |
155 | |
156 | |
157 //////////////////////////////////////////////////////////////////////////// | |
158 // Atomic StoreIf | |
159 //////////////////////////////////////////////////////////////////////////// | |
160 | |
161 | |
162 /** | |
163 * Supported msync values: | |
164 * msync.raw | |
165 * msync.ssb | |
166 * msync.acq | |
167 * msync.rel | |
168 * msync.seq | |
169 */ | |
170 template atomicStoreIf( msync ms, T ) | |
171 { | |
172 /** | |
173 * Stores 'newval' to the memory referenced by 'val' if val is equal to | |
174 * 'equalTo'. This operation is both lock-free and atomic. | |
175 * | |
176 * Params: | |
177 * val = The destination variable. | |
178 * newval = The value to store. | |
179 * equalTo = The comparison value. | |
180 * | |
181 * Returns: | |
182 * true if the store occurred, false if not. | |
183 */ | |
184 bool atomicStoreIf( inout T val, T newval, T equalTo ) | |
185 { | |
186 return false; | |
187 } | |
188 } | |
189 | |
190 | |
191 //////////////////////////////////////////////////////////////////////////// | |
192 // Atomic Increment | |
193 //////////////////////////////////////////////////////////////////////////// | |
194 | |
195 | |
196 /** | |
197 * Supported msync values: | |
198 * msync.raw | |
199 * msync.ssb | |
200 * msync.acq | |
201 * msync.rel | |
202 * msync.seq | |
203 */ | |
204 template atomicIncrement( msync ms, T ) | |
205 { | |
206 /** | |
207 * This operation is only legal for built-in value and pointer types, | |
208 * and is equivalent to an atomic "val = val + 1" operation. This | |
209 * function exists to facilitate use of the optimized increment | |
210 * instructions provided by some architecures. If no such instruction | |
211 * exists on the target platform then the behavior will perform the | |
212 * operation using more traditional means. This operation is both | |
213 * lock-free and atomic. | |
214 * | |
215 * Params: | |
216 * val = The value to increment. | |
217 * | |
218 * Returns: | |
219 * The result of an atomicLoad of val immediately following the | |
220 * increment operation. This value is not required to be equal to the | |
221 * newly stored value. Thus, competing writes are allowed to occur | |
222 * between the increment and successive load operation. | |
223 */ | |
224 T atomicIncrement( inout T val ) | |
225 { | |
226 return val; | |
227 } | |
228 } | |
229 | |
230 | |
231 //////////////////////////////////////////////////////////////////////////// | |
232 // Atomic Decrement | |
233 //////////////////////////////////////////////////////////////////////////// | |
234 | |
235 | |
236 /** | |
237 * Supported msync values: | |
238 * msync.raw | |
239 * msync.ssb | |
240 * msync.acq | |
241 * msync.rel | |
242 * msync.seq | |
243 */ | |
244 template atomicDecrement( msync ms, T ) | |
245 { | |
246 /** | |
247 * This operation is only legal for built-in value and pointer types, | |
248 * and is equivalent to an atomic "val = val - 1" operation. This | |
249 * function exists to facilitate use of the optimized decrement | |
250 * instructions provided by some architecures. If no such instruction | |
251 * exists on the target platform then the behavior will perform the | |
252 * operation using more traditional means. This operation is both | |
253 * lock-free and atomic. | |
254 * | |
255 * Params: | |
256 * val = The value to decrement. | |
257 * | |
258 * Returns: | |
259 * The result of an atomicLoad of val immediately following the | |
260 * increment operation. This value is not required to be equal to the | |
261 * newly stored value. Thus, competing writes are allowed to occur | |
262 * between the increment and successive load operation. | |
263 */ | |
264 T atomicDecrement( inout T val ) | |
265 { | |
266 return val; | |
267 } | |
268 } | |
269 } | |
270 | |
271 | |
272 //////////////////////////////////////////////////////////////////////////////// | |
273 // x86 Atomic Function Implementation | |
274 //////////////////////////////////////////////////////////////////////////////// | |
275 | |
276 | |
277 else version( D_InlineAsm_X86 ) | |
278 { | |
279 version( X86 ) | |
280 { | |
281 version( BuildInfo ) | |
282 { | |
283 pragma( msg, "tango.core.Atomic: using IA-32 inline asm" ); | |
284 } | |
285 | |
286 version = Has32BitOps; | |
287 version = Has64BitCAS; | |
288 } | |
289 version( X86_64 ) | |
290 { | |
291 version( BuildInfo ) | |
292 { | |
293 pragma( msg, "tango.core.Atomic: using AMD64 inline asm" ); | |
294 } | |
295 | |
296 version = Has64BitOps; | |
297 } | |
298 | |
299 private | |
300 { | |
301 //////////////////////////////////////////////////////////////////////// | |
302 // x86 Value Requirements | |
303 //////////////////////////////////////////////////////////////////////// | |
304 | |
305 | |
306 // NOTE: Strictly speaking, the x86 supports atomic operations on | |
307 // unaligned values. However, this is far slower than the | |
308 // common case, so such behavior should be prohibited. | |
309 template atomicValueIsProperlyAligned( T ) | |
310 { | |
311 bool atomicValueIsProperlyAligned( size_t addr ) | |
312 { | |
313 return addr % T.sizeof == 0; | |
314 } | |
315 } | |
316 | |
317 | |
318 //////////////////////////////////////////////////////////////////////// | |
319 // x86 Synchronization Requirements | |
320 //////////////////////////////////////////////////////////////////////// | |
321 | |
322 | |
323 // NOTE: While x86 loads have acquire semantics for stores, it appears | |
324 // that independent loads may be reordered by some processors | |
325 // (notably the AMD64). This implies that the hoist-load barrier | |
326 // op requires an ordering instruction, which also extends this | |
327 // requirement to acquire ops (though hoist-store should not need | |
328 // one if support is added for this later). However, since no | |
329 // modern architectures will reorder dependent loads to occur | |
330 // before the load they depend on (except the Alpha), raw loads | |
331 // are actually a possible means of ordering specific sequences | |
332 // of loads in some instances. The original atomic<> | |
333 // implementation provides a 'ddhlb' ordering specifier for | |
334 // data-dependent loads to handle this situation, but as there | |
335 // are no plans to support the Alpha there is no reason to add | |
336 // that option here. | |
337 // | |
338 // For reference, the old behavior (acquire semantics for loads) | |
339 // required a memory barrier if: ms == msync.seq || isSinkOp!(ms) | |
340 template needsLoadBarrier( msync ms ) | |
341 { | |
342 const bool needsLoadBarrier = ms != msync.raw; | |
343 } | |
344 | |
345 | |
346 // NOTE: x86 stores implicitly have release semantics so a membar is only | |
347 // necessary on acquires. | |
348 template needsStoreBarrier( msync ms ) | |
349 { | |
350 const bool needsStoreBarrier = ms == msync.seq || isHoistOp!(ms); | |
351 } | |
352 } | |
353 | |
354 | |
355 //////////////////////////////////////////////////////////////////////////// | |
356 // Atomic Load | |
357 //////////////////////////////////////////////////////////////////////////// | |
358 | |
359 | |
360 template atomicLoad( msync ms = msync.seq, T ) | |
361 { | |
362 T atomicLoad( inout T val ) | |
363 in | |
364 { | |
365 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
366 } | |
367 body | |
368 { | |
369 static if( T.sizeof == byte.sizeof ) | |
370 { | |
371 //////////////////////////////////////////////////////////////// | |
372 // 1 Byte Load | |
373 //////////////////////////////////////////////////////////////// | |
374 | |
375 | |
376 static if( needsLoadBarrier!(ms) ) | |
377 { | |
378 volatile asm | |
379 { | |
380 mov BL, 42; | |
381 mov AL, 42; | |
382 mov ECX, val; | |
383 lock; | |
384 cmpxchg [ECX], BL; | |
385 } | |
386 } | |
387 else | |
388 { | |
389 volatile | |
390 { | |
391 return val; | |
392 } | |
393 } | |
394 } | |
395 else static if( T.sizeof == short.sizeof ) | |
396 { | |
397 //////////////////////////////////////////////////////////////// | |
398 // 2 Byte Load | |
399 //////////////////////////////////////////////////////////////// | |
400 | |
401 static if( needsLoadBarrier!(ms) ) | |
402 { | |
403 volatile asm | |
404 { | |
405 mov BX, 42; | |
406 mov AX, 42; | |
407 mov ECX, val; | |
408 lock; | |
409 cmpxchg [ECX], BX; | |
410 } | |
411 } | |
412 else | |
413 { | |
414 volatile | |
415 { | |
416 return val; | |
417 } | |
418 } | |
419 } | |
420 else static if( T.sizeof == int.sizeof ) | |
421 { | |
422 //////////////////////////////////////////////////////////////// | |
423 // 4 Byte Load | |
424 //////////////////////////////////////////////////////////////// | |
425 | |
426 | |
427 static if( needsLoadBarrier!(ms) ) | |
428 { | |
429 volatile asm | |
430 { | |
431 mov EBX, 42; | |
432 mov EAX, 42; | |
433 mov ECX, val; | |
434 lock; | |
435 cmpxchg [ECX], EBX; | |
436 } | |
437 } | |
438 else | |
439 { | |
440 volatile | |
441 { | |
442 return val; | |
443 } | |
444 } | |
445 } | |
446 else static if( T.sizeof == long.sizeof ) | |
447 { | |
448 //////////////////////////////////////////////////////////////// | |
449 // 8 Byte Load | |
450 //////////////////////////////////////////////////////////////// | |
451 | |
452 | |
453 version( Has64BitOps ) | |
454 { | |
455 //////////////////////////////////////////////////////////// | |
456 // 8 Byte Load on 64-Bit Processor | |
457 //////////////////////////////////////////////////////////// | |
458 | |
459 | |
460 static if( needsLoadBarrier!(ms) ) | |
461 { | |
462 volatile asm | |
463 { | |
464 mov RAX, val; | |
465 lock; | |
466 mov RAX, [RAX]; | |
467 } | |
468 } | |
469 else | |
470 { | |
471 volatile | |
472 { | |
473 return val; | |
474 } | |
475 } | |
476 } | |
477 else | |
478 { | |
479 //////////////////////////////////////////////////////////// | |
480 // 8 Byte Load on 32-Bit Processor | |
481 //////////////////////////////////////////////////////////// | |
482 | |
483 | |
484 pragma( msg, "This operation is only available on 64-bit platforms." ); | |
485 static assert( false ); | |
486 } | |
487 } | |
488 else | |
489 { | |
490 //////////////////////////////////////////////////////////////// | |
491 // Not a 1, 2, 4, or 8 Byte Type | |
492 //////////////////////////////////////////////////////////////// | |
493 | |
494 | |
495 pragma( msg, "Invalid template type specified." ); | |
496 static assert( false ); | |
497 } | |
498 } | |
499 } | |
500 | |
501 | |
502 //////////////////////////////////////////////////////////////////////////// | |
503 // Atomic Store | |
504 //////////////////////////////////////////////////////////////////////////// | |
505 | |
506 | |
507 template atomicStore( msync ms = msync.seq, T ) | |
508 { | |
509 void atomicStore( inout T val, T newval ) | |
510 in | |
511 { | |
512 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
513 } | |
514 body | |
515 { | |
516 static if( T.sizeof == byte.sizeof ) | |
517 { | |
518 //////////////////////////////////////////////////////////////// | |
519 // 1 Byte Store | |
520 //////////////////////////////////////////////////////////////// | |
521 | |
522 | |
523 static if( needsStoreBarrier!(ms) ) | |
524 { | |
525 volatile asm | |
526 { | |
527 mov EAX, val; | |
528 mov BL, newval; | |
529 lock; | |
530 xchg [EAX], BL; | |
531 } | |
532 } | |
533 else | |
534 { | |
535 volatile asm | |
536 { | |
537 mov EAX, val; | |
538 mov BL, newval; | |
539 mov [EAX], BL; | |
540 } | |
541 } | |
542 } | |
543 else static if( T.sizeof == short.sizeof ) | |
544 { | |
545 //////////////////////////////////////////////////////////////// | |
546 // 2 Byte Store | |
547 //////////////////////////////////////////////////////////////// | |
548 | |
549 | |
550 static if( needsStoreBarrier!(ms) ) | |
551 { | |
552 volatile asm | |
553 { | |
554 mov EAX, val; | |
555 mov BX, newval; | |
556 lock; | |
557 xchg [EAX], BX; | |
558 } | |
559 } | |
560 else | |
561 { | |
562 volatile asm | |
563 { | |
564 mov EAX, val; | |
565 mov BX, newval; | |
566 mov [EAX], BX; | |
567 } | |
568 } | |
569 } | |
570 else static if( T.sizeof == int.sizeof ) | |
571 { | |
572 //////////////////////////////////////////////////////////////// | |
573 // 4 Byte Store | |
574 //////////////////////////////////////////////////////////////// | |
575 | |
576 | |
577 static if( needsStoreBarrier!(ms) ) | |
578 { | |
579 volatile asm | |
580 { | |
581 mov EAX, val; | |
582 mov EBX, newval; | |
583 lock; | |
584 xchg [EAX], EBX; | |
585 } | |
586 } | |
587 else | |
588 { | |
589 volatile asm | |
590 { | |
591 mov EAX, val; | |
592 mov EBX, newval; | |
593 mov [EAX], EBX; | |
594 } | |
595 } | |
596 } | |
597 else static if( T.sizeof == long.sizeof ) | |
598 { | |
599 //////////////////////////////////////////////////////////////// | |
600 // 8 Byte Store | |
601 //////////////////////////////////////////////////////////////// | |
602 | |
603 | |
604 version( Has64BitOps ) | |
605 { | |
606 //////////////////////////////////////////////////////////// | |
607 // 8 Byte Store on 64-Bit Processor | |
608 //////////////////////////////////////////////////////////// | |
609 | |
610 | |
611 static if( needsStoreBarrier!(ms) ) | |
612 { | |
613 volatile asm | |
614 { | |
615 mov RAX, val; | |
616 mov RBX, newval; | |
617 lock; | |
618 xchg [RAX], RBX; | |
619 } | |
620 } | |
621 else | |
622 { | |
623 volatile asm | |
624 { | |
625 mov RAX, val; | |
626 mov RBX, newval; | |
627 mov [RAX], RBX; | |
628 } | |
629 } | |
630 } | |
631 else | |
632 { | |
633 //////////////////////////////////////////////////////////// | |
634 // 8 Byte Store on 32-Bit Processor | |
635 //////////////////////////////////////////////////////////// | |
636 | |
637 | |
638 pragma( msg, "This operation is only available on 64-bit platforms." ); | |
639 static assert( false ); | |
640 } | |
641 } | |
642 else | |
643 { | |
644 //////////////////////////////////////////////////////////////// | |
645 // Not a 1, 2, 4, or 8 Byte Type | |
646 //////////////////////////////////////////////////////////////// | |
647 | |
648 | |
649 pragma( msg, "Invalid template type specified." ); | |
650 static assert( false ); | |
651 } | |
652 } | |
653 } | |
654 | |
655 | |
656 //////////////////////////////////////////////////////////////////////////// | |
657 // Atomic Store If | |
658 //////////////////////////////////////////////////////////////////////////// | |
659 | |
660 | |
661 template atomicStoreIf( msync ms = msync.seq, T ) | |
662 { | |
663 bool atomicStoreIf( inout T val, T newval, T equalTo ) | |
664 in | |
665 { | |
666 // NOTE: 32 bit x86 systems support 8 byte CAS, which only requires | |
667 // 4 byte alignment, so use size_t as the align type here. | |
668 static if( T.sizeof > size_t.sizeof ) | |
669 assert( atomicValueIsProperlyAligned!(size_t)( cast(size_t) &val ) ); | |
670 else | |
671 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
672 } | |
673 body | |
674 { | |
675 static if( T.sizeof == byte.sizeof ) | |
676 { | |
677 //////////////////////////////////////////////////////////////// | |
678 // 1 Byte StoreIf | |
679 //////////////////////////////////////////////////////////////// | |
680 | |
681 | |
682 volatile asm | |
683 { | |
684 mov BL, newval; | |
685 mov AL, equalTo; | |
686 mov ECX, val; | |
687 lock; // lock always needed to make this op atomic | |
688 cmpxchg [ECX], BL; | |
689 setz AL; | |
690 } | |
691 } | |
692 else static if( T.sizeof == short.sizeof ) | |
693 { | |
694 //////////////////////////////////////////////////////////////// | |
695 // 2 Byte StoreIf | |
696 //////////////////////////////////////////////////////////////// | |
697 | |
698 | |
699 volatile asm | |
700 { | |
701 mov BX, newval; | |
702 mov AX, equalTo; | |
703 mov ECX, val; | |
704 lock; // lock always needed to make this op atomic | |
705 cmpxchg [ECX], BX; | |
706 setz AL; | |
707 } | |
708 } | |
709 else static if( T.sizeof == int.sizeof ) | |
710 { | |
711 //////////////////////////////////////////////////////////////// | |
712 // 4 Byte StoreIf | |
713 //////////////////////////////////////////////////////////////// | |
714 | |
715 | |
716 volatile asm | |
717 { | |
718 mov EBX, newval; | |
719 mov EAX, equalTo; | |
720 mov ECX, val; | |
721 lock; // lock always needed to make this op atomic | |
722 cmpxchg [ECX], EBX; | |
723 setz AL; | |
724 } | |
725 } | |
726 else static if( T.sizeof == long.sizeof ) | |
727 { | |
728 //////////////////////////////////////////////////////////////// | |
729 // 8 Byte StoreIf | |
730 //////////////////////////////////////////////////////////////// | |
731 | |
732 | |
733 version( Has64BitOps ) | |
734 { | |
735 //////////////////////////////////////////////////////////// | |
736 // 8 Byte StoreIf on 64-Bit Processor | |
737 //////////////////////////////////////////////////////////// | |
738 | |
739 | |
740 volatile asm | |
741 { | |
742 mov RBX, newval; | |
743 mov RAX, equalTo; | |
744 mov RCX, val; | |
745 lock; // lock always needed to make this op atomic | |
746 cmpxchg [RCX], RBX; | |
747 setz AL; | |
748 } | |
749 } | |
750 else version( Has64BitCAS ) | |
751 { | |
752 //////////////////////////////////////////////////////////// | |
753 // 8 Byte StoreIf on 32-Bit Processor | |
754 //////////////////////////////////////////////////////////// | |
755 | |
756 | |
757 volatile asm | |
758 { | |
759 lea EDI, newval; | |
760 mov EBX, [EDI]; | |
761 mov ECX, 4[EDI]; | |
762 lea EDI, equalTo; | |
763 mov EAX, [EDI]; | |
764 mov EDX, 4[EDI]; | |
765 mov EDI, val; | |
766 lock; // lock always needed to make this op atomic | |
767 cmpxch8b [EDI]; | |
768 setz AL; | |
769 } | |
770 } | |
771 } | |
772 else | |
773 { | |
774 //////////////////////////////////////////////////////////////// | |
775 // Not a 1, 2, 4, or 8 Byte Type | |
776 //////////////////////////////////////////////////////////////// | |
777 | |
778 | |
779 pragma( msg, "Invalid template type specified." ); | |
780 static assert( false ); | |
781 } | |
782 } | |
783 } | |
784 | |
785 | |
786 //////////////////////////////////////////////////////////////////////////// | |
787 // Atomic Increment | |
788 //////////////////////////////////////////////////////////////////////////// | |
789 | |
790 | |
791 template atomicIncrement( msync ms = msync.seq, T ) | |
792 { | |
793 // | |
794 // NOTE: This operation is only valid for integer or pointer types | |
795 // | |
796 static assert( isValidNumericType!(T) ); | |
797 | |
798 | |
799 T atomicIncrement( inout T val ) | |
800 in | |
801 { | |
802 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
803 } | |
804 body | |
805 { | |
806 static if( T.sizeof == byte.sizeof ) | |
807 { | |
808 //////////////////////////////////////////////////////////////// | |
809 // 1 Byte Increment | |
810 //////////////////////////////////////////////////////////////// | |
811 | |
812 | |
813 volatile asm | |
814 { | |
815 mov EAX, val; | |
816 lock; // lock always needed to make this op atomic | |
817 inc [EAX]; | |
818 mov AL, [EAX]; | |
819 } | |
820 } | |
821 else static if( T.sizeof == short.sizeof ) | |
822 { | |
823 //////////////////////////////////////////////////////////////// | |
824 // 2 Byte Increment | |
825 //////////////////////////////////////////////////////////////// | |
826 | |
827 | |
828 volatile asm | |
829 { | |
830 mov EAX, val; | |
831 lock; // lock always needed to make this op atomic | |
832 inc [EAX]; | |
833 mov AX, [EAX]; | |
834 } | |
835 } | |
836 else static if( T.sizeof == int.sizeof ) | |
837 { | |
838 //////////////////////////////////////////////////////////////// | |
839 // 4 Byte Increment | |
840 //////////////////////////////////////////////////////////////// | |
841 | |
842 | |
843 volatile asm | |
844 { | |
845 mov EAX, val; | |
846 lock; // lock always needed to make this op atomic | |
847 inc [EAX]; | |
848 mov EAX, [EAX]; | |
849 } | |
850 } | |
851 else static if( T.sizeof == long.sizeof ) | |
852 { | |
853 //////////////////////////////////////////////////////////////// | |
854 // 8 Byte Increment | |
855 //////////////////////////////////////////////////////////////// | |
856 | |
857 | |
858 version( Has64BitOps ) | |
859 { | |
860 //////////////////////////////////////////////////////////// | |
861 // 8 Byte Increment on 64-Bit Processor | |
862 //////////////////////////////////////////////////////////// | |
863 | |
864 | |
865 volatile asm | |
866 { | |
867 mov RAX, val; | |
868 lock; // lock always needed to make this op atomic | |
869 inc [RAX]; | |
870 mov RAX, [RAX]; | |
871 } | |
872 } | |
873 else | |
874 { | |
875 //////////////////////////////////////////////////////////// | |
876 // 8 Byte Increment on 32-Bit Processor | |
877 //////////////////////////////////////////////////////////// | |
878 | |
879 | |
880 pragma( msg, "This operation is only available on 64-bit platforms." ); | |
881 static assert( false ); | |
882 } | |
883 } | |
884 else | |
885 { | |
886 //////////////////////////////////////////////////////////////// | |
887 // Not a 1, 2, 4, or 8 Byte Type | |
888 //////////////////////////////////////////////////////////////// | |
889 | |
890 | |
891 pragma( msg, "Invalid template type specified." ); | |
892 static assert( false ); | |
893 } | |
894 } | |
895 } | |
896 | |
897 | |
898 //////////////////////////////////////////////////////////////////////////// | |
899 // Atomic Decrement | |
900 //////////////////////////////////////////////////////////////////////////// | |
901 | |
902 | |
903 template atomicDecrement( msync ms = msync.seq, T ) | |
904 { | |
905 // | |
906 // NOTE: This operation is only valid for integer or pointer types | |
907 // | |
908 static assert( isValidNumericType!(T) ); | |
909 | |
910 | |
911 T atomicDecrement( inout T val ) | |
912 in | |
913 { | |
914 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
915 } | |
916 body | |
917 { | |
918 static if( T.sizeof == byte.sizeof ) | |
919 { | |
920 //////////////////////////////////////////////////////////////// | |
921 // 1 Byte Decrement | |
922 //////////////////////////////////////////////////////////////// | |
923 | |
924 | |
925 volatile asm | |
926 { | |
927 mov EAX, val; | |
928 lock; // lock always needed to make this op atomic | |
929 dec [EAX]; | |
930 mov AL, [EAX]; | |
931 } | |
932 } | |
933 else static if( T.sizeof == short.sizeof ) | |
934 { | |
935 //////////////////////////////////////////////////////////////// | |
936 // 2 Byte Decrement | |
937 //////////////////////////////////////////////////////////////// | |
938 | |
939 | |
940 volatile asm | |
941 { | |
942 mov EAX, val; | |
943 lock; // lock always needed to make this op atomic | |
944 dec [EAX]; | |
945 mov AX, [EAX]; | |
946 } | |
947 } | |
948 else static if( T.sizeof == int.sizeof ) | |
949 { | |
950 //////////////////////////////////////////////////////////////// | |
951 // 4 Byte Decrement | |
952 //////////////////////////////////////////////////////////////// | |
953 | |
954 | |
955 volatile asm | |
956 { | |
957 mov EAX, val; | |
958 lock; // lock always needed to make this op atomic | |
959 dec [EAX]; | |
960 mov EAX, [EAX]; | |
961 } | |
962 } | |
963 else static if( T.sizeof == long.sizeof ) | |
964 { | |
965 //////////////////////////////////////////////////////////////// | |
966 // 8 Byte Decrement | |
967 //////////////////////////////////////////////////////////////// | |
968 | |
969 | |
970 version( Has64BitOps ) | |
971 { | |
972 //////////////////////////////////////////////////////////// | |
973 // 8 Byte Decrement on 64-Bit Processor | |
974 //////////////////////////////////////////////////////////// | |
975 | |
976 | |
977 volatile asm | |
978 { | |
979 mov RAX, val; | |
980 lock; // lock always needed to make this op atomic | |
981 dec [RAX]; | |
982 mov RAX, [RAX]; | |
983 } | |
984 } | |
985 else | |
986 { | |
987 //////////////////////////////////////////////////////////// | |
988 // 8 Byte Decrement on 32-Bit Processor | |
989 //////////////////////////////////////////////////////////// | |
990 | |
991 | |
992 pragma( msg, "This operation is only available on 64-bit platforms." ); | |
993 static assert( false ); | |
994 } | |
995 } | |
996 else | |
997 { | |
998 //////////////////////////////////////////////////////////////// | |
999 // Not a 1, 2, 4, or 8 Byte Type | |
1000 //////////////////////////////////////////////////////////////// | |
1001 | |
1002 | |
1003 pragma( msg, "Invalid template type specified." ); | |
1004 static assert( false ); | |
1005 } | |
1006 } | |
1007 } | |
1008 } | |
1009 else | |
1010 { | |
1011 version( BuildInfo ) | |
1012 { | |
1013 pragma( msg, "tango.core.Atomic: using synchronized ops" ); | |
1014 } | |
1015 | |
1016 private | |
1017 { | |
1018 //////////////////////////////////////////////////////////////////////// | |
1019 // Default Value Requirements | |
1020 //////////////////////////////////////////////////////////////////////// | |
1021 | |
1022 | |
1023 template atomicValueIsProperlyAligned( T ) | |
1024 { | |
1025 bool atomicValueIsProperlyAligned( size_t addr ) | |
1026 { | |
1027 return addr % T.sizeof == 0; | |
1028 } | |
1029 } | |
1030 | |
1031 | |
1032 //////////////////////////////////////////////////////////////////////// | |
1033 // Default Synchronization Requirements | |
1034 //////////////////////////////////////////////////////////////////////// | |
1035 | |
1036 | |
1037 template needsLoadBarrier( msync ms ) | |
1038 { | |
1039 const bool needsLoadBarrier = ms != msync.raw; | |
1040 } | |
1041 | |
1042 | |
1043 template needsStoreBarrier( msync ms ) | |
1044 { | |
1045 const bool needsStoreBarrier = ms != msync.raw; | |
1046 } | |
1047 } | |
1048 | |
1049 | |
1050 //////////////////////////////////////////////////////////////////////////// | |
1051 // Atomic Load | |
1052 //////////////////////////////////////////////////////////////////////////// | |
1053 | |
1054 | |
1055 template atomicLoad( msync ms = msync.seq, T ) | |
1056 { | |
1057 T atomicLoad( inout T val ) | |
1058 in | |
1059 { | |
1060 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
1061 } | |
1062 body | |
1063 { | |
1064 static if( T.sizeof <= (void*).sizeof ) | |
1065 { | |
1066 //////////////////////////////////////////////////////////////// | |
1067 // <= (void*).sizeof Byte Load | |
1068 //////////////////////////////////////////////////////////////// | |
1069 | |
1070 | |
1071 static if( needsLoadBarrier!(ms) ) | |
1072 { | |
1073 synchronized | |
1074 { | |
1075 return val; | |
1076 } | |
1077 } | |
1078 else | |
1079 { | |
1080 volatile | |
1081 { | |
1082 return val; | |
1083 } | |
1084 } | |
1085 } | |
1086 else | |
1087 { | |
1088 //////////////////////////////////////////////////////////////// | |
1089 // > (void*).sizeof Byte Type | |
1090 //////////////////////////////////////////////////////////////// | |
1091 | |
1092 | |
1093 pragma( msg, "Invalid template type specified." ); | |
1094 static assert( false ); | |
1095 } | |
1096 } | |
1097 } | |
1098 | |
1099 | |
1100 //////////////////////////////////////////////////////////////////////////// | |
1101 // Atomic Store | |
1102 //////////////////////////////////////////////////////////////////////////// | |
1103 | |
1104 | |
1105 template atomicStore( msync ms = msync.seq, T ) | |
1106 { | |
1107 void atomicStore( inout T val, T newval ) | |
1108 in | |
1109 { | |
1110 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
1111 } | |
1112 body | |
1113 { | |
1114 static if( T.sizeof <= (void*).sizeof ) | |
1115 { | |
1116 //////////////////////////////////////////////////////////////// | |
1117 // <= (void*).sizeof Byte Store | |
1118 //////////////////////////////////////////////////////////////// | |
1119 | |
1120 | |
1121 static if( needsStoreBarrier!(ms) ) | |
1122 { | |
1123 synchronized | |
1124 { | |
1125 val = newval; | |
1126 } | |
1127 } | |
1128 else | |
1129 { | |
1130 volatile | |
1131 { | |
1132 val = newval; | |
1133 } | |
1134 } | |
1135 } | |
1136 else | |
1137 { | |
1138 //////////////////////////////////////////////////////////////// | |
1139 // > (void*).sizeof Byte Type | |
1140 //////////////////////////////////////////////////////////////// | |
1141 | |
1142 | |
1143 pragma( msg, "Invalid template type specified." ); | |
1144 static assert( false ); | |
1145 } | |
1146 } | |
1147 } | |
1148 | |
1149 | |
1150 //////////////////////////////////////////////////////////////////////////// | |
1151 // Atomic Store If | |
1152 //////////////////////////////////////////////////////////////////////////// | |
1153 | |
1154 | |
1155 template atomicStoreIf( msync ms = msync.seq, T ) | |
1156 { | |
1157 bool atomicStoreIf( inout T val, T newval, T equalTo ) | |
1158 in | |
1159 { | |
1160 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
1161 } | |
1162 body | |
1163 { | |
1164 static if( T.sizeof <= (void*).sizeof ) | |
1165 { | |
1166 //////////////////////////////////////////////////////////////// | |
1167 // <= (void*).sizeof Byte StoreIf | |
1168 //////////////////////////////////////////////////////////////// | |
1169 | |
1170 | |
1171 synchronized | |
1172 { | |
1173 if( val == equalTo ) | |
1174 { | |
1175 val = newval; | |
1176 return true; | |
1177 } | |
1178 return false; | |
1179 } | |
1180 } | |
1181 else | |
1182 { | |
1183 //////////////////////////////////////////////////////////////// | |
1184 // > (void*).sizeof Byte Type | |
1185 //////////////////////////////////////////////////////////////// | |
1186 | |
1187 | |
1188 pragma( msg, "Invalid template type specified." ); | |
1189 static assert( false ); | |
1190 } | |
1191 } | |
1192 } | |
1193 | |
1194 | |
1195 ///////////////////////////////////////////////////////////////////////////// | |
1196 // Atomic Increment | |
1197 //////////////////////////////////////////////////////////////////////////// | |
1198 | |
1199 | |
1200 template atomicIncrement( msync ms = msync.seq, T ) | |
1201 { | |
1202 // | |
1203 // NOTE: This operation is only valid for integer or pointer types | |
1204 // | |
1205 static assert( isValidNumericType!(T) ); | |
1206 | |
1207 | |
1208 T atomicIncrement( inout T val ) | |
1209 in | |
1210 { | |
1211 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
1212 } | |
1213 body | |
1214 { | |
1215 static if( T.sizeof <= (void*).sizeof ) | |
1216 { | |
1217 //////////////////////////////////////////////////////////////// | |
1218 // <= (void*).sizeof Byte Increment | |
1219 //////////////////////////////////////////////////////////////// | |
1220 | |
1221 | |
1222 synchronized | |
1223 { | |
1224 return ++val; | |
1225 } | |
1226 } | |
1227 else | |
1228 { | |
1229 //////////////////////////////////////////////////////////////// | |
1230 // > (void*).sizeof Byte Type | |
1231 //////////////////////////////////////////////////////////////// | |
1232 | |
1233 | |
1234 pragma( msg, "Invalid template type specified." ); | |
1235 static assert( false ); | |
1236 } | |
1237 } | |
1238 } | |
1239 | |
1240 | |
1241 //////////////////////////////////////////////////////////////////////////// | |
1242 // Atomic Decrement | |
1243 //////////////////////////////////////////////////////////////////////////// | |
1244 | |
1245 | |
1246 template atomicDecrement( msync ms = msync.seq, T ) | |
1247 { | |
1248 // | |
1249 // NOTE: This operation is only valid for integer or pointer types | |
1250 // | |
1251 static assert( isValidNumericType!(T) ); | |
1252 | |
1253 | |
1254 T atomicDecrement( inout T val ) | |
1255 in | |
1256 { | |
1257 assert( atomicValueIsProperlyAligned!(T)( cast(size_t) &val ) ); | |
1258 } | |
1259 body | |
1260 { | |
1261 static if( T.sizeof <= (void*).sizeof ) | |
1262 { | |
1263 //////////////////////////////////////////////////////////////// | |
1264 // <= (void*).sizeof Byte Decrement | |
1265 //////////////////////////////////////////////////////////////// | |
1266 | |
1267 | |
1268 synchronized | |
1269 { | |
1270 return --val; | |
1271 } | |
1272 } | |
1273 else | |
1274 { | |
1275 //////////////////////////////////////////////////////////////// | |
1276 // > (void*).sizeof Byte Type | |
1277 //////////////////////////////////////////////////////////////// | |
1278 | |
1279 | |
1280 pragma( msg, "Invalid template type specified." ); | |
1281 static assert( false ); | |
1282 } | |
1283 } | |
1284 } | |
1285 } | |
1286 | |
1287 | |
1288 //////////////////////////////////////////////////////////////////////////////// | |
1289 // Atomic | |
1290 //////////////////////////////////////////////////////////////////////////////// | |
1291 | |
1292 | |
1293 /** | |
1294 * This struct represents a value which will be subject to competing access. | |
1295 * All accesses to this value will be synchronized with main memory, and | |
1296 * various memory barriers may be employed for instruction ordering. Any | |
1297 * primitive type of size equal to or smaller than the memory bus size is | |
1298 * allowed, so 32-bit machines may use values with size <= int.sizeof and | |
1299 * 64-bit machines may use values with size <= long.sizeof. The one exception | |
1300 * to this rule is that architectures that support DCAS will allow double-wide | |
1301 * storeIf operations. The 32-bit x86 architecture, for example, supports | |
1302 * 64-bit storeIf operations. | |
1303 */ | |
1304 struct Atomic( T ) | |
1305 { | |
1306 //////////////////////////////////////////////////////////////////////////// | |
1307 // Atomic Load | |
1308 //////////////////////////////////////////////////////////////////////////// | |
1309 | |
1310 | |
1311 template load( msync ms = msync.seq ) | |
1312 { | |
1313 static assert( ms == msync.raw || ms == msync.hlb || | |
1314 ms == msync.acq || ms == msync.seq, | |
1315 "ms must be one of: msync.raw, msync.hlb, msync.acq, msync.seq" ); | |
1316 | |
1317 /** | |
1318 * Refreshes the contents of this value from main memory. This | |
1319 * operation is both lock-free and atomic. | |
1320 * | |
1321 * Returns: | |
1322 * The loaded value. | |
1323 */ | |
1324 T load() | |
1325 { | |
1326 return atomicLoad!(ms,T)( m_val ); | |
1327 } | |
1328 } | |
1329 | |
1330 | |
1331 //////////////////////////////////////////////////////////////////////////// | |
1332 // Atomic Store | |
1333 //////////////////////////////////////////////////////////////////////////// | |
1334 | |
1335 | |
1336 template store( msync ms = msync.seq ) | |
1337 { | |
1338 static assert( ms == msync.raw || ms == msync.ssb || | |
1339 ms == msync.acq || ms == msync.rel || | |
1340 ms == msync.seq, | |
1341 "ms must be one of: msync.raw, msync.ssb, msync.acq, msync.rel, msync.seq" ); | |
1342 | |
1343 /** | |
1344 * Stores 'newval' to the memory referenced by this value. This | |
1345 * operation is both lock-free and atomic. | |
1346 * | |
1347 * Params: | |
1348 * newval = The value to store. | |
1349 */ | |
1350 void store( T newval ) | |
1351 { | |
1352 atomicStore!(ms,T)( m_val, newval ); | |
1353 } | |
1354 } | |
1355 | |
1356 | |
1357 //////////////////////////////////////////////////////////////////////////// | |
1358 // Atomic StoreIf | |
1359 //////////////////////////////////////////////////////////////////////////// | |
1360 | |
1361 | |
1362 template storeIf( msync ms = msync.seq ) | |
1363 { | |
1364 static assert( ms == msync.raw || ms == msync.ssb || | |
1365 ms == msync.acq || ms == msync.rel || | |
1366 ms == msync.seq, | |
1367 "ms must be one of: msync.raw, msync.ssb, msync.acq, msync.rel, msync.seq" ); | |
1368 | |
1369 /** | |
1370 * Stores 'newval' to the memory referenced by this value if val is | |
1371 * equal to 'equalTo'. This operation is both lock-free and atomic. | |
1372 * | |
1373 * Params: | |
1374 * newval = The value to store. | |
1375 * equalTo = The comparison value. | |
1376 * | |
1377 * Returns: | |
1378 * true if the store occurred, false if not. | |
1379 */ | |
1380 bool storeIf( T newval, T equalTo ) | |
1381 { | |
1382 return atomicStoreIf!(ms,T)( m_val, newval, equalTo ); | |
1383 } | |
1384 } | |
1385 | |
1386 | |
1387 //////////////////////////////////////////////////////////////////////////// | |
1388 // Numeric Functions | |
1389 //////////////////////////////////////////////////////////////////////////// | |
1390 | |
1391 | |
1392 /** | |
1393 * The following additional functions are available for integer types. | |
1394 */ | |
1395 static if( isValidNumericType!(T) ) | |
1396 { | |
1397 //////////////////////////////////////////////////////////////////////// | |
1398 // Atomic Increment | |
1399 //////////////////////////////////////////////////////////////////////// | |
1400 | |
1401 | |
1402 template increment( msync ms = msync.seq ) | |
1403 { | |
1404 static assert( ms == msync.raw || ms == msync.ssb || | |
1405 ms == msync.acq || ms == msync.rel || | |
1406 ms == msync.seq, | |
1407 "ms must be one of: msync.raw, msync.ssb, msync.acq, msync.rel, msync.seq" ); | |
1408 | |
1409 /** | |
1410 * This operation is only legal for built-in value and pointer | |
1411 * types, and is equivalent to an atomic "val = val + 1" operation. | |
1412 * This function exists to facilitate use of the optimized | |
1413 * increment instructions provided by some architecures. If no | |
1414 * such instruction exists on the target platform then the | |
1415 * behavior will perform the operation using more traditional | |
1416 * means. This operation is both lock-free and atomic. | |
1417 * | |
1418 * Returns: | |
1419 * The result of an atomicLoad of val immediately following the | |
1420 * increment operation. This value is not required to be equal to | |
1421 * the newly stored value. Thus, competing writes are allowed to | |
1422 * occur between the increment and successive load operation. | |
1423 */ | |
1424 T increment() | |
1425 { | |
1426 return atomicIncrement!(ms,T)( m_val ); | |
1427 } | |
1428 } | |
1429 | |
1430 | |
1431 //////////////////////////////////////////////////////////////////////// | |
1432 // Atomic Decrement | |
1433 //////////////////////////////////////////////////////////////////////// | |
1434 | |
1435 | |
1436 template decrement( msync ms = msync.seq ) | |
1437 { | |
1438 static assert( ms == msync.raw || ms == msync.ssb || | |
1439 ms == msync.acq || ms == msync.rel || | |
1440 ms == msync.seq, | |
1441 "ms must be one of: msync.raw, msync.ssb, msync.acq, msync.rel, msync.seq" ); | |
1442 | |
1443 /** | |
1444 * This operation is only legal for built-in value and pointer | |
1445 * types, and is equivalent to an atomic "val = val - 1" operation. | |
1446 * This function exists to facilitate use of the optimized | |
1447 * decrement instructions provided by some architecures. If no | |
1448 * such instruction exists on the target platform then the behavior | |
1449 * will perform the operation using more traditional means. This | |
1450 * operation is both lock-free and atomic. | |
1451 * | |
1452 * Returns: | |
1453 * The result of an atomicLoad of val immediately following the | |
1454 * increment operation. This value is not required to be equal to | |
1455 * the newly stored value. Thus, competing writes are allowed to | |
1456 * occur between the increment and successive load operation. | |
1457 */ | |
1458 T decrement() | |
1459 { | |
1460 return atomicDecrement!(ms,T)( m_val ); | |
1461 } | |
1462 } | |
1463 } | |
1464 | |
1465 private: | |
1466 T m_val; | |
1467 } | |
1468 | |
1469 | |
1470 //////////////////////////////////////////////////////////////////////////////// | |
1471 // Support Code for Unit Tests | |
1472 //////////////////////////////////////////////////////////////////////////////// | |
1473 | |
1474 | |
1475 private | |
1476 { | |
1477 version( DDoc ) {} else | |
1478 { | |
1479 template testLoad( msync ms, T ) | |
1480 { | |
1481 void testLoad( T val = T.init + 1 ) | |
1482 { | |
1483 T base; | |
1484 Atomic!(T) atom; | |
1485 | |
1486 assert( atom.load!(ms)() == base ); | |
1487 base = val; | |
1488 atom.m_val = val; | |
1489 assert( atom.load!(ms)() == base ); | |
1490 } | |
1491 } | |
1492 | |
1493 | |
1494 template testStore( msync ms, T ) | |
1495 { | |
1496 void testStore( T val = T.init + 1 ) | |
1497 { | |
1498 T base; | |
1499 Atomic!(T) atom; | |
1500 | |
1501 assert( atom.m_val == base ); | |
1502 base = val; | |
1503 atom.store!(ms)( base ); | |
1504 assert( atom.m_val == base ); | |
1505 } | |
1506 } | |
1507 | |
1508 | |
1509 template testStoreIf( msync ms, T ) | |
1510 { | |
1511 void testStoreIf( T val = T.init + 1 ) | |
1512 { | |
1513 T base; | |
1514 Atomic!(T) atom; | |
1515 | |
1516 assert( atom.m_val == base ); | |
1517 base = val; | |
1518 atom.storeIf!(ms)( base, val ); | |
1519 assert( atom.m_val != base ); | |
1520 atom.storeIf!(ms)( base, T.init ); | |
1521 assert( atom.m_val == base ); | |
1522 } | |
1523 } | |
1524 | |
1525 | |
1526 template testIncrement( msync ms, T ) | |
1527 { | |
1528 void testIncrement( T val = T.init + 1 ) | |
1529 { | |
1530 T base = val; | |
1531 T incr = val; | |
1532 Atomic!(T) atom; | |
1533 | |
1534 atom.m_val = val; | |
1535 assert( atom.m_val == base && incr == base ); | |
1536 base = cast(T)( base + 1 ); | |
1537 incr = atom.increment!(ms)(); | |
1538 assert( atom.m_val == base && incr == base ); | |
1539 } | |
1540 } | |
1541 | |
1542 | |
1543 template testDecrement( msync ms, T ) | |
1544 { | |
1545 void testDecrement( T val = T.init + 1 ) | |
1546 { | |
1547 T base = val; | |
1548 T decr = val; | |
1549 Atomic!(T) atom; | |
1550 | |
1551 atom.m_val = val; | |
1552 assert( atom.m_val == base && decr == base ); | |
1553 base = cast(T)( base - 1 ); | |
1554 decr = atom.decrement!(ms)(); | |
1555 assert( atom.m_val == base && decr == base ); | |
1556 } | |
1557 } | |
1558 | |
1559 | |
1560 template testType( T ) | |
1561 { | |
1562 void testType( T val = T.init +1 ) | |
1563 { | |
1564 testLoad!(msync.raw, T)( val ); | |
1565 testLoad!(msync.hlb, T)( val ); | |
1566 testLoad!(msync.acq, T)( val ); | |
1567 testLoad!(msync.seq, T)( val ); | |
1568 | |
1569 testStore!(msync.raw, T)( val ); | |
1570 testStore!(msync.ssb, T)( val ); | |
1571 testStore!(msync.acq, T)( val ); | |
1572 testStore!(msync.rel, T)( val ); | |
1573 testStore!(msync.seq, T)( val ); | |
1574 | |
1575 testStoreIf!(msync.raw, T)( val ); | |
1576 testStoreIf!(msync.ssb, T)( val ); | |
1577 testStoreIf!(msync.acq, T)( val ); | |
1578 testStoreIf!(msync.rel, T)( val ); | |
1579 testStoreIf!(msync.seq, T)( val ); | |
1580 | |
1581 static if( isValidNumericType!(T) ) | |
1582 { | |
1583 testIncrement!(msync.raw, T)( val ); | |
1584 testIncrement!(msync.ssb, T)( val ); | |
1585 testIncrement!(msync.acq, T)( val ); | |
1586 testIncrement!(msync.rel, T)( val ); | |
1587 testIncrement!(msync.seq, T)( val ); | |
1588 | |
1589 testDecrement!(msync.raw, T)( val ); | |
1590 testDecrement!(msync.ssb, T)( val ); | |
1591 testDecrement!(msync.acq, T)( val ); | |
1592 testDecrement!(msync.rel, T)( val ); | |
1593 testDecrement!(msync.seq, T)( val ); | |
1594 } | |
1595 } | |
1596 } | |
1597 } | |
1598 } | |
1599 | |
1600 | |
1601 //////////////////////////////////////////////////////////////////////////////// | |
1602 // Unit Tests | |
1603 //////////////////////////////////////////////////////////////////////////////// | |
1604 | |
1605 | |
1606 debug( UnitTest ) | |
1607 { | |
1608 unittest | |
1609 { | |
1610 testType!(bool)(); | |
1611 | |
1612 testType!(byte)(); | |
1613 testType!(ubyte)(); | |
1614 | |
1615 testType!(short)(); | |
1616 testType!(ushort)(); | |
1617 | |
1618 testType!(int)(); | |
1619 testType!(uint)(); | |
1620 | |
1621 int x; | |
1622 testType!(void*)( &x ); | |
1623 | |
1624 version( Has64BitOps ) | |
1625 { | |
1626 testType!(long)(); | |
1627 testType!(ulong)(); | |
1628 } | |
1629 else version( Has64BitCAS ) | |
1630 { | |
1631 testStoreIf!(msync.raw, long)(); | |
1632 testStoreIf!(msync.ssb, long)(); | |
1633 testStoreIf!(msync.acq, long)(); | |
1634 testStoreIf!(msync.rel, long)(); | |
1635 testStoreIf!(msync.seq, long)(); | |
1636 | |
1637 testStoreIf!(msync.raw, ulong)(); | |
1638 testStoreIf!(msync.ssb, ulong)(); | |
1639 testStoreIf!(msync.acq, ulong)(); | |
1640 testStoreIf!(msync.rel, ulong)(); | |
1641 testStoreIf!(msync.seq, ulong)(); | |
1642 } | |
1643 } | |
1644 } |