mirror of
https://github.com/facebook/sapling.git
synced 2024-10-05 14:28:17 +03:00
inodes: Use ImmediateFuture in most part of checkout
Summary: The checkout code is a heavy user of `QueuedImmediateExecutor` due to relying on `folly::Future`, the immediate executor is used to convert `ImmediateFuture` onto `folly::Future`. Unfortunately, this transformation is unsafe and leads to deadlocks. The full migration of checkout to `ImmediateFuture` is done in the next diff, and as a result still relies on `QueuedImmediateExecutor` in `EdenMount`. As part of this diff, the CheckoutAction is greatly simplified, from relying on refcounts and `Promise`, the code now merely makes use of future chaining, thus making it easier to read. Reviewed By: genevievehelsel Differential Revision: D50347461 fbshipit-source-id: 8159f523f6fbe4414b99ecb924ba0446f7edeb6e
This commit is contained in:
parent
f713eef690
commit
048eec83e8
@ -63,83 +63,34 @@ PathComponentPiece CheckoutAction::getEntryName() const {
|
||||
: newScmEntry_.value().first;
|
||||
}
|
||||
|
||||
class CheckoutAction::LoadingRefcount {
|
||||
public:
|
||||
explicit LoadingRefcount(CheckoutAction* action) : action_(action) {
|
||||
action_->numLoadsPending_.fetch_add(1);
|
||||
}
|
||||
LoadingRefcount(LoadingRefcount&& other) noexcept : action_(other.action_) {
|
||||
other.action_ = nullptr;
|
||||
}
|
||||
LoadingRefcount& operator=(LoadingRefcount&& other) noexcept {
|
||||
decref();
|
||||
action_ = other.action_;
|
||||
other.action_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
~LoadingRefcount() {
|
||||
decref();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement the arrow operator, so that LoadingRefcount can be used like a
|
||||
* pointer. This allows users to easily call through it into the underlying
|
||||
* CheckoutAction methods.
|
||||
*/
|
||||
CheckoutAction* operator->() const {
|
||||
return action_;
|
||||
}
|
||||
|
||||
private:
|
||||
void decref() {
|
||||
if (action_) {
|
||||
auto oldCount = action_->numLoadsPending_.fetch_sub(1);
|
||||
if (oldCount == 1) {
|
||||
// We were the last load to complete. We can perform the action now.
|
||||
action_->allLoadsComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckoutAction* action_;
|
||||
};
|
||||
|
||||
Future<InvalidationRequired> CheckoutAction::run(
|
||||
ImmediateFuture<InvalidationRequired> CheckoutAction::run(
|
||||
CheckoutContext* ctx,
|
||||
ObjectStore* store) {
|
||||
// Immediately create one LoadingRefcount, to ensure that our
|
||||
// numLoadsPending_ refcount does not drop to 0 until after we have started
|
||||
// all required load operations.
|
||||
//
|
||||
// Even if all loads complete immediately, allLoadsComplete() won't be called
|
||||
// until this LoadingRefcount is destroyed.
|
||||
LoadingRefcount refcount{this};
|
||||
|
||||
std::vector<ImmediateFuture<folly::Unit>> loadFutures;
|
||||
try {
|
||||
// Load the Blob or Tree for the old TreeEntry.
|
||||
if (oldScmEntry_.has_value()) {
|
||||
const auto& oldEntry = oldScmEntry_.value();
|
||||
if (oldEntry.second.isTree()) {
|
||||
store->getTree(oldEntry.second.getHash(), ctx->getFetchContext())
|
||||
.thenValue([rc = LoadingRefcount(this)](
|
||||
std::shared_ptr<const Tree> oldTree) {
|
||||
rc->setOldTree(std::move(oldTree));
|
||||
})
|
||||
.semi()
|
||||
.via(&folly::QueuedImmediateExecutor::instance())
|
||||
.thenError([rc = LoadingRefcount(this)](exception_wrapper&& ew) {
|
||||
rc->error("error getting old tree", std::move(ew));
|
||||
});
|
||||
loadFutures.emplace_back(
|
||||
store->getTree(oldEntry.second.getHash(), ctx->getFetchContext())
|
||||
.thenValue([self = shared_from_this()](
|
||||
std::shared_ptr<const Tree> oldTree) {
|
||||
self->setOldTree(std::move(oldTree));
|
||||
})
|
||||
.thenError([self = shared_from_this()](exception_wrapper&& ew) {
|
||||
self->error("error getting old tree", std::move(ew));
|
||||
}));
|
||||
} else {
|
||||
store->getBlobSha1(oldEntry.second.getHash(), ctx->getFetchContext())
|
||||
.thenValue([rc = LoadingRefcount(this)](Hash20 oldBlobSha1) {
|
||||
rc->setOldBlob(std::move(oldBlobSha1));
|
||||
})
|
||||
.semi()
|
||||
.via(&folly::QueuedImmediateExecutor::instance())
|
||||
.thenError([rc = LoadingRefcount(this)](exception_wrapper&& ew) {
|
||||
rc->error("error getting old blob Sha1", std::move(ew));
|
||||
});
|
||||
loadFutures.emplace_back(
|
||||
store
|
||||
->getBlobSha1(oldEntry.second.getHash(), ctx->getFetchContext())
|
||||
.thenValue([self = shared_from_this()](Hash20 oldBlobSha1) {
|
||||
self->setOldBlob(std::move(oldBlobSha1));
|
||||
})
|
||||
.thenError([self = shared_from_this()](exception_wrapper&& ew) {
|
||||
self->error("error getting old blob Sha1", std::move(ew));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,41 +98,61 @@ Future<InvalidationRequired> CheckoutAction::run(
|
||||
if (newScmEntry_.has_value()) {
|
||||
const auto& newEntry = newScmEntry_.value();
|
||||
if (newEntry.second.isTree()) {
|
||||
store->getTree(newEntry.second.getHash(), ctx->getFetchContext())
|
||||
.thenValue([rc = LoadingRefcount(this)](
|
||||
std::shared_ptr<const Tree> newTree) {
|
||||
rc->setNewTree(std::move(newTree));
|
||||
})
|
||||
.semi()
|
||||
.via(&folly::QueuedImmediateExecutor::instance())
|
||||
.thenError([rc = LoadingRefcount(this)](exception_wrapper&& ew) {
|
||||
rc->error("error getting new tree", std::move(ew));
|
||||
});
|
||||
loadFutures.emplace_back(
|
||||
store->getTree(newEntry.second.getHash(), ctx->getFetchContext())
|
||||
.thenValue([self = shared_from_this()](
|
||||
std::shared_ptr<const Tree> newTree) {
|
||||
self->setNewTree(std::move(newTree));
|
||||
})
|
||||
.thenError([self = shared_from_this()](exception_wrapper&& ew) {
|
||||
self->error("error getting new tree", std::move(ew));
|
||||
}));
|
||||
} else {
|
||||
// We don't actually compare the new blob to anything, so we don't need
|
||||
// to fetch it. This just marks that the new inode will be a file.
|
||||
LoadingRefcount(this)->setNewBlob();
|
||||
setNewBlob();
|
||||
}
|
||||
}
|
||||
|
||||
// If we were constructed with a Future<InodePtr>, wait for it.
|
||||
if (!inode_) {
|
||||
XCHECK(inodeFuture_.valid());
|
||||
std::move(inodeFuture_)
|
||||
.thenValue([rc = LoadingRefcount(this)](InodePtr inode) {
|
||||
rc->setInode(std::move(inode));
|
||||
})
|
||||
.thenError([rc = LoadingRefcount(this)](exception_wrapper&& ew) {
|
||||
rc->error("error getting inode", std::move(ew));
|
||||
});
|
||||
loadFutures.emplace_back(
|
||||
std::move(inodeFuture_)
|
||||
.thenValue([self = shared_from_this()](InodePtr inode) {
|
||||
self->setInode(std::move(inode));
|
||||
})
|
||||
.thenError([self = shared_from_this()](exception_wrapper&& ew) {
|
||||
self->error("error getting inode", std::move(ew));
|
||||
})
|
||||
.semi());
|
||||
}
|
||||
} catch (...) {
|
||||
auto ew = exception_wrapper{std::current_exception()};
|
||||
refcount->error(
|
||||
"error preparing to load data for checkout action", std::move(ew));
|
||||
error("error preparing to load data for checkout action", std::move(ew));
|
||||
}
|
||||
|
||||
return promise_.getFuture();
|
||||
return collectAll(std::move(loadFutures))
|
||||
.thenValue(
|
||||
[self = shared_from_this()](
|
||||
auto&&) -> ImmediateFuture<InvalidationRequired> {
|
||||
if (!self->errors_.empty()) {
|
||||
// If multiple errors occurred, we log them all, but only
|
||||
// propagate up the first one. If necessary we could change this
|
||||
// to create a single exception that contains all of the messages
|
||||
// concatenated together.
|
||||
XLOG(ERR) << "multiple errors while attempting to load data for "
|
||||
"checkout action:";
|
||||
for (const auto& ew : self->errors_) {
|
||||
XLOG(ERR) << "CheckoutAction error: "
|
||||
<< folly::exceptionStr(ew);
|
||||
}
|
||||
return makeImmediateFuture<InvalidationRequired>(
|
||||
self->errors_[0]);
|
||||
}
|
||||
|
||||
return self->doAction();
|
||||
});
|
||||
}
|
||||
|
||||
void CheckoutAction::setOldTree(std::shared_ptr<const Tree> tree) {
|
||||
@ -220,71 +191,17 @@ void CheckoutAction::error(
|
||||
errors_.push_back(std::move(ew));
|
||||
}
|
||||
|
||||
void CheckoutAction::allLoadsComplete() noexcept {
|
||||
if (!ensureDataReady()) {
|
||||
// ensureDataReady() will fulfilled promise_ with an exception
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
doAction().thenTry([this](folly::Try<InvalidationRequired>&& t) {
|
||||
this->promise_.setTry(std::move(t));
|
||||
});
|
||||
} catch (...) {
|
||||
auto ew = exception_wrapper{std::current_exception()};
|
||||
promise_.setException(std::move(ew));
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckoutAction::ensureDataReady() noexcept {
|
||||
if (!errors_.empty()) {
|
||||
// If multiple errors occurred, we log them all, but only propagate
|
||||
// up the first one. If necessary we could change this to create
|
||||
// a single exception that contains all of the messages concatenated
|
||||
// together.
|
||||
if (errors_.size() > 1) {
|
||||
XLOG(ERR) << "multiple errors while attempting to load data for "
|
||||
"checkout action:";
|
||||
for (const auto& ew : errors_) {
|
||||
XLOG(ERR) << "CheckoutAction error: " << folly::exceptionStr(ew);
|
||||
}
|
||||
}
|
||||
promise_.setException(errors_[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure we actually have all the data we need.
|
||||
// (Just in case something went wrong when wiring up the callbacks in such a
|
||||
// way that we also failed to call error().)
|
||||
if (oldScmEntry_.has_value() && (!oldTree_ && !oldBlobSha1_)) {
|
||||
promise_.setException(
|
||||
std::runtime_error("failed to load data for old TreeEntry"));
|
||||
return false;
|
||||
}
|
||||
if (newScmEntry_.has_value() && (!newTree_ && !newBlobMarker_)) {
|
||||
promise_.setException(
|
||||
std::runtime_error("failed to load data for new TreeEntry"));
|
||||
return false;
|
||||
}
|
||||
if (!inode_) {
|
||||
promise_.setException(std::runtime_error("failed to load affected inode"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<InvalidationRequired> CheckoutAction::doAction() {
|
||||
ImmediateFuture<InvalidationRequired> CheckoutAction::doAction() {
|
||||
// All the data is ready and we're ready to go!
|
||||
|
||||
// Check for conflicts first.
|
||||
return hasConflict().thenValue(
|
||||
[this](
|
||||
bool conflictWasAddedToCtx) -> folly::Future<InvalidationRequired> {
|
||||
[self = shared_from_this()](
|
||||
bool conflictWasAddedToCtx) -> ImmediateFuture<InvalidationRequired> {
|
||||
// Note that even if we know we are not going to apply the changes, we
|
||||
// must still run hasConflict() first because we rely on its
|
||||
// side-effects.
|
||||
if (conflictWasAddedToCtx && !ctx_->forceUpdate()) {
|
||||
if (conflictWasAddedToCtx && !self->ctx_->forceUpdate()) {
|
||||
// We only report conflicts for files, not directories. The only
|
||||
// possible conflict that can occur here if this inode is a TreeInode
|
||||
// is that the old source control state was for a file. There aren't
|
||||
@ -302,18 +219,18 @@ Future<InvalidationRequired> CheckoutAction::doAction() {
|
||||
// into a PathComponent owned either by oldScmEntry_ or newScmEntry_.
|
||||
// Therefore don't move these scm entries, to make sure we don't
|
||||
// invalidate the PathComponentPiece data.
|
||||
auto parent = inode_->getParent(ctx_->renameLock());
|
||||
auto parent = self->inode_->getParent(self->ctx_->renameLock());
|
||||
return parent->checkoutUpdateEntry(
|
||||
ctx_,
|
||||
getEntryName(),
|
||||
std::move(inode_),
|
||||
std::move(oldTree_),
|
||||
std::move(newTree_),
|
||||
newScmEntry_);
|
||||
self->ctx_,
|
||||
self->getEntryName(),
|
||||
std::move(self->inode_),
|
||||
std::move(self->oldTree_),
|
||||
std::move(self->newTree_),
|
||||
self->newScmEntry_);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> CheckoutAction::hasConflict() {
|
||||
ImmediateFuture<bool> CheckoutAction::hasConflict() {
|
||||
if (oldTree_) {
|
||||
auto treeInode = inode_.asTreePtrOrNull();
|
||||
if (!treeInode) {
|
||||
@ -346,7 +263,7 @@ Future<bool> CheckoutAction::hasConflict() {
|
||||
oldScmEntry_.value().second.getType(),
|
||||
ctx_->getWindowsSymlinksEnabled()),
|
||||
ctx_->getFetchContext())
|
||||
.thenValue([this](bool isSame) {
|
||||
.thenValue([self = shared_from_this()](bool isSame) {
|
||||
if (isSame) {
|
||||
// no conflict
|
||||
return false;
|
||||
@ -358,13 +275,12 @@ Future<bool> CheckoutAction::hasConflict() {
|
||||
// conflict.
|
||||
// - If the file does not exist in the new tree, then this is a
|
||||
// MODIFIED_REMOVED conflict.
|
||||
auto conflictType = newScmEntry_ ? ConflictType::MODIFIED_MODIFIED
|
||||
: ConflictType::MODIFIED_REMOVED;
|
||||
ctx_->addConflict(conflictType, inode_.get());
|
||||
auto conflictType = self->newScmEntry_
|
||||
? ConflictType::MODIFIED_MODIFIED
|
||||
: ConflictType::MODIFIED_REMOVED;
|
||||
self->ctx_->addConflict(conflictType, self->inode_.get());
|
||||
return true;
|
||||
})
|
||||
.semi()
|
||||
.via(&folly::QueuedImmediateExecutor::instance());
|
||||
});
|
||||
}
|
||||
|
||||
XDCHECK(!oldScmEntry_) << "Both oldTree_ and oldBlob_ are nullptr, "
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "eden/fs/fuse/Invalidation.h"
|
||||
#include "eden/fs/inodes/InodePtr.h"
|
||||
#include "eden/fs/model/Tree.h"
|
||||
#include "eden/fs/utils/ImmediateFuture.h"
|
||||
|
||||
namespace folly {
|
||||
class exception_wrapper;
|
||||
@ -41,7 +42,7 @@ class ObjectStore;
|
||||
* CheckoutAction objects are ever created for these cases, since these actions
|
||||
* can be taken immediately.
|
||||
*/
|
||||
class CheckoutAction {
|
||||
class CheckoutAction : public std::enable_shared_from_this<CheckoutAction> {
|
||||
public:
|
||||
/**
|
||||
* Create a CheckoutAction with an already loaded Inode object.
|
||||
@ -95,13 +96,11 @@ class CheckoutAction {
|
||||
* whether the caller is responsible for invalidating the directory's inode
|
||||
* cache in the kernel.
|
||||
*/
|
||||
FOLLY_NODISCARD folly::Future<InvalidationRequired> run(
|
||||
FOLLY_NODISCARD ImmediateFuture<InvalidationRequired> run(
|
||||
CheckoutContext* ctx,
|
||||
ObjectStore* store);
|
||||
|
||||
private:
|
||||
class LoadingRefcount;
|
||||
|
||||
enum InternalConstructor {
|
||||
INTERNAL,
|
||||
};
|
||||
@ -121,13 +120,13 @@ class CheckoutAction {
|
||||
|
||||
void allLoadsComplete() noexcept;
|
||||
bool ensureDataReady() noexcept;
|
||||
folly::Future<bool> hasConflict();
|
||||
ImmediateFuture<bool> hasConflict();
|
||||
|
||||
/**
|
||||
* Return whether the directory's contents have changed and the
|
||||
* inode's readdir cache must be flushed.
|
||||
*/
|
||||
FOLLY_NODISCARD folly::Future<InvalidationRequired> doAction();
|
||||
FOLLY_NODISCARD ImmediateFuture<InvalidationRequired> doAction();
|
||||
|
||||
/**
|
||||
* The context for the in-progress checkout operation.
|
||||
@ -186,11 +185,7 @@ class CheckoutAction {
|
||||
* The errors vector keeps track of any errors that occurred while trying to
|
||||
* load the data needed to perform the checkout action.
|
||||
*/
|
||||
// TODO: Add locking?
|
||||
std::vector<folly::exception_wrapper> errors_;
|
||||
|
||||
/**
|
||||
* The promise that we will fulfil when the CheckoutAction is complete.
|
||||
*/
|
||||
folly::Promise<InvalidationRequired> promise_;
|
||||
};
|
||||
} // namespace facebook::eden
|
||||
|
@ -1513,13 +1513,13 @@ folly::Future<CheckoutResult> EdenMount::checkout(
|
||||
|
||||
return serverState_->getFaultInjector()
|
||||
.checkAsync("inodeCheckout", getPath().view())
|
||||
.semi()
|
||||
.via(getServerThreadPool().get())
|
||||
.thenValue([ctx, treeResults = std::move(treeResults), rootInode](
|
||||
auto&&) mutable {
|
||||
auto& [fromTree, toTree] = treeResults;
|
||||
return rootInode->checkout(ctx.get(), fromTree.tree, toTree.tree);
|
||||
});
|
||||
})
|
||||
.semi()
|
||||
.via(&folly::QueuedImmediateExecutor::instance());
|
||||
})
|
||||
.thenValue([ctx, checkoutTimes, stopWatch, snapshotHash](auto&&) {
|
||||
checkoutTimes->didCheckout = stopWatch.elapsed();
|
||||
|
@ -3029,7 +3029,7 @@ ImmediateFuture<Unit> TreeInode::computeDiff(
|
||||
});
|
||||
}
|
||||
|
||||
Future<Unit> TreeInode::checkout(
|
||||
ImmediateFuture<Unit> TreeInode::checkout(
|
||||
CheckoutContext* ctx,
|
||||
std::shared_ptr<const Tree> fromTree,
|
||||
std::shared_ptr<const Tree> toTree) {
|
||||
@ -3038,8 +3038,8 @@ Future<Unit> TreeInode::checkout(
|
||||
<< " --> "
|
||||
<< (toTree ? toTree->getHash().toLogString() : "<none>");
|
||||
|
||||
vector<unique_ptr<CheckoutAction>> actions;
|
||||
vector<IncompleteInodeLoad> pendingLoads;
|
||||
std::vector<std::shared_ptr<CheckoutAction>> actions;
|
||||
std::vector<IncompleteInodeLoad> pendingLoads;
|
||||
|
||||
// This default to true on Windows to always make sure that the directory is
|
||||
// a placeholder and is safe to be dematerialized. On Windows, adding a
|
||||
@ -3062,21 +3062,19 @@ Future<Unit> TreeInode::checkout(
|
||||
}
|
||||
|
||||
// Now start all of the checkout actions
|
||||
vector<Future<InvalidationRequired>> actionFutures;
|
||||
std::vector<ImmediateFuture<InvalidationRequired>> actionFutures;
|
||||
actionFutures.reserve(actions.size());
|
||||
for (const auto& action : actions) {
|
||||
actionFutures.emplace_back(action->run(ctx, &getObjectStore()));
|
||||
}
|
||||
|
||||
ImmediateFuture<Unit> faultFuture =
|
||||
auto faultFuture =
|
||||
getMount()->getServerState()->getFaultInjector().checkAsync(
|
||||
"TreeInode::checkout", getLogPath(), ctx->isDryRun());
|
||||
folly::SemiFuture<vector<folly::Try<facebook::eden::InvalidationRequired>>>
|
||||
collectFuture = folly::collectAll(actionFutures);
|
||||
auto collectFuture = collectAll(std::move(actionFutures));
|
||||
|
||||
// Wait for all of the actions, and record any errors.
|
||||
return std::move(faultFuture)
|
||||
.semi()
|
||||
.toUnsafeFuture()
|
||||
.thenValue([collectFuture = std::move(collectFuture)](auto&&) mutable {
|
||||
return std::move(collectFuture);
|
||||
})
|
||||
@ -3086,7 +3084,8 @@ Future<Unit> TreeInode::checkout(
|
||||
toTree = std::move(toTree),
|
||||
actions = std::move(actions),
|
||||
shouldInvalidateDirectory](
|
||||
vector<folly::Try<InvalidationRequired>> actionResults) mutable {
|
||||
vector<folly::Try<InvalidationRequired>> actionResults) mutable
|
||||
-> ImmediateFuture<folly::Unit> {
|
||||
// Record any errors that occurred
|
||||
size_t numErrors = 0;
|
||||
for (size_t n = 0; n < actionResults.size(); ++n) {
|
||||
@ -3127,25 +3126,16 @@ Future<Unit> TreeInode::checkout(
|
||||
});
|
||||
}
|
||||
|
||||
auto fut = std::move(invalidation)
|
||||
.thenValue([self,
|
||||
ctx,
|
||||
toTree = std::move(toTree),
|
||||
numErrors](auto&&) {
|
||||
// Update our state in the overlay
|
||||
self->saveOverlayPostCheckout(ctx, toTree.get());
|
||||
return std::move(invalidation)
|
||||
.thenValue(
|
||||
[self, ctx, toTree = std::move(toTree), numErrors](auto&&) {
|
||||
// Update our state in the overlay
|
||||
self->saveOverlayPostCheckout(ctx, toTree.get());
|
||||
|
||||
XLOG(DBG4) << "checkout: finished update of "
|
||||
<< self->getLogPath() << ": "
|
||||
<< numErrors << " errors";
|
||||
});
|
||||
|
||||
if (fut.isReady()) {
|
||||
return folly::makeFuture(std::move(fut).getTry());
|
||||
} else {
|
||||
return std::move(fut).semi().via(
|
||||
self->getMount()->getServerThreadPool().get());
|
||||
}
|
||||
XLOG(DBG4) << "checkout: finished update of "
|
||||
<< self->getLogPath() << ": " << numErrors
|
||||
<< " errors";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -3206,7 +3196,7 @@ void TreeInode::computeCheckoutActions(
|
||||
CheckoutContext* ctx,
|
||||
const Tree* fromTree,
|
||||
const Tree* toTree,
|
||||
vector<unique_ptr<CheckoutAction>>& actions,
|
||||
vector<std::shared_ptr<CheckoutAction>>& actions,
|
||||
vector<IncompleteInodeLoad>& pendingLoads,
|
||||
bool& wasDirectoryListModified) {
|
||||
// Grab the contents_ lock for the duration of this function
|
||||
@ -3234,7 +3224,7 @@ void TreeInode::computeCheckoutActions(
|
||||
auto newIter = toTree ? toTree->cbegin() : emptyEntries.cbegin();
|
||||
auto newEnd = toTree ? toTree->cend() : emptyEntries.cend();
|
||||
while (true) {
|
||||
unique_ptr<CheckoutAction> action;
|
||||
std::shared_ptr<CheckoutAction> action;
|
||||
|
||||
if (oldIter == oldEnd) {
|
||||
if (newIter == newEnd) {
|
||||
@ -3304,7 +3294,7 @@ void TreeInode::computeCheckoutActions(
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<CheckoutAction> TreeInode::processCheckoutEntry(
|
||||
std::shared_ptr<CheckoutAction> TreeInode::processCheckoutEntry(
|
||||
CheckoutContext* ctx,
|
||||
TreeInodeState& state,
|
||||
const Tree::value_type* oldScmEntry,
|
||||
@ -3357,7 +3347,7 @@ unique_ptr<CheckoutAction> TreeInode::processCheckoutEntry(
|
||||
auto& entry = it->second;
|
||||
if (auto childPtr = entry.getInodePtr()) {
|
||||
// If the inode is already loaded, create a CheckoutAction to process it
|
||||
return make_unique<CheckoutAction>(
|
||||
return std::make_shared<CheckoutAction>(
|
||||
ctx, oldScmEntry, newScmEntry, std::move(childPtr));
|
||||
}
|
||||
|
||||
@ -3383,7 +3373,7 @@ unique_ptr<CheckoutAction> TreeInode::processCheckoutEntry(
|
||||
// CheckoutAction to process it once it is loaded.
|
||||
auto inodeFuture = loadChildLocked(
|
||||
contents, name, entry, pendingLoads, ctx->getFetchContext());
|
||||
return make_unique<CheckoutAction>(
|
||||
return std::make_shared<CheckoutAction>(
|
||||
ctx, oldScmEntry, newScmEntry, std::move(inodeFuture));
|
||||
} else {
|
||||
XLOG(DBG6) << "not loading child: inode=" << getNodeId()
|
||||
@ -3415,7 +3405,7 @@ unique_ptr<CheckoutAction> TreeInode::processCheckoutEntry(
|
||||
// for sure is to load the inode.
|
||||
auto inodeFuture = loadChildLocked(
|
||||
contents, name, entry, pendingLoads, ctx->getFetchContext());
|
||||
return make_unique<CheckoutAction>(
|
||||
return std::make_shared<CheckoutAction>(
|
||||
ctx, oldScmEntry, newScmEntry, std::move(inodeFuture));
|
||||
}
|
||||
case ObjectComparison::Identical:
|
||||
@ -3435,7 +3425,7 @@ unique_ptr<CheckoutAction> TreeInode::processCheckoutEntry(
|
||||
if (entry.isDirectory()) {
|
||||
auto inodeFuture = loadChildLocked(
|
||||
contents, name, entry, pendingLoads, ctx->getFetchContext());
|
||||
return make_unique<CheckoutAction>(
|
||||
return std::make_shared<CheckoutAction>(
|
||||
ctx, oldScmEntry, newScmEntry, std::move(inodeFuture));
|
||||
}
|
||||
|
||||
@ -3475,7 +3465,7 @@ unique_ptr<CheckoutAction> TreeInode::processCheckoutEntry(
|
||||
<< getNodeId() << " child=" << name;
|
||||
auto inodeFuture = loadChildLocked(
|
||||
contents, name, entry, pendingLoads, ctx->getFetchContext());
|
||||
return make_unique<CheckoutAction>(
|
||||
return std::make_shared<CheckoutAction>(
|
||||
ctx, oldScmEntry, newScmEntry, std::move(inodeFuture));
|
||||
}
|
||||
}
|
||||
@ -3520,7 +3510,7 @@ unique_ptr<CheckoutAction> TreeInode::processCheckoutEntry(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<CheckoutAction> TreeInode::processAbsentCheckoutEntry(
|
||||
std::shared_ptr<CheckoutAction> TreeInode::processAbsentCheckoutEntry(
|
||||
CheckoutContext* ctx,
|
||||
TreeInodeState& state,
|
||||
const Tree::value_type* oldScmEntry,
|
||||
@ -3602,7 +3592,7 @@ PathComponent getInodeName(CheckoutContext* ctx, const InodePtr& inode) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Future<InvalidationRequired> TreeInode::checkoutUpdateEntry(
|
||||
ImmediateFuture<InvalidationRequired> TreeInode::checkoutUpdateEntry(
|
||||
CheckoutContext* ctx,
|
||||
PathComponentPiece name,
|
||||
InodePtr inode,
|
||||
@ -3722,7 +3712,8 @@ Future<InvalidationRequired> TreeInode::checkoutUpdateEntry(
|
||||
parentInode = inodePtrFromThis(),
|
||||
treeInode,
|
||||
windowsSymlinksEnabled,
|
||||
newScmEntry](auto&&) mutable -> folly::Future<InvalidationRequired> {
|
||||
newScmEntry](
|
||||
auto&&) mutable -> ImmediateFuture<InvalidationRequired> {
|
||||
if (ctx->isDryRun()) {
|
||||
// If this is a dry run, simply report conflicts and don't update
|
||||
// or invalidate the inode.
|
||||
|
@ -13,9 +13,9 @@
|
||||
#include <optional>
|
||||
#include "eden/common/utils/FileOffset.h"
|
||||
#include "eden/fs/fuse/Invalidation.h"
|
||||
#include "eden/fs/inodes/CheckoutAction.h"
|
||||
#include "eden/fs/inodes/DirEntry.h"
|
||||
#include "eden/fs/inodes/InodeBase.h"
|
||||
#include "eden/fs/model/Tree.h"
|
||||
#include "eden/fs/utils/PathFuncs.h"
|
||||
|
||||
namespace facebook::eden {
|
||||
@ -350,7 +350,7 @@ class TreeInode final : public InodeBaseMetadata<DirContents> {
|
||||
* @return Returns a future that will be fulfilled once this tree and all of
|
||||
* its children have been updated.
|
||||
*/
|
||||
FOLLY_NODISCARD folly::Future<folly::Unit> checkout(
|
||||
FOLLY_NODISCARD ImmediateFuture<folly::Unit> checkout(
|
||||
CheckoutContext* ctx,
|
||||
std::shared_ptr<const Tree> fromTree,
|
||||
std::shared_ptr<const Tree> toTree);
|
||||
@ -489,7 +489,7 @@ class TreeInode final : public InodeBaseMetadata<DirContents> {
|
||||
* This entry will refer to a tree if and only if the newTree parameter
|
||||
* is non-null.
|
||||
*/
|
||||
FOLLY_NODISCARD folly::Future<InvalidationRequired> checkoutUpdateEntry(
|
||||
FOLLY_NODISCARD ImmediateFuture<InvalidationRequired> checkoutUpdateEntry(
|
||||
CheckoutContext* ctx,
|
||||
PathComponentPiece name,
|
||||
InodePtr inode,
|
||||
@ -778,7 +778,7 @@ class TreeInode final : public InodeBaseMetadata<DirContents> {
|
||||
CheckoutContext* ctx,
|
||||
const Tree* fromTree,
|
||||
const Tree* toTree,
|
||||
std::vector<std::unique_ptr<CheckoutAction>>& actions,
|
||||
std::vector<std::shared_ptr<CheckoutAction>>& actions,
|
||||
std::vector<IncompleteInodeLoad>& pendingLoads,
|
||||
bool& wasDirectoryListModified);
|
||||
|
||||
@ -787,9 +787,9 @@ class TreeInode final : public InodeBaseMetadata<DirContents> {
|
||||
* modified the directory contents, which implies the return value is nullptr.
|
||||
*
|
||||
* This function could return a std::variant of InvalidationRequired and
|
||||
* std::unique_ptr<CheckoutAction> instead of setting a boolean.
|
||||
* std::shared_ptr<CheckoutAction> instead of setting a boolean.
|
||||
*/
|
||||
std::unique_ptr<CheckoutAction> processCheckoutEntry(
|
||||
std::shared_ptr<CheckoutAction> processCheckoutEntry(
|
||||
CheckoutContext* ctx,
|
||||
TreeInodeState& contents,
|
||||
const Tree::value_type* oldScmEntry,
|
||||
@ -797,7 +797,7 @@ class TreeInode final : public InodeBaseMetadata<DirContents> {
|
||||
std::vector<IncompleteInodeLoad>& pendingLoads,
|
||||
bool& wasDirectoryListModified);
|
||||
|
||||
std::unique_ptr<CheckoutAction> processAbsentCheckoutEntry(
|
||||
std::shared_ptr<CheckoutAction> processAbsentCheckoutEntry(
|
||||
CheckoutContext* ctx,
|
||||
TreeInodeState& state,
|
||||
const Tree::value_type* oldScmEntry,
|
||||
|
Loading…
Reference in New Issue
Block a user