diff --git a/include/bitcoin/database/impl/memory/mmap_staging.ipp b/include/bitcoin/database/impl/memory/mmap_staging.ipp index 0b864d6e1..41bf2927d 100644 --- a/include/bitcoin/database/impl/memory/mmap_staging.ipp +++ b/include/bitcoin/database/impl/memory/mmap_staging.ipp @@ -1417,11 +1417,19 @@ bool CLASS::share_(size_t transferred) NOEXCEPT auto shared = false; if (loaded_.load() && !fault_.load() && (marks_.load() == transferred)) { + // The drain transfers below logical; the committed fill above it is + // content (a raised logical exposes it unwritten). + const auto logical = to_width(logical_.load()); const auto span = to_width(capacity_.load()); - shared = mmap_share(memory_map_[zero], span, opened_[zero], - zero) != fail; + const auto persisted = (span <= logical) || pwrite_all(opened_[zero], + std::next(memory_map_[zero], logical), span - logical, logical); - if (!shared) + shared = persisted && (mmap_share(memory_map_[zero], span, + opened_[zero], zero) != fail); + + if (!persisted) + set_first_code(error::fsync_failure); + else if (!shared) set_first_code(error::mmap_failure); #if !defined(WITHOUT_MADVISE) else if (!advise_(memory_map_[zero], span)) diff --git a/test/memory/mmap.cpp b/test/memory/mmap.cpp index f2a22dbdf..cdfe9807b 100644 --- a/test/memory/mmap.cpp +++ b/test/memory/mmap.cpp @@ -1532,6 +1532,32 @@ BOOST_AUTO_TEST_CASE(mmap__settle__sustained_writes__unsettled_without_loss) BOOST_REQUIRE(!instance.get_fault()); } +BOOST_AUTO_TEST_CASE(mmap__settle__filled_tail_above_logical__survives_settle) +{ + constexpr size_t cells = 512; + constexpr size_t tail = add1(cells); + constexpr auto fill = system::bit_all; + + const std::string file = TEST_PATH; + BOOST_REQUIRE(test::create(file)); + + map instance(file, { 1, 50 }); + BOOST_REQUIRE(!instance.open()); + BOOST_REQUIRE(!instance.load()); + BOOST_REQUIRE_NE(instance.allocate(cell_width), storage::eof); + BOOST_REQUIRE(instance.get_filled(cells * cell_width, cell_width, system::bit_all)); + BOOST_REQUIRE_GT(instance.capacity(), add1(tail) * cell_width); + + instance.current(true); + BOOST_REQUIRE(settled_within(instance, true, settle_wait)); + BOOST_REQUIRE(instance.get_filled(tail * cell_width, cell_width, system::bit_all)); + BOOST_REQUIRE_EQUAL(read_cells(instance, add1(tail)).back(), fill); + + BOOST_REQUIRE(!instance.unload()); + BOOST_REQUIRE(!instance.close()); + BOOST_REQUIRE(!instance.get_fault()); +} + #endif // MANAGE_STAGING BC_POP_WARNING()