sapling/eden/fs/monitor/EdenInstance.h
Wez Furlong 154d7309c9 eden: introduce SpawnedProcess
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
2020-09-01 13:31:32 -07:00

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