mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-26 02:48:59 -04:00
Use atomic open file reference counts
- Use relaxed increment under the map bucket lock when duplicating file handles. - Use acq_rel decrement when releasing and checking for final references. - Load moved ref_count with relaxed ordering during map insertion.
This commit is contained in:
parent
cb33f39a61
commit
2910bef934
3 changed files with 27 additions and 4 deletions
|
|
@ -367,10 +367,19 @@ _open(const fuse_req_ctx_t *ctx_,
|
||||||
|
|
||||||
// The ref increment keeps fuse_release.cpp from erasing and
|
// The ref increment keeps fuse_release.cpp from erasing and
|
||||||
// freeing the canonical entry while we duplicate its fd.
|
// freeing the canonical entry while we duplicate its fd.
|
||||||
|
//
|
||||||
|
// memory_order_relaxed is sufficient for the increment. The
|
||||||
|
// concurrent_flat_map bucket lock held inside visit() already
|
||||||
|
// provides the happens-before relationship with other mutators
|
||||||
|
// of the same key (the release() erase_if() callback and other
|
||||||
|
// open() visitors.) The lock's release/acquire semantics
|
||||||
|
// publish the ref_count update to subsequent observers of the
|
||||||
|
// same entry, so we only need atomicity from the fetch_add
|
||||||
|
// itself.
|
||||||
of.visit(ctx_->nodeid,
|
of.visit(ctx_->nodeid,
|
||||||
[&](auto &v_)
|
[&](auto &v_)
|
||||||
{
|
{
|
||||||
v_.second.ref_count++;
|
v_.second.ref_count.fetch_add(1,std::memory_order_relaxed);
|
||||||
fi = v_.second.fi;
|
fi = v_.second.fi;
|
||||||
backing_id = v_.second.backing_id;
|
backing_id = v_.second.backing_id;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -81,11 +81,21 @@ FUSE::release(cu64 nodeid_,
|
||||||
FileInfo *canonical_fi = nullptr;
|
FileInfo *canonical_fi = nullptr;
|
||||||
FileInfo *fh_fi_to_free = nullptr;
|
FileInfo *fh_fi_to_free = nullptr;
|
||||||
|
|
||||||
|
// memory_order_acq_rel on the decrement gives us the canonical
|
||||||
|
// refcount-publication pattern:
|
||||||
|
// - release: prior writes to the OpenFile by this thread are
|
||||||
|
// visible to whichever thread observes the final 0.
|
||||||
|
// - acquire: the thread that drops the last ref acquires every
|
||||||
|
// prior ref holder's writes before tearing down the entry.
|
||||||
|
// The concurrent_flat_map bucket lock already serializes concurrent
|
||||||
|
// visitors of this key so the acq_rel is good for any observer that
|
||||||
|
// might examine ref_count outside a visit().
|
||||||
of.erase_if(nodeid_,
|
of.erase_if(nodeid_,
|
||||||
[&](auto &v_)
|
[&](auto &v_)
|
||||||
{
|
{
|
||||||
v_.second.ref_count--;
|
const int prev =
|
||||||
if(v_.second.ref_count > 0)
|
v_.second.ref_count.fetch_sub(1,std::memory_order_acq_rel);
|
||||||
|
if(prev > 1)
|
||||||
{
|
{
|
||||||
if(fi_ != v_.second.fi)
|
if(fi_ != v_.second.fi)
|
||||||
fh_fi_to_free = fi_;
|
fh_fi_to_free = fi_;
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,12 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move constructor runs during initial insertion into the
|
||||||
|
// concurrent_flat_map before the new entry is reachable by the
|
||||||
|
// other threads. There is no concurrent observer of the moved
|
||||||
|
// value at this point so relaxed load is sufficient.
|
||||||
OpenFile(OpenFile &&o_) noexcept
|
OpenFile(OpenFile &&o_) noexcept
|
||||||
: ref_count(o_.ref_count.load()),
|
: ref_count(o_.ref_count.load(std::memory_order_relaxed)),
|
||||||
backing_id(o_.backing_id),
|
backing_id(o_.backing_id),
|
||||||
fi(o_.fi)
|
fi(o_.fi)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue