Merge pull request #3489 from sfc-gh-tclinkenbeard/dont-throw-deque-relocate

Add exception-safety to Deque::grow
This commit is contained in:
Evan Tschannen 2020-08-06 10:33:49 -07:00 committed by GitHub
commit 18e53caf6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 3 deletions

View File

@ -79,4 +79,41 @@ TEST_CASE("/flow/Deque/max_size") {
return Void();
}
struct RandomlyThrows {
int data = 0;
RandomlyThrows() = default;
explicit RandomlyThrows(int data) : data(data) {}
~RandomlyThrows() = default;
RandomlyThrows(const RandomlyThrows& other) : data(other.data) { randomlyThrow(); }
RandomlyThrows& operator=(const RandomlyThrows& other) {
data = other.data;
randomlyThrow();
return *this;
}
private:
void randomlyThrow() {
if (deterministicRandom()->random01() < 0.1) {
throw success();
}
}
};
TEST_CASE("/flow/Deque/grow_exception_safety") {
Deque<RandomlyThrows> q;
for (int i = 0; i < 100; ++i) {
loop {
try {
q.push_back(RandomlyThrows{ i });
break;
} catch (Error& e) {
}
}
}
for (int i = 0; i < 100; ++i) {
ASSERT(q[i].data == i);
}
return Void();
}
void forceLinkDequeTests() {}

View File

@ -168,10 +168,18 @@ private:
if (newSize > max_size()) throw std::bad_alloc();
//printf("Growing to %lld (%u-%u mask %u)\n", (long long)newSize, begin, end, mask);
T* newArr = (T*)aligned_alloc(std::max(__alignof(T), sizeof(void*)),
newSize * sizeof(T)); // SOMEDAY: FastAllocator, exception safety
newSize * sizeof(T)); // SOMEDAY: FastAllocator
ASSERT(newArr != nullptr);
for (int i = begin; i != end; i++) {
new (&newArr[i - begin]) T(std::move(arr[i&mask]));
try {
new (&newArr[i - begin]) T(std::move_if_noexcept(arr[i & mask]));
} catch (...) {
cleanup(newArr, i-begin);
throw;
}
}
for (int i = begin; i != end; i++) {
static_assert(std::is_nothrow_destructible_v<T>);
arr[i&mask].~T();
}
aligned_free(arr);
@ -181,7 +189,14 @@ private:
mask = uint32_t(newSize - 1);
}
void cleanup() {
static void cleanup(T *data, size_t size) noexcept {
for (int i = 0; i < size; ++i) {
data[i].~T();
}
aligned_free(data);
}
void cleanup() noexcept {
for (int i = begin; i != end; i++)
arr[i&mask].~T();
if(arr)