@@ -240,10 +240,11 @@ WINDOWS_ONLY(public:) // VS2017 warns (C2027) use of undefined type if IsPointer
240
240
// bytes and (if different) pointer size bytes are required. The
241
241
// class must be default constructable, with these requirements:
242
242
//
243
- // - dest is of type D*, an integral or pointer type.
243
+ // - dest is of type D*, where D is an integral or pointer type.
244
244
// - add_value is of type I, an integral type.
245
245
// - sizeof(I) == sizeof(D).
246
246
// - if D is an integral type, I == D.
247
+ // - if D is a pointer type P*, sizeof(P) == 1.
247
248
// - order is of type atomic_memory_order.
248
249
// - platform_add is an object of type PlatformAdd<sizeof(D)>.
249
250
//
@@ -258,9 +259,17 @@ WINDOWS_ONLY(public:) // VS2017 warns (C2027) use of undefined type if IsPointer
258
259
// fetch_and_add atomically adds add_value to the value of dest,
259
260
// returning the old value.
260
261
//
261
- // When D is a pointer type P*, both add_and_fetch and fetch_and_add
262
- // treat it as if it were an uintptr_t; they do not perform any
263
- // scaling of add_value, as that has already been done by the caller.
262
+ // When the destination type D of the Atomic operation is a pointer type P*,
263
+ // the addition must scale the add_value by sizeof(P) to add that many bytes
264
+ // to the destination value. Rather than requiring each platform deal with
265
+ // this, the shared part of the implementation performs some adjustments
266
+ // before and after calling the platform operation. It ensures the pointee
267
+ // type of the destination value passed to the platform operation has size
268
+ // 1, casting if needed. It also scales add_value by sizeof(P). The result
269
+ // of the platform operation is cast back to P*. This means the platform
270
+ // operation does not need to account for the scaling. It also makes it
271
+ // easy for the platform to implement one of add_and_fetch or fetch_and_add
272
+ // in terms of the other (which is a common approach).
264
273
//
265
274
// No definition is provided; all platforms must explicitly define
266
275
// this class and any needed specializations.
@@ -690,21 +699,43 @@ struct Atomic::AddImpl<
690
699
{
691
700
STATIC_ASSERT (sizeof (intptr_t ) == sizeof (P*));
692
701
STATIC_ASSERT (sizeof (uintptr_t ) == sizeof (P*));
693
- typedef typename Conditional<IsSigned<I>::value,
694
- intptr_t ,
695
- uintptr_t >::type CI;
696
702
697
- static CI scale_addend (CI add_value) {
698
- return add_value * sizeof (P);
703
+ // Type of the scaled addend. An integral type of the same size as a
704
+ // pointer, and the same signedness as I.
705
+ using SI = typename Conditional<IsSigned<I>::value, intptr_t , uintptr_t >::type;
706
+
707
+ // Type of the unscaled destination. A pointer type with pointee size == 1.
708
+ using UP = const char *;
709
+
710
+ // Scale add_value by the size of the pointee.
711
+ static SI scale_addend (SI add_value) {
712
+ return add_value * SI (sizeof (P));
713
+ }
714
+
715
+ // Casting between P* and UP* here intentionally uses C-style casts,
716
+ // because reinterpret_cast can't cast away cv qualifiers. Using copy_cv
717
+ // would be an alternative if it existed.
718
+
719
+ // Unscale dest to a char* pointee for consistency with scaled addend.
720
+ static UP volatile * unscale_dest (P* volatile * dest) {
721
+ return (UP volatile *) dest;
722
+ }
723
+
724
+ // Convert the unscaled char* result to a P*.
725
+ static P* scale_result (UP result) {
726
+ return (P*) result;
699
727
}
700
728
701
- static P* add_and_fetch (P* volatile * dest, I add_value, atomic_memory_order order) {
702
- CI addend = add_value;
703
- return PlatformAdd<sizeof (P*)>().add_and_fetch (dest, scale_addend (addend), order);
729
+ static P* add_and_fetch (P* volatile * dest, I addend, atomic_memory_order order) {
730
+ return scale_result (PlatformAdd<sizeof (P*)>().add_and_fetch (unscale_dest (dest),
731
+ scale_addend (addend),
732
+ order));
704
733
}
705
- static P* fetch_and_add (P* volatile * dest, I add_value, atomic_memory_order order) {
706
- CI addend = add_value;
707
- return PlatformAdd<sizeof (P*)>().fetch_and_add (dest, scale_addend (addend), order);
734
+
735
+ static P* fetch_and_add (P* volatile * dest, I addend, atomic_memory_order order) {
736
+ return scale_result (PlatformAdd<sizeof (P*)>().fetch_and_add (unscale_dest (dest),
737
+ scale_addend (addend),
738
+ order));
708
739
}
709
740
};
710
741
0 commit comments