1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //
6 //  This software is distributed WITHOUT ANY WARRANTY; without even
7 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 //  PURPOSE.  See the above copyright notice for more information.
9 //============================================================================
10 
11 #ifndef vtk_m_cont_BitField_h
12 #define vtk_m_cont_BitField_h
13 
14 #include <vtkm/cont/ArrayHandle.h>
15 #include <vtkm/cont/vtkm_cont_export.h>
16 
17 #include <vtkm/Atomic.h>
18 #include <vtkm/Deprecated.h>
19 #include <vtkm/List.h>
20 #include <vtkm/Types.h>
21 
22 #include <cassert>
23 #include <climits>
24 #include <memory>
25 #include <type_traits>
26 
27 namespace vtkm
28 {
29 namespace cont
30 {
31 
32 class BitField;
33 
34 namespace internal
35 {
36 
37 struct StorageTagBitField;
38 
39 struct VTKM_ALWAYS_EXPORT BitFieldMetaData
40 {
41   vtkm::Id NumberOfBits = 0;
42 };
43 
44 }
45 
46 namespace detail
47 {
48 
49 struct BitFieldTraits
50 {
51   // Allocations will occur in blocks of BlockSize bytes. This ensures that
52   // power-of-two word sizes up to BlockSize will not access invalid data
53   // during word-based access, and that atomic values will be properly aligned.
54   // We use the default StorageBasic alignment for this.
55   constexpr static vtkm::Id BlockSize = VTKM_ALLOCATION_ALIGNMENT;
56 
57   // Make sure the blocksize is at least 64. Eventually we may implement SIMD
58   // bit operations, and the current largest vector width is 512 bits.
59   VTKM_STATIC_ASSERT(BlockSize >= 64);
60 
61   /// Require an unsigned integral type that is <= BlockSize bytes.
62   template <typename WordType>
63   using IsValidWordType =
64     std::integral_constant<bool,
65                            /* is unsigned */
66                            std::is_unsigned<WordType>::value &&
67                              /* doesn't exceed blocksize */
68                              sizeof(WordType) <= static_cast<size_t>(BlockSize) &&
69                              /* BlockSize is a multiple of WordType */
70                              static_cast<size_t>(BlockSize) % sizeof(WordType) == 0>;
71 
72   /// Require an unsigned integral type that is <= BlockSize bytes, and is
73   /// is supported by the specified AtomicInterface.
74   template <typename WordType>
75   using IsValidWordTypeAtomic =
76     std::integral_constant<bool,
77                            /* is unsigned */
78                            std::is_unsigned<WordType>::value &&
79                              /* doesn't exceed blocksize */
80                              sizeof(WordType) <= static_cast<size_t>(BlockSize) &&
81                              /* BlockSize is a multiple of WordType */
82                              static_cast<size_t>(BlockSize) % sizeof(WordType) == 0 &&
83                              /* Supported by atomic interface */
84                              vtkm::ListHas<vtkm::AtomicTypesSupported, WordType>::value>;
85 };
86 
87 /// Identifies a bit in a BitField by Word and BitOffset. Note that these
88 /// values are dependent on the type of word used to generate the coordinate.
89 struct BitCoordinate
90 {
91   /// The word containing the specified bit.
92   vtkm::Id WordIndex;
93 
94   /// The zero-indexed bit in the word.
95   vtkm::Int32 BitOffset; // [0, bitsInWord)
96 };
97 
98 /// Portal for performing bit or word operations on a BitField.
99 ///
100 /// This is the implementation used by BitPortal and BitPortalConst.
101 template <bool IsConst>
102 class BitPortalBase
103 {
104   // Checks if PortalType has a GetIteratorBegin() method that returns a
105   // pointer.
106   template <typename PortalType,
107             typename PointerType = decltype(std::declval<PortalType>().GetIteratorBegin())>
108   struct HasPointerAccess : public std::is_pointer<PointerType>
109   {
110   };
111 
112   // Determine whether we should store a const vs. mutable pointer:
113   template <typename T>
114   using MaybeConstPointer = typename std::conditional<IsConst, T const*, T*>::type;
115   using BufferType = MaybeConstPointer<void>; // void* or void const*, as appropriate
116 
117 public:
118   /// The fastest word type for performing bitwise operations through AtomicInterface.
119   using WordTypePreferred = vtkm::AtomicTypePreferred;
120 
121   /// MPL check for whether a WordType may be used for non-atomic operations.
122   template <typename WordType>
123   using IsValidWordType = BitFieldTraits::IsValidWordType<WordType>;
124 
125   /// MPL check for whether a WordType may be used for atomic operations.
126   template <typename WordType>
127   using IsValidWordTypeAtomic = BitFieldTraits::IsValidWordTypeAtomic<WordType>;
128 
129   VTKM_STATIC_ASSERT_MSG(IsValidWordType<WordTypeDefault>::value,
130                          "Internal error: Default word type is invalid.");
131   VTKM_STATIC_ASSERT_MSG(IsValidWordType<WordTypePreferred>::value,
132                          "Device-specific fast word type is invalid.");
133 
134   VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordTypeDefault>::value,
135                          "Internal error: Default word type is invalid.");
136   VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordTypePreferred>::value,
137                          "Device-specific fast word type is invalid for atomic operations.");
138 
139 protected:
140   friend class vtkm::cont::BitField;
141   friend class vtkm::cont::internal::Storage<bool, vtkm::cont::internal::StorageTagBitField>;
142 
143   /// Construct a BitPortal from a raw array.
BitPortalBase(BufferType rawArray,vtkm::Id numberOfBits)144   VTKM_CONT BitPortalBase(BufferType rawArray, vtkm::Id numberOfBits)
145     : Data{ rawArray }
146     , NumberOfBits{ numberOfBits }
147   {
148   }
149 
150 public:
151   BitPortalBase() noexcept = default;
152   BitPortalBase(const BitPortalBase&) noexcept = default;
153   BitPortalBase(BitPortalBase&&) noexcept = default;
154   BitPortalBase& operator=(const BitPortalBase&) noexcept = default;
155   BitPortalBase& operator=(BitPortalBase&&) noexcept = default;
156 
157   /// Returns the number of bits in the BitField.
158   VTKM_EXEC_CONT
GetNumberOfBits()159   vtkm::Id GetNumberOfBits() const noexcept { return this->NumberOfBits; }
160 
161   /// Returns how many words of type @a WordTypePreferred exist in the dataset.
162   /// Note that this is rounded up and may contain partial words. See
163   /// also GetFinalWordMask to handle the trailing partial word.
164   template <typename WordType = WordTypePreferred>
GetNumberOfWords()165   VTKM_EXEC_CONT vtkm::Id GetNumberOfWords() const noexcept
166   {
167     VTKM_STATIC_ASSERT(IsValidWordType<WordType>::value);
168     static constexpr vtkm::Id WordSize = static_cast<vtkm::Id>(sizeof(WordType));
169     static constexpr vtkm::Id WordBits = WordSize * CHAR_BIT;
170     return (this->NumberOfBits + WordBits - 1) / WordBits;
171   }
172 
173   /// Return a mask in which the valid bits in the final word (of type @a
174   /// WordType) are set to 1.
175   template <typename WordType = WordTypePreferred>
GetFinalWordMask()176   VTKM_EXEC_CONT WordType GetFinalWordMask() const noexcept
177   {
178     if (this->NumberOfBits == 0)
179     {
180       return WordType{ 0 };
181     }
182 
183     static constexpr vtkm::Int32 BitsPerWord =
184       static_cast<vtkm::Int32>(sizeof(WordType) * CHAR_BIT);
185 
186     const auto maxBit = this->NumberOfBits - 1;
187     const auto coord = this->GetBitCoordinateFromIndex<WordType>(maxBit);
188     const vtkm::Int32 shift = BitsPerWord - coord.BitOffset - 1;
189     return (~WordType{ 0 }) >> shift;
190   }
191 
192   /// Given a bit index, compute a @a BitCoordinate that identifies the
193   /// corresponding word index and bit offset.
194   template <typename WordType = WordTypePreferred>
GetBitCoordinateFromIndex(vtkm::Id bitIdx)195   VTKM_EXEC_CONT static BitCoordinate GetBitCoordinateFromIndex(vtkm::Id bitIdx) noexcept
196   {
197     VTKM_STATIC_ASSERT(IsValidWordType<WordType>::value);
198     static constexpr vtkm::Id BitsPerWord = static_cast<vtkm::Id>(sizeof(WordType) * CHAR_BIT);
199     return { static_cast<vtkm::Id>(bitIdx / BitsPerWord),
200              static_cast<vtkm::Int32>(bitIdx % BitsPerWord) };
201   }
202 
203   /// Set the bit at @a bitIdx to @a val. This method is not thread-safe --
204   /// threads modifying bits nearby may interfere with this operation.
205   /// Additionally, this should not be used for synchronization, as there are
206   /// no memory ordering requirements. See SetBitAtomic for those usecases.
207   VTKM_EXEC_CONT
SetBit(vtkm::Id bitIdx,bool val)208   void SetBit(vtkm::Id bitIdx, bool val) const noexcept
209   {
210     VTKM_STATIC_ASSERT_MSG(!IsConst, "'Set' method called on const BitField portal.");
211     using WordType = WordTypePreferred;
212     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
213     const auto mask = WordType(1) << coord.BitOffset;
214     WordType* wordAddr = this->GetWordAddress<WordType>(coord.WordIndex);
215     if (val)
216     {
217       *wordAddr |= mask;
218     }
219     else
220     {
221       *wordAddr &= ~mask;
222     }
223   }
224 
225   /// Set the bit at @a bitIdx to @a val using atomic operations. This method
226   /// is thread-safe and guarantees, at minimum, "release" memory ordering.
227   VTKM_EXEC_CONT
SetBitAtomic(vtkm::Id bitIdx,bool val)228   void SetBitAtomic(vtkm::Id bitIdx, bool val) const
229   {
230     VTKM_STATIC_ASSERT_MSG(!IsConst, "'Set' method called on const BitField portal.");
231     using WordType = WordTypePreferred;
232     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
233     const auto mask = WordType(1) << coord.BitOffset;
234     if (val)
235     {
236       this->OrWordAtomic(coord.WordIndex, mask);
237     }
238     else
239     {
240       this->AndWordAtomic(coord.WordIndex, ~mask);
241     }
242   }
243 
244   /// Return whether or not the bit at @a bitIdx is set. Note that this uses
245   /// non-atomic loads and thus should not be used for synchronization.
246   VTKM_EXEC_CONT
GetBit(vtkm::Id bitIdx)247   bool GetBit(vtkm::Id bitIdx) const noexcept
248   {
249     using WordType = WordTypePreferred;
250     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
251     const auto word = this->GetWord<WordType>(coord.WordIndex);
252     const auto mask = WordType(1) << coord.BitOffset;
253     return (word & mask) != WordType(0);
254   }
255 
256   /// Return whether or not the bit at @a bitIdx is set using atomic loads.
257   /// This method is thread safe and guarantees, at minimum, "acquire" memory
258   /// ordering.
259   VTKM_EXEC_CONT
GetBitAtomic(vtkm::Id bitIdx)260   bool GetBitAtomic(vtkm::Id bitIdx) const
261   {
262     using WordType = WordTypePreferred;
263     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
264     const auto word = this->GetWordAtomic<WordType>(coord.WordIndex);
265     const auto mask = WordType(1) << coord.BitOffset;
266     return (word & mask) != WordType(0);
267   }
268 
269   /// Set the word (of type @a WordType) at @a wordIdx to @a word using
270   /// non-atomic operations.
271   template <typename WordType = WordTypePreferred>
SetWord(vtkm::Id wordIdx,WordType word)272   VTKM_EXEC_CONT void SetWord(vtkm::Id wordIdx, WordType word) const noexcept
273   {
274     VTKM_STATIC_ASSERT_MSG(!IsConst, "'Set' method called on const BitField portal.");
275     *this->GetWordAddress<WordType>(wordIdx) = word;
276   }
277 
278   /// Set the word (of type @a WordType) at @a wordIdx to @a word using atomic
279   /// operations. The store guarantees, at minimum, "release" memory ordering.
280   template <typename WordType = WordTypePreferred>
SetWordAtomic(vtkm::Id wordIdx,WordType word)281   VTKM_EXEC_CONT void SetWordAtomic(vtkm::Id wordIdx, WordType word) const
282   {
283     VTKM_STATIC_ASSERT_MSG(!IsConst, "'Set' method called on const BitField portal.");
284     VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value,
285                            "Requested WordType does not support atomic"
286                            " operations on target execution platform.");
287     vtkm::AtomicStore(this->GetWordAddress<WordType>(wordIdx), word);
288   }
289 
290   /// Get the word (of type @a WordType) at @a wordIdx using non-atomic
291   /// operations.
292   template <typename WordType = WordTypePreferred>
GetWord(vtkm::Id wordIdx)293   VTKM_EXEC_CONT WordType GetWord(vtkm::Id wordIdx) const noexcept
294   {
295     return *this->GetWordAddress<WordType>(wordIdx);
296   }
297 
298   /// Get the word (of type @a WordType) at @ wordIdx using an atomic read with,
299   /// at minimum, "acquire" memory ordering.
300   template <typename WordType = WordTypePreferred>
GetWordAtomic(vtkm::Id wordIdx)301   VTKM_EXEC_CONT WordType GetWordAtomic(vtkm::Id wordIdx) const
302   {
303     VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value,
304                            "Requested WordType does not support atomic"
305                            " operations on target execution platform.");
306     return vtkm::AtomicLoad(this->GetWordAddress<WordType>(wordIdx));
307   }
308 
309   /// Toggle the bit at @a bitIdx, returning the original value. This method
310   /// uses atomic operations and a full memory barrier.
311   VTKM_EXEC_CONT
NotBitAtomic(vtkm::Id bitIdx)312   bool NotBitAtomic(vtkm::Id bitIdx) const
313   {
314     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
315     using WordType = WordTypePreferred;
316     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
317     const auto mask = WordType(1) << coord.BitOffset;
318     const auto oldWord = this->XorWordAtomic(coord.WordIndex, mask);
319     return (oldWord & mask) != WordType(0);
320   }
321 
322   /// Perform a bitwise "not" operation on the word at @a wordIdx, returning the
323   /// original word. This uses atomic operations and a full memory barrier.
324   template <typename WordType = WordTypePreferred>
NotWordAtomic(vtkm::Id wordIdx)325   VTKM_EXEC_CONT WordType NotWordAtomic(vtkm::Id wordIdx) const
326   {
327     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
328     VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value,
329                            "Requested WordType does not support atomic"
330                            " operations on target execution platform.");
331     WordType* addr = this->GetWordAddress<WordType>(wordIdx);
332     return vtkm::AtomicNot(addr);
333   }
334 
335   /// Perform an "and" operation between the bit at @a bitIdx and @a val,
336   /// returning the original value at @a bitIdx. This method uses atomic
337   /// operations and a full memory barrier.
338   VTKM_EXEC_CONT
AndBitAtomic(vtkm::Id bitIdx,bool val)339   bool AndBitAtomic(vtkm::Id bitIdx, bool val) const
340   {
341     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
342     using WordType = WordTypePreferred;
343     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
344     const auto bitmask = WordType(1) << coord.BitOffset;
345     // wordmask is all 1's, except for BitOffset which is (val ? 1 : 0)
346     const auto wordmask = val ? ~WordType(0) : ~bitmask;
347     const auto oldWord = this->AndWordAtomic(coord.WordIndex, wordmask);
348     return (oldWord & bitmask) != WordType(0);
349   }
350 
351   /// Perform an "and" operation between the word at @a wordIdx and @a wordMask,
352   /// returning the original word at @a wordIdx. This method uses atomic
353   /// operations and a full memory barrier.
354   template <typename WordType = WordTypePreferred>
AndWordAtomic(vtkm::Id wordIdx,WordType wordmask)355   VTKM_EXEC_CONT WordType AndWordAtomic(vtkm::Id wordIdx, WordType wordmask) const
356   {
357     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
358     VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value,
359                            "Requested WordType does not support atomic"
360                            " operations on target execution platform.");
361     WordType* addr = this->GetWordAddress<WordType>(wordIdx);
362     return vtkm::AtomicAnd(addr, wordmask);
363   }
364 
365   /// Perform an "of" operation between the bit at @a bitIdx and @a val,
366   /// returning the original value at @a bitIdx. This method uses atomic
367   /// operations and a full memory barrier.
368   VTKM_EXEC_CONT
OrBitAtomic(vtkm::Id bitIdx,bool val)369   bool OrBitAtomic(vtkm::Id bitIdx, bool val) const
370   {
371     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
372     using WordType = WordTypePreferred;
373     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
374     const auto bitmask = WordType(1) << coord.BitOffset;
375     // wordmask is all 0's, except for BitOffset which is (val ? 1 : 0)
376     const auto wordmask = val ? bitmask : WordType(0);
377     const auto oldWord = this->OrWordAtomic(coord.WordIndex, wordmask);
378     return (oldWord & bitmask) != WordType(0);
379   }
380 
381   /// Perform an "or" operation between the word at @a wordIdx and @a wordMask,
382   /// returning the original word at @a wordIdx. This method uses atomic
383   /// operations and a full memory barrier.
384   template <typename WordType = WordTypePreferred>
OrWordAtomic(vtkm::Id wordIdx,WordType wordmask)385   VTKM_EXEC_CONT WordType OrWordAtomic(vtkm::Id wordIdx, WordType wordmask) const
386   {
387     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
388     VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value,
389                            "Requested WordType does not support atomic"
390                            " operations on target execution platform.");
391     WordType* addr = this->GetWordAddress<WordType>(wordIdx);
392     return vtkm::AtomicOr(addr, wordmask);
393   }
394 
395   /// Perform an "xor" operation between the bit at @a bitIdx and @a val,
396   /// returning the original value at @a bitIdx. This method uses atomic
397   /// operations and a full memory barrier.
398   VTKM_EXEC_CONT
XorBitAtomic(vtkm::Id bitIdx,bool val)399   bool XorBitAtomic(vtkm::Id bitIdx, bool val) const
400   {
401     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
402     using WordType = WordTypePreferred;
403     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
404     const auto bitmask = WordType(1) << coord.BitOffset;
405     // wordmask is all 0's, except for BitOffset which is (val ? 1 : 0)
406     const auto wordmask = val ? bitmask : WordType(0);
407     const auto oldWord = this->XorWordAtomic(coord.WordIndex, wordmask);
408     return (oldWord & bitmask) != WordType(0);
409   }
410 
411   /// Perform an "xor" operation between the word at @a wordIdx and @a wordMask,
412   /// returning the original word at @a wordIdx. This method uses atomic
413   /// operations and a full memory barrier.
414   template <typename WordType = WordTypePreferred>
XorWordAtomic(vtkm::Id wordIdx,WordType wordmask)415   VTKM_EXEC_CONT WordType XorWordAtomic(vtkm::Id wordIdx, WordType wordmask) const
416   {
417     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
418     VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value,
419                            "Requested WordType does not support atomic"
420                            " operations on target execution platform.");
421     WordType* addr = this->GetWordAddress<WordType>(wordIdx);
422     return vtkm::AtomicXor(addr, wordmask);
423   }
424 
425   /// Perform an atomic compare-and-swap operation on the bit at @a bitIdx.
426   /// If the value in memory is equal to @a oldBit, it is replaced with
427   /// the value of @a newBit and true is returned. If the value in memory is
428   /// not equal to @oldBit, @oldBit is changed to that value and false is
429   /// returned. This method implements a full memory barrier around the atomic
430   /// operation.
431   VTKM_EXEC_CONT
CompareExchangeBitAtomic(vtkm::Id bitIdx,bool * oldBit,bool newBit)432   bool CompareExchangeBitAtomic(vtkm::Id bitIdx, bool* oldBit, bool newBit) const
433   {
434     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
435     using WordType = WordTypePreferred;
436     const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx);
437     const auto bitmask = WordType(1) << coord.BitOffset;
438 
439     WordType oldWord = this->GetWord<WordType>(coord.WordIndex);
440     do
441     {
442       bool actualBit = (oldWord & bitmask) != WordType(0);
443       if (actualBit != *oldBit)
444       { // The bit-of-interest does not match what we expected.
445         *oldBit = actualBit;
446         return false;
447       }
448       else if (actualBit == newBit)
449       { // The bit hasn't changed, but also already matches newVal. We're done.
450         return true;
451       }
452 
453       // Attempt to update the word with a compare-exchange in the loop condition.
454       // If the old word changed since last queried, oldWord will get updated and
455       // the loop will continue until it succeeds.
456     } while (!this->CompareExchangeWordAtomic(coord.WordIndex, &oldWord, oldWord ^ bitmask));
457 
458     return true;
459   }
460 
461   VTKM_DEPRECATED(1.6, "Use CompareExchangeBitAtomic. (Note the changed interface.)")
CompareAndSwapBitAtomic(vtkm::Id bitIdx,bool newBit,bool expectedBit)462   VTKM_EXEC_CONT bool CompareAndSwapBitAtomic(vtkm::Id bitIdx, bool newBit, bool expectedBit) const
463   {
464     this->CompareExchangeBitAtomic(bitIdx, &expectedBit, newBit);
465     return expectedBit;
466   }
467 
468   /// Perform an atomic compare-exchange operation on the word at @a wordIdx.
469   /// If the word in memory is equal to @a oldWord, it is replaced with
470   /// the value of @a newWord and true returned. If the word in memory is not
471   /// equal to @oldWord, @oldWord is set to the word in memory and false is
472   /// returned. This method implements a full memory barrier around the atomic
473   /// operation.
474   template <typename WordType = WordTypePreferred>
CompareExchangeWordAtomic(vtkm::Id wordIdx,WordType * oldWord,WordType newWord)475   VTKM_EXEC_CONT bool CompareExchangeWordAtomic(vtkm::Id wordIdx,
476                                                 WordType* oldWord,
477                                                 WordType newWord) const
478   {
479     VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal.");
480     VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value,
481                            "Requested WordType does not support atomic"
482                            " operations on target execution platform.");
483     WordType* addr = this->GetWordAddress<WordType>(wordIdx);
484     return vtkm::AtomicCompareExchange(addr, oldWord, newWord);
485   }
486 
487   template <typename WordType = WordTypePreferred>
488   VTKM_DEPRECATED(1.6, "Use CompareExchangeWordAtomic. (Note the changed interface.)")
489   VTKM_EXEC_CONT WordType
CompareAndSwapWordAtomic(vtkm::Id wordIdx,WordType newWord,WordType expected)490     CompareAndSwapWordAtomic(vtkm::Id wordIdx, WordType newWord, WordType expected) const
491   {
492     this->CompareExchangeWordAtomic(wordIdx, &expected, newWord);
493     return expected;
494   }
495 
496 private:
497   template <typename WordType>
GetWordAddress(vtkm::Id wordId)498   VTKM_EXEC_CONT MaybeConstPointer<WordType> GetWordAddress(vtkm::Id wordId) const noexcept
499   {
500     VTKM_STATIC_ASSERT(IsValidWordType<WordType>::value);
501     return reinterpret_cast<MaybeConstPointer<WordType>>(this->Data) + wordId;
502   }
503 
504   BufferType Data{ nullptr };
505   vtkm::Id NumberOfBits{ 0 };
506 };
507 
508 using BitPortal = BitPortalBase<false>;
509 
510 using BitPortalConst = BitPortalBase<true>;
511 
512 template <typename WordType, typename Device>
513 struct IsValidWordTypeDeprecated
514 {
515   using type VTKM_DEPRECATED(
516     1.6,
517     "BitField::IsValidWordTypeAtomic no longer takes a second Device parameter.") =
518     detail::BitFieldTraits::IsValidWordTypeAtomic<WordType>;
519 };
520 
521 template <typename WordType>
522 struct IsValidWordTypeDeprecated<WordType, void>
523 {
524   using type = detail::BitFieldTraits::IsValidWordTypeAtomic<WordType>;
525 };
526 
527 } // end namespace detail
528 
529 class VTKM_CONT_EXPORT BitField
530 {
531   static constexpr vtkm::Id BlockSize = detail::BitFieldTraits::BlockSize;
532 
533 public:
534   /// The type array handle used to store the bit data internally:
535   using ArrayHandleType VTKM_DEPRECATED(1.6, "BitField now uses a Buffer to store data.") =
536     ArrayHandle<vtkm::WordTypeDefault, StorageTagBasic>;
537 
538   /// The BitPortal used in the control environment.
539   using WritePortalType = detail::BitPortal;
540 
541   /// A read-only BitPortal used in the control environment.
542   using ReadPortalType = detail::BitPortalConst;
543 
544   using PortalControl VTKM_DEPRECATED(1.6,
545                                       "Use BitField::WritePortalType instead.") = detail::BitPortal;
546   using PortalConstControl VTKM_DEPRECATED(1.6, "Use ArrayBitField::ReadPortalType instead.") =
547     detail::BitPortalConst;
548 
549   template <typename Device>
550   struct ExecutionTypes
551   {
552     /// The preferred word type used by the specified device.
553     using WordTypePreferred = vtkm::AtomicTypePreferred;
554 
555     /// A BitPortal that is usable on the specified device.
556     using Portal = detail::BitPortal;
557 
558     /// A read-only BitPortal that is usable on the specified device.
559     using PortalConst = detail::BitPortalConst;
560   };
561 
562   /// Check whether a word type is valid for non-atomic operations.
563   template <typename WordType>
564   using IsValidWordType = detail::BitFieldTraits::IsValidWordType<WordType>;
565 
566   /// Check whether a word type is valid for atomic operations.
567   template <typename WordType, typename Device = void>
568   using IsValidWordTypeAtomic = detail::BitFieldTraits::IsValidWordTypeAtomic<WordType>;
569 
570   /// Check whether a word type is valid for atomic operations from the control
571   /// environment.
572   template <typename WordType>
573   using IsValidWordTypeAtomicControl VTKM_DEPRECATED(1.6, "Use IsValidWordTypeAtomic instead.") =
574     detail::BitFieldTraits::IsValidWordTypeAtomic<WordType>;
575 
576   VTKM_CONT BitField();
577   VTKM_CONT BitField(const BitField&) = default;
578   VTKM_CONT BitField(BitField&&) noexcept = default;
579   VTKM_CONT ~BitField() = default;
580   VTKM_CONT BitField& operator=(const BitField&) = default;
581   VTKM_CONT BitField& operator=(BitField&&) noexcept = default;
582 
583   VTKM_CONT
584   bool operator==(const BitField& rhs) const { return this->Buffer == rhs.Buffer; }
585 
586   VTKM_CONT
587   bool operator!=(const BitField& rhs) const { return this->Buffer != rhs.Buffer; }
588 
589   /// Return the internal `Buffer` used to store the `BitField`.
590   VTKM_CONT vtkm::cont::internal::Buffer GetBuffer() const { return this->Buffer; }
591 
592   /// Return the internal ArrayHandle used to store the BitField.
593   VTKM_CONT VTKM_DEPRECATED(1.6, "BitField now uses a Buffer to store data.")
594     ArrayHandle<vtkm::WordTypeDefault, StorageTagBasic> GetData() const
595   {
596     return vtkm::cont::ArrayHandle<vtkm::WordTypeDefault, StorageTagBasic>(&this->Buffer);
597   }
598 
599   /// Return the number of bits stored by this BitField.
600   VTKM_CONT vtkm::Id GetNumberOfBits() const;
601 
602   /// Return the number of words (of @a WordType) stored in this bit fields.
603   ///
604   template <typename WordType>
605   VTKM_CONT vtkm::Id GetNumberOfWords() const
606   {
607     VTKM_STATIC_ASSERT(IsValidWordType<WordType>::value);
608     static constexpr vtkm::Id WordBits = static_cast<vtkm::Id>(sizeof(WordType) * CHAR_BIT);
609     return (this->GetNumberOfBits() + WordBits - 1) / WordBits;
610   }
611 
612   /// Allocate the requested number of bits.
613   VTKM_CONT void Allocate(vtkm::Id numberOfBits,
614                           vtkm::CopyFlag preserve,
615                           vtkm::cont::Token& token) const;
616 
617   /// Allocate the requested number of bits.
618   VTKM_CONT void Allocate(vtkm::Id numberOfBits,
619                           vtkm::CopyFlag preserve = vtkm::CopyFlag::Off) const
620   {
621     vtkm::cont::Token token;
622     this->Allocate(numberOfBits, preserve, token);
623   }
624 
625   /// Shrink the bit field to the requested number of bits.
626   VTKM_CONT VTKM_DEPRECATED(1.6,
627                             "Use Allocate with preserve = On.") void Shrink(vtkm::Id numberOfBits)
628   {
629     this->Allocate(numberOfBits, vtkm::CopyFlag::On);
630   }
631 
632   /// Release all execution-side resources held by this BitField.
633   VTKM_CONT void ReleaseResourcesExecution();
634 
635   /// Release all resources held by this BitField and reset to empty.
636   VTKM_CONT void ReleaseResources();
637 
638   /// Force the control array to sync with the last-used device.
639   VTKM_CONT void SyncControlArray() const;
640 
641   /// Returns true if the `BitField`'s data is on the given device. If the data are on the given
642   /// device, then preparing for that device should not require any data movement.
643   ///
644   VTKM_CONT bool IsOnDevice(vtkm::cont::DeviceAdapterId device) const;
645 
646   /// Returns true if the `BitField`'s data is on the host. If the data are on the given
647   /// device, then calling `ReadPortal` or `WritePortal` should not require any data movement.
648   ///
649   VTKM_CONT bool IsOnHost() const
650   {
651     return this->IsOnDevice(vtkm::cont::DeviceAdapterTagUndefined{});
652   }
653 
654   VTKM_CONT VTKM_DEPRECATED(1.6, "Data can be on multiple devices. Use IsOnDevice.")
655     vtkm::cont::DeviceAdapterId GetDeviceAdapterId() const;
656 
657   /// \brief Get a portal to the data that is usable from the control environment.
658   ///
659   /// As long as this portal is in scope, no one else will be able to read or write the BitField.
660   VTKM_CONT WritePortalType WritePortal() const;
661 
662   /// \brief Get a read-only portal to the data that is usable from the control environment.
663   ///
664   /// As long as this portal is in scope, no one else will be able to write in the BitField.
665   VTKM_CONT ReadPortalType ReadPortal() const;
666 
667   VTKM_CONT
668   VTKM_DEPRECATED(1.6,
669                   "Use BitField::WritePortal() instead. "
670                   "Note that the returned portal will lock the array while it is in scope.")
671   detail::BitPortal GetPortalControl() { return this->WritePortal(); }
672 
673   /// Get a read-only portal to the data that is usable from the control
674   /// environment.
675   VTKM_CONT
676   VTKM_DEPRECATED(1.6,
677                   "Use BitField::ReadPortal() instead. "
678                   "Note that the returned portal will lock the array while it is in scope.")
679   detail::BitPortalConst GetPortalConstControl() const { return this->ReadPortal(); }
680 
681   /// Prepares this BitField to be used as an input to an operation in the
682   /// execution environment. If necessary, copies data to the execution
683   /// environment. Can throw an exception if this BitField does not yet contain
684   /// any data. Returns a portal that can be used in code running in the
685   /// execution environment.
686   VTKM_CONT ReadPortalType PrepareForInput(vtkm::cont::DeviceAdapterId device,
687                                            vtkm::cont::Token& token) const;
688 
689   template <typename DeviceAdapterTag>
690   VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForInput now requires a vtkm::cont::Token object.")
691     typename ExecutionTypes<DeviceAdapterTag>::PortalConst
692     PrepareForInput(DeviceAdapterTag device) const
693   {
694     vtkm::cont::Token token;
695     return this->PrepareForInput(device, token);
696   }
697 
698   /// Prepares (allocates) this BitField to be used as an output from an
699   /// operation in the execution environment. The internal state of this class
700   /// is set to have valid data in the execution BitField with the assumption
701   /// that the array will be filled soon (i.e. before any other methods of this
702   /// object are called). Returns a portal that can be used in code running in
703   /// the execution environment.
704   VTKM_CONT WritePortalType PrepareForOutput(vtkm::Id numBits,
705                                              vtkm::cont::DeviceAdapterId device,
706                                              vtkm::cont::Token& token) const;
707 
708   template <typename DeviceAdapterTag>
709   VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForOutput now requires a vtkm::cont::Token object.")
710     typename ExecutionTypes<DeviceAdapterTag>::Portal
711     PrepareForOutput(vtkm::Id numBits, DeviceAdapterTag device) const
712   {
713     vtkm::cont::Token token;
714     return this->PrepareForOutput(numBits, device, token);
715   }
716 
717   /// Prepares this BitField to be used in an in-place operation (both as input
718   /// and output) in the execution environment. If necessary, copies data to
719   /// the execution environment. Can throw an exception if this BitField does
720   /// not yet contain any data. Returns a portal that can be used in code
721   /// running in the execution environment.
722   VTKM_CONT WritePortalType PrepareForInPlace(vtkm::cont::DeviceAdapterId device,
723                                               vtkm::cont::Token& token) const;
724 
725   template <typename DeviceAdapterTag>
726   VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForInPlace now requires a vtkm::cont::Token object.")
727     typename ExecutionTypes<DeviceAdapterTag>::Portal
728     PrepareForInPlace(DeviceAdapterTag device) const
729   {
730     vtkm::cont::Token token;
731     return this->PrepareForInPlace(device, token);
732   }
733 
734 private:
735   mutable vtkm::cont::internal::Buffer Buffer;
736 };
737 }
738 } // end namespace vtkm::cont
739 
740 #endif // vtk_m_cont_BitField_h
741