| 106 | |
| 107 | template <class T, class IndexType, IndexType CAPACITY> |
| 108 | std::vector<Reference<T>> WriteOnlySet<T, IndexType, CAPACITY>::copy() { |
| 109 | std::vector<Reference<T>> result; |
| 110 | for (int i = 0; i < CAPACITY; ++i) { |
| 111 | auto ptr = _set[i].load(); |
| 112 | if (ptr) { |
| 113 | ASSERT((ptr & LOCK) == 0); // if we lock something we need to immediately unlock after we're done copying |
| 114 | // We attempt lock so this won't get deleted. We will try this only once, if the other thread removed the |
| 115 | // object from the set between the previews lines and now, we just won't make it part of the result. |
| 116 | if (_set[i].compare_exchange_strong(ptr, ptr | LOCK)) { |
| 117 | T* entry = reinterpret_cast<T*>(ptr); |
| 118 | ptr |= LOCK; |
| 119 | entry->addref(); |
| 120 | // we try to unlock now. If this element was removed while we incremented the refcount, the element will |
| 121 | // end up in the freeList, so we will decrement later. |
| 122 | _set[i].compare_exchange_strong(ptr, ptr ^ LOCK); |
| 123 | result.push_back(Reference(entry)); |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | // after we're done we need to clean up all objects that contented on a lock. This won't be perfect (as some thread |
| 128 | // might not yet added the object to the free list), but whatever we don't get now we'll clean up in the next |
| 129 | // iteration |
| 130 | freeList.consume_all([](auto toClean) { toClean->delref(); }); |
| 131 | return result; |
| 132 | } |
| 133 | |
| 134 | template <class T, class IndexType> |
| 135 | WriteOnlyVariable<T, IndexType>::WriteOnlyVariable() : WriteOnlySet<T, IndexType, 1>() {} |