Skip to content

Commit

Permalink
Clarify how to free the last released memory by QSBR threads
Browse files Browse the repository at this point in the history
  • Loading branch information
laurynas-biveinis committed Jan 7, 2025
1 parent 2fe8d40 commit 669aebf
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 8 deletions.
9 changes: 9 additions & 0 deletions examples/example_olc_art.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ int main() {
threads[1].join();
threads[2].join();

// Quitting threads may race with epoch changes by design, resulting in
// previous epoch orphaned requests not being executed until epoch
// changes one more time. If that does not happen, some memory might be
// held too long. Thus users are advised to pass through Q state in the
// last thread a couple more times at the end.
unodb::this_thread().qsbr_resume();
unodb::this_thread().quiescent();
unodb::this_thread().quiescent();

std::cerr << "Final tree memory use: " << tree.get_current_memory_use()
<< '\n';
std::cerr << "QSBR epochs changed: "
Expand Down
20 changes: 12 additions & 8 deletions qsbr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ class [[nodiscard]] qsbr_per_thread final {

std::size_t current_interval_total_dealloc_size{0};

bool paused{true};
bool paused{false};

void advance_last_seen_epoch(
bool single_thread_mode, qsbr_epoch new_seen_epoch,
Expand Down Expand Up @@ -755,6 +755,13 @@ class qsbr final {
const auto current_state = get_state();
qsbr_state::assert_invariants(current_state);
UNODB_DETAIL_ASSERT(qsbr_state::get_thread_count(current_state) <= 1);
// Quitting threads may race with epoch changes by design, resulting in
// previous epoch orphaned requests not being executed until epoch
// changes one more time. If that does not happen, this assert may fire at
// the process exit time. While it is possible to handle this silently, by
// executing the requests then, this may also result in some memory being
// held too long. Thus users are advised to pass through Q state in the
// last thread a couple more times at the end.
UNODB_DETAIL_ASSERT(previous_interval_orphaned_requests_empty());
UNODB_DETAIL_ASSERT(current_interval_orphaned_requests_empty());
detail::deallocation_request::assert_zero_instances();
Expand Down Expand Up @@ -853,10 +860,7 @@ static_assert(std::atomic<double>::is_always_lock_free);
UNODB_DETAIL_DISABLE_MSVC_WARNING(26455)
inline qsbr_per_thread::qsbr_per_thread()
: last_seen_quiescent_state_epoch{qsbr::instance().register_thread()},
last_seen_epoch{last_seen_quiescent_state_epoch} {
UNODB_DETAIL_ASSERT(paused);
paused = false;
}
last_seen_epoch{last_seen_quiescent_state_epoch} {}
UNODB_DETAIL_RESTORE_MSVC_WARNINGS()

inline void qsbr_per_thread::on_next_epoch_deallocate(
Expand Down Expand Up @@ -919,10 +923,10 @@ inline void qsbr_per_thread::advance_last_seen_epoch(
// NOLINTNEXTLINE(readability-simplify-boolean-expr)
UNODB_DETAIL_ASSERT(
new_seen_epoch == last_seen_epoch.advance()
// The current thread is 1) quitting; 2) not having seen
// the current epoch yet; 3) it quitting will cause an
// epoch advance
// The current thread is 1) quitting; 2) not having seen the current epoch
// yet; 3) it quitting will cause an epoch advance
|| (!single_thread_mode && new_seen_epoch == last_seen_epoch.advance(2)));

update_requests(single_thread_mode, new_seen_epoch,
std::move(new_current_requests));
}
Expand Down

0 comments on commit 669aebf

Please sign in to comment.