sapling/eden/fs/telemetry/SubprocessScribeLogger.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

78 lines
2.1 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 <folly/Synchronized.h>
#include <list>
#include "eden/fs/telemetry/ScribeLogger.h"
#include "eden/fs/utils/SpawnedProcess.h"
namespace facebook {
namespace eden {
/**
* SubprocessScribeLogger manages an external unix process and asynchronously
* forwards newline-delimited messages to its stdin.
*/
class SubprocessScribeLogger : public ScribeLogger {
public:
/**
* Launch `executable` with `category` as the first argument.
*/
SubprocessScribeLogger(const char* executable, folly::StringPiece category);
/**
* Launch the process specified at argv[0] with the given argv, and forward
* its stdout to `stdoutFd`, if non-negative. Otherwise, output goes to
* /dev/null.
*/
explicit SubprocessScribeLogger(
const std::vector<std::string>& argv,
FileDescriptor stdoutFd = FileDescriptor());
/**
* Waits for the managed process to exit. If it is hung and doesn't complete,
* terminates the process. Either way, this destructor will completed within a
* bounded amount of time.
*/
~SubprocessScribeLogger();
/**
* Forwards a log message to the external process. Must not contain newlines,
* since that is how the process distinguishes between messages.
*
* If the writer process is not keeping up, messages are dropped.
*/
void log(std::string message) override;
using ScribeLogger::log;
private:
void closeProcess();
void writerThread();
struct State {
bool shouldStop = false;
bool didStop = false;
/// Sum of sizes of queued messages.
size_t totalBytes = 0;
/// Invariant: empty if didStop is true
std::list<std::string> messages;
};
SpawnedProcess process_;
std::thread writerThread_;
folly::Synchronized<State, std::mutex> state_;
std::condition_variable newMessageOrStop_;
std::condition_variable allMessagesWritten_;
};
} // namespace eden
} // namespace facebook