sapling/eden/fs/service/EdenServer.h

293 lines
8.7 KiB
C
Raw Normal View History

/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <folly/Executor.h>
#include <folly/File.h>
#include <folly/Range.h>
#include <folly/SocketAddress.h>
#include <folly/Synchronized.h>
#include <folly/ThreadLocal.h>
#include <folly/experimental/StringKeyedMap.h>
#include <folly/futures/SharedPromise.h>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include "eden/fs/fuse/EdenStats.h"
#include "eden/fs/utils/PathFuncs.h"
#include "folly/experimental/FunctionScheduler.h"
constexpr folly::StringPiece kPeriodicUnloadCounterKey{"PeriodicUnloadCounter"};
namespace apache {
namespace thrift {
class ThriftServer;
}
} // namespace apache
namespace folly {
class EventBase;
}
namespace facebook {
namespace eden {
class BackingStore;
class Dirstate;
class EdenMount;
class EdenServiceHandler;
class LocalStore;
class MountInfo;
/*
* EdenServer contains logic for running the Eden main loop.
*
* It performs locking to ensure only a single EdenServer instance is running
* for a particular location, then starts the thrift management server
* and the fuse session.
*/
class EdenServer {
public:
using MountList = std::vector<std::shared_ptr<EdenMount>>;
using DirstateMap = folly::StringKeyedMap<std::shared_ptr<Dirstate>>;
EdenServer(
AbsolutePathPiece edenDir,
AbsolutePathPiece etcEdenDir,
AbsolutePathPiece configPath,
AbsolutePathPiece rocksPath);
virtual ~EdenServer();
/**
* Run the EdenServer.
*/
void run();
/**
* Prepare to run the EdenServer.
*
* This acquires the lock on the eden directory, remounts configured mount
* points, and prepares the thrift server to run.
*
* After prepare returns the caller can call getServer()->serve() to
* run the thrift server main loop.
*/
void prepare();
/**
* Stops this server, which includes the underlying Thrift server.
*
* This may be called from any thread while a call to run() is outstanding,
* and will cause run() to return.
*/
void stop() const;
/**
* Mount and return an EdenMount.
*/
FOLLY_NODISCARD folly::Future<std::shared_ptr<EdenMount>> mount(
const MountInfo& info);
/**
* Unmount an EdenMount.
*/
FOLLY_NODISCARD folly::Future<folly::Unit> unmount(
folly::StringPiece mountPath);
/**
* Unmount all mount points maintained by this server, and wait for them to
* be completely unmounted.
*/
fix EdenServer::unmount() to fully wait for mount point cleanup Summary: This fixes EdenServer::unmount() to actually wait for all EdenMount cleanup to complete, and fixes unmountAll() to return a Future that correctly waits for all mount points to be cleaned up. Previously `unmount()` waited for the mount point to be unmounted from the kernel, but did not wait for EdenMount shutdown to complete. Previously EdenMount shutdown was not triggered until the last reference to the shared_ptr<EdenMount> was released. This often happened in the FUSE channel thread that triggered the mountFinished() call--it would still hold a reference to this pointer, and would not release it until after mountFinished() returns. As a result, when the main thread was shutting down, `main()` would call `unmountAll()`, and then return soon after it completed. Some FUSE channel threads may still be running at this point, still performing `EdenMount` shutdown while the main thread was exiting. This could result in crashes and deadlocks as shutdown tried to access objects already destroyed by the main thread. With this change `EdenMount::shutdown()` is triggered explicitly during `mountFinished()`, and `unmount()` will not complete until this finishes. The `EdenMount` object may still exist at this point, and could still be deleted by the FUSE channel thread, but the deletion now only requires freeing the memory and does not require accessing other data that may have been cleaned up by the main thread. We should still clean up the FUSE channel thread handling in the future, to make sure these threads are joined before the main thread exits. However, that cleanup can wait until a separate diff. Ideally I would like to move more of the mount and unmount logic from EdenServer and EdenServiceHandler and put that code in EdenMount instead. Reviewed By: bolinfest Differential Revision: D5541318 fbshipit-source-id: 470332478357a85c314bc40458373cb0f827f62b
2017-08-03 02:52:18 +03:00
FOLLY_NODISCARD folly::Future<folly::Unit> unmountAll();
const std::shared_ptr<EdenServiceHandler>& getHandler() const {
return handler_;
}
const std::shared_ptr<apache::thrift::ThriftServer>& getServer() const {
return server_;
}
AbsolutePath getSocketPath() const;
MountList getMountPoints() const;
/**
* Look up an EdenMount by the path where it is mounted.
*
* Throws an EdenError if no mount exists with the specified path.
*/
std::shared_ptr<EdenMount> getMount(folly::StringPiece mountPath) const;
/**
* Look up an EdenMount by the path where it is mounted.
*
* Returns nullptr if no mount exists with the specified path.
*/
std::shared_ptr<EdenMount> getMountOrNull(folly::StringPiece mountPath) const;
std::shared_ptr<LocalStore> getLocalStore() const {
return localStore_;
}
/**
* Look up the BackingStore object for the specified repository type+name.
*
* EdenServer maintains an internal cache of all known BackingStores,
* so that multiple mount points that use the same repository can
* share the same BackingStore object.
*
* If this is the first time this given (type, name) has been used, a new
* BackingStore object will be created and returned. Otherwise this will
* return the existing BackingStore that was previously created.
*/
std::shared_ptr<BackingStore> getBackingStore(
folly::StringPiece type,
folly::StringPiece name);
AbsolutePathPiece getEdenDir() {
return edenDir_;
}
fusell::ThreadLocalEdenStats* getStats() const {
return &edenStats_;
}
/**
* Flush all thread-local stats to the main ServiceData object.
*
* Thread-local counters are normally flushed to the main ServiceData once
* a second. flushStatsNow() can be used to flush thread-local counters on
* demand, in addition to the normal once-a-second flush.
*
* This is mainly useful for unit and integration tests that want to ensure
* they see up-to-date counter information without waiting for the normal
* flush interval.
*/
void flushStatsNow() const;
/**
* Get the main thread's EventBase.
*
* Callers can use this for scheduling work to be run in the main thread.
*/
folly::EventBase* getMainEventBase() const {
return mainEventBase_;
}
private:
// Struct to store EdenMount along with SharedPromise that is set
// during unmount to allow synchronizaiton between unmoutFinished
// and unmount functions.
struct EdenMountInfo {
std::shared_ptr<EdenMount> edenMount;
folly::SharedPromise<folly::Unit> unmountPromise;
explicit EdenMountInfo(const std::shared_ptr<EdenMount>& mount)
: edenMount(mount),
unmountPromise(folly::SharedPromise<folly::Unit>()) {}
};
using BackingStoreKey = std::pair<std::string, std::string>;
using BackingStoreMap =
std::unordered_map<BackingStoreKey, std::shared_ptr<BackingStore>>;
using MountMap = folly::StringKeyedMap<struct EdenMountInfo>;
class ThriftServerEventHandler;
// Forbidden copy constructor and assignment operator
EdenServer(EdenServer const&) = delete;
EdenServer& operator=(EdenServer const&) = delete;
// Schedules a timer to flush stats (and reschedule itself).
// We should have at most one of these pending at a time.
// Must be called only from the eventBase thread.
void scheduleFlushStats();
// Schedule a call to unloadInodes() to happen after timeout
// has expired.
// Must be called only from the eventBase thread.
void scheduleInodeUnload(std::chrono::milliseconds timeout);
// Perform unloading of inodes based on their last access time
// and then schedule another call to unloadInodes() to happen
// at the next appropriate interval. The unload attempt applies to
// all mounts.
void unloadInodes();
std::shared_ptr<BackingStore> createBackingStore(
folly::StringPiece type,
folly::StringPiece name);
void createThriftServer();
void acquireEdenLock();
void prepareThriftAddress();
// Called when a mount has been unmounted and has stopped.
void mountFinished(EdenMount* mountPoint);
// Called before destructing EdenServer
void shutdown();
// Add the mount point to mountPoints_.
// This also makes sure we don't have this path mounted already.
void addToMountPoints(std::shared_ptr<EdenMount> edenMount);
// Registers (or removes) stats callbacks for edenMount.
// These are here rather than in EdenMount because we need to
// hold an owning reference to the mount to safely sample stats.
void registerStats(std::shared_ptr<EdenMount> edenMount);
void unregisterStats(EdenMount* edenMount);
/*
* Member variables.
*
* Note that the declaration order below is important for initialization
* and cleanup order. lockFile_ is near the top so it will be released last.
* mountPoints_ are near the bottom, so they get destroyed before the
* backingStores_ and localStore_.
*/
AbsolutePath edenDir_;
AbsolutePath etcEdenDir_;
AbsolutePath configPath_;
AbsolutePath rocksPath_;
folly::File lockFile_;
std::shared_ptr<EdenServiceHandler> handler_;
std::shared_ptr<apache::thrift::ThriftServer> server_;
std::shared_ptr<ThriftServerEventHandler> serverEventHandler_;
std::shared_ptr<LocalStore> localStore_;
folly::Synchronized<BackingStoreMap> backingStores_;
folly::Synchronized<MountMap> mountPoints_;
mutable fusell::ThreadLocalEdenStats edenStats_;
/**
* The EventBase driving the main thread loop.
*
* This is used to drive the the thrift server and can also be used for
* scheduling other asynchronous operations.
*
* This is set when the EdenServer is started and is never updated after
* this, so we do not need synchronization when reading it.
*/
folly::EventBase* mainEventBase_;
/**
* A CPU executor for running arbitrary tasks.
* This is here because we need to keep it alive for the duration
* of the server lifetime.
*/
std::shared_ptr<folly::Executor> threadPool_;
};
} // namespace eden
} // namespace facebook