mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 16:31:02 +03:00
154d7309c9
Summary: This commit introduces a new process spawning class derived from the ChildProcess class in the watchman codebase. `SpawnedProcess` is similar to folly::Subprocess but is designed around the idea that we will use a system provided spawning API to start a process, rather than assuming the use of `fork`. `fork` is to be avoided because it can be expensive for processes with large address spaces and also because it interacts poorly with threads on macOS. In particular, we see the objC runtime terminating our process in some scenarios where fork and threads are mixed. There are some important differences from `folly::Subprocess` and that means that some assumptions and uses need to be altered slightly from their prior workings. For example, detaching a SpawnedProcess moves the responsibility of waiting on the child to a periodic task as there is no way to detach via posix_spawn without also using fork. On the plus side, this commit allows unifying spawning between posix and windows systems, which simplifies the code! Reviewed By: xavierd Differential Revision: D23287763 fbshipit-source-id: b662af1d7eaaa9ed445c42f6c5765ae9af975eea
143 lines
3.4 KiB
C++
143 lines
3.4 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This software may be used and distributed according to the terms of the
|
|
* GNU General Public License version 2.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <sys/types.h>
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <cstddef>
|
|
#include <memory>
|
|
|
|
#include <folly/File.h>
|
|
#include <folly/Portability.h>
|
|
#include <folly/io/async/AsyncTimeout.h>
|
|
#include <folly/io/async/EventHandler.h>
|
|
|
|
#include "eden/fs/utils/PathFuncs.h"
|
|
#include "eden/fs/utils/SpawnedProcess.h"
|
|
|
|
namespace folly {
|
|
class EventBase;
|
|
template <typename T>
|
|
class Future;
|
|
struct Unit;
|
|
} // namespace folly
|
|
|
|
namespace facebook {
|
|
namespace eden {
|
|
|
|
class EdenMonitor;
|
|
class LogFile;
|
|
|
|
/**
|
|
* EdenInstance represents a single instance of the edenfs process.
|
|
*
|
|
* It exists to manage the process and inform the EdenMonitor when the edenfs
|
|
* process exits.
|
|
*/
|
|
class EdenInstance {
|
|
public:
|
|
explicit EdenInstance(EdenMonitor* monitor);
|
|
virtual ~EdenInstance();
|
|
|
|
FOLLY_NODISCARD virtual folly::Future<folly::Unit> start() = 0;
|
|
virtual pid_t getPid() const = 0;
|
|
virtual void checkLiveness() = 0;
|
|
|
|
protected:
|
|
EdenInstance(EdenInstance const&) = delete;
|
|
EdenInstance& operator=(EdenInstance const&) = delete;
|
|
|
|
/*
|
|
* We store a raw pointer to the EdenMonitor.
|
|
* The EdenMonitor owns us, and will destroy us before it is destroyed.
|
|
*/
|
|
EdenMonitor* const monitor_;
|
|
};
|
|
|
|
/**
|
|
* ExistingEdenInstance tracks an edenfs process that was not started by this
|
|
* process.
|
|
*/
|
|
class ExistingEdenInstance : public EdenInstance, private folly::AsyncTimeout {
|
|
public:
|
|
ExistingEdenInstance(EdenMonitor* monitor, pid_t pid);
|
|
|
|
FOLLY_NODISCARD folly::Future<folly::Unit> start() override;
|
|
|
|
pid_t getPid() const override {
|
|
return pid_;
|
|
}
|
|
|
|
void checkLiveness() override;
|
|
|
|
private:
|
|
void timeoutExpired() noexcept override;
|
|
bool isAlive();
|
|
|
|
pid_t pid_{0};
|
|
std::chrono::milliseconds pollInterval_{60000};
|
|
};
|
|
|
|
/**
|
|
* SpawnedEdenInstance tracks an edenfs process that was spawned directly by
|
|
* this process.
|
|
*
|
|
* It reads stdout and stderr output from EdenFS and writes them to a log file,
|
|
* performing log rotation as necessary.
|
|
*/
|
|
class SpawnedEdenInstance : public EdenInstance,
|
|
private folly::EventHandler,
|
|
private folly::AsyncTimeout {
|
|
public:
|
|
SpawnedEdenInstance(EdenMonitor* monitor, std::shared_ptr<LogFile> log);
|
|
~SpawnedEdenInstance() override;
|
|
|
|
FOLLY_NODISCARD folly::Future<folly::Unit> start() override;
|
|
|
|
void takeover(pid_t pid, int logFD);
|
|
|
|
pid_t getPid() const override {
|
|
return pid_;
|
|
}
|
|
|
|
int getLogPipeFD() const {
|
|
return logPipe_.fd();
|
|
}
|
|
|
|
void checkLiveness() override;
|
|
|
|
private:
|
|
static constexpr size_t kLogBufferSize = 64 * 1024;
|
|
|
|
class StartupStatusChecker;
|
|
|
|
void handlerReady(uint16_t events) noexcept override;
|
|
void timeoutExpired() noexcept override;
|
|
|
|
void beginProcessingLogPipe();
|
|
void forwardLogOutput();
|
|
void closeLogPipe();
|
|
void checkLivenessImpl();
|
|
|
|
AbsolutePath edenfsExe_;
|
|
SpawnedProcess cmd_;
|
|
pid_t pid_{0};
|
|
FileDescriptor logPipe_;
|
|
std::shared_ptr<LogFile> log_;
|
|
std::unique_ptr<StartupStatusChecker> startupChecker_;
|
|
|
|
// EdenInstance objects are always allocated on the heap, so we just
|
|
// keep the log buffer in an inline array, rather than in a separately
|
|
// allocated buffer.
|
|
std::array<std::byte, kLogBufferSize> logBuffer_{};
|
|
};
|
|
|
|
} // namespace eden
|
|
} // namespace facebook
|