mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +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
233 lines
7.2 KiB
C++
233 lines
7.2 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/Try.h>
|
|
#include <folly/portability/IOVec.h>
|
|
#include "eden/fs/utils/PathFuncs.h"
|
|
|
|
namespace facebook {
|
|
namespace eden {
|
|
|
|
/** Windows doesn't have equivalent bits for all of the various
|
|
* open(2) flags, so we abstract it out here */
|
|
struct OpenFileHandleOptions {
|
|
unsigned followSymlinks : 1; // O_NOFOLLOW
|
|
unsigned closeOnExec : 1; // O_CLOEXEC
|
|
unsigned metaDataOnly : 1; // avoid accessing file contents
|
|
unsigned readContents : 1; // the read portion of O_RDONLY or O_RDWR
|
|
unsigned writeContents : 1; // the write portion of O_WRONLY or O_RDWR
|
|
unsigned create : 1; // O_CREAT
|
|
unsigned exclusiveCreate : 1; // O_EXCL
|
|
unsigned truncate : 1; // O_TRUNC
|
|
// The posix mode values to use when creating a file.
|
|
// Has no meaning on win32. On posix systems, will be modified by umask(2).
|
|
int createMode;
|
|
|
|
// Convervative defaults won't follow symlinks and won't be inherited
|
|
OpenFileHandleOptions()
|
|
: followSymlinks(0),
|
|
closeOnExec(1),
|
|
metaDataOnly(0),
|
|
readContents(0),
|
|
writeContents(0),
|
|
create(0),
|
|
exclusiveCreate(0),
|
|
truncate(0),
|
|
createMode(0777) {}
|
|
|
|
// Open an existing file for reading.
|
|
// Does not follow symlinks.
|
|
static inline OpenFileHandleOptions readFile() {
|
|
OpenFileHandleOptions opts;
|
|
opts.readContents = 1;
|
|
return opts;
|
|
}
|
|
|
|
// Open a file for write, creating if needed
|
|
// Does not follow symlinks.
|
|
static inline OpenFileHandleOptions writeFile() {
|
|
OpenFileHandleOptions opts;
|
|
opts.readContents = 1;
|
|
opts.writeContents = 1;
|
|
opts.create = 1;
|
|
return opts;
|
|
}
|
|
|
|
// Open a file so that it can be fstat'd
|
|
static inline OpenFileHandleOptions queryFileInfo() {
|
|
OpenFileHandleOptions opts;
|
|
opts.metaDataOnly = 1;
|
|
return opts;
|
|
}
|
|
|
|
// Open a directory for directory listing
|
|
// Does not follow symlinks.
|
|
static inline OpenFileHandleOptions openDir() {
|
|
OpenFileHandleOptions opts;
|
|
opts.readContents = 1;
|
|
return opts;
|
|
}
|
|
};
|
|
|
|
// Manages the lifetime of a system independent file descriptor.
|
|
// On POSIX systems this is a posix file descriptor.
|
|
// On Win32 systems this is a Win32 HANDLE object.
|
|
// It will close() the descriptor when it is destroyed.
|
|
class FileDescriptor {
|
|
public:
|
|
using system_handle_type =
|
|
#ifdef _WIN32
|
|
// We track the HANDLE value as intptr_t to avoid needing
|
|
// to pull in the windows header files all over the place;
|
|
// this is consistent with the _get_osfhandle function in
|
|
// the msvcrt library.
|
|
intptr_t
|
|
#else
|
|
int
|
|
#endif
|
|
;
|
|
|
|
// Understanding what sort of object the descriptor references
|
|
// is important in a number of situations on Windows systems.
|
|
// This enum allows tracking that type along with the descriptor.
|
|
enum class FDType {
|
|
Unknown,
|
|
Generic,
|
|
Pipe,
|
|
Socket,
|
|
};
|
|
|
|
// A value representing the canonical invalid handle
|
|
// value for the system.
|
|
static constexpr system_handle_type kInvalid = -1;
|
|
|
|
// Normalizes invalid handle values to our canonical invalid handle value.
|
|
// Otherwise, just returns the handle as-is.
|
|
static system_handle_type normalizeHandleValue(system_handle_type h);
|
|
|
|
// If the FDType is Unknown, probe it to determine its type
|
|
static FDType resolveFDType(system_handle_type h, FDType fdType);
|
|
|
|
~FileDescriptor();
|
|
|
|
// Default construct to an empty instance
|
|
FileDescriptor() = default;
|
|
|
|
// Construct a file descriptor object from an fd.
|
|
// Will happily accept an invalid handle value without
|
|
// raising an error; the FileDescriptor will simply evaluate as
|
|
// false in a boolean context.
|
|
explicit FileDescriptor(system_handle_type fd, FDType fdType);
|
|
|
|
// Construct a file descriptor object from an fd.
|
|
// If fd is invalid will throw a generic error with a message
|
|
// constructed from the provided operation name and the current
|
|
// errno value.
|
|
FileDescriptor(system_handle_type fd, const char* operation, FDType fdType);
|
|
|
|
// No copying
|
|
FileDescriptor(const FileDescriptor&) = delete;
|
|
FileDescriptor& operator=(const FileDescriptor&) = delete;
|
|
|
|
FileDescriptor(FileDescriptor&& other) noexcept;
|
|
FileDescriptor& operator=(FileDescriptor&& other) noexcept;
|
|
|
|
// Attempt to duplicate the file descriptor.
|
|
// If successful, returns a new descriptor referencing the same underlying
|
|
// file/stream/socket.
|
|
// On failure, throws an exception.
|
|
FileDescriptor duplicate() const;
|
|
|
|
// Closes the associated descriptor
|
|
void close();
|
|
|
|
// Stops tracking the descriptor, returning it to the caller.
|
|
// The caller is then responsible for closing it.
|
|
system_handle_type release();
|
|
|
|
// In a boolean context, returns true if this object owns
|
|
// a valid descriptor.
|
|
explicit operator bool() const {
|
|
return fd_ != kInvalid;
|
|
}
|
|
|
|
// Returns the underlying descriptor value
|
|
inline system_handle_type systemHandle() const {
|
|
return fd_;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
// Returns the descriptor value as a file descriptor.
|
|
// This method is only present on posix systems to aid in
|
|
// detecting non-portable use at compile time.
|
|
inline int fd() const {
|
|
return fd_;
|
|
}
|
|
#else
|
|
// Returns the descriptor value as a file handle.
|
|
// This method is only present on win32 systems to aid in
|
|
// detecting non-portable use at compile time.
|
|
inline intptr_t handle() const {
|
|
return fd_;
|
|
}
|
|
#endif
|
|
|
|
inline FDType fdType() const {
|
|
return fdType_;
|
|
}
|
|
|
|
// Set the close-on-exec bit
|
|
void setCloExec();
|
|
void clearCloExec();
|
|
|
|
// Enable non-blocking IO
|
|
void setNonBlock();
|
|
|
|
// Disable non-blocking IO
|
|
void clearNonBlock();
|
|
|
|
/** read(2), but yielding a Try for system independent error reporting */
|
|
folly::Try<ssize_t> read(void* buf, int size) const;
|
|
/** read(2), but will continue to read the full `size` parameter in
|
|
* event of short reads or EINTR */
|
|
folly::Try<ssize_t> readFull(void* buf, int size) const;
|
|
folly::Try<ssize_t> readNoInt(void* buf, int size) const;
|
|
folly::Try<ssize_t> readv(struct iovec* iov, size_t numIov) const;
|
|
folly::Try<ssize_t> readvFull(struct iovec* iov, size_t numIov) const;
|
|
|
|
/** write(2), but yielding a Try for system independent error reporting */
|
|
folly::Try<ssize_t> write(const void* buf, int size) const;
|
|
folly::Try<ssize_t> writeNoInt(const void* buf, int size) const;
|
|
folly::Try<ssize_t> writeFull(const void* buf, int size) const;
|
|
folly::Try<ssize_t> writev(struct iovec* iov, size_t numIov) const;
|
|
folly::Try<ssize_t> writevFull(struct iovec* iov, size_t numIov) const;
|
|
|
|
// Open a file descriptor on the supplied path using the specified
|
|
// open options. Will throw an exception on failure.
|
|
static FileDescriptor open(
|
|
AbsolutePathPiece path,
|
|
OpenFileHandleOptions options);
|
|
|
|
private:
|
|
system_handle_type fd_{kInvalid};
|
|
FDType fdType_{FDType::Unknown};
|
|
|
|
#ifdef _WIN32
|
|
folly::Try<ssize_t>
|
|
doVecOp(const struct iovec* iov, size_t numIov, bool isRead) const;
|
|
#endif
|
|
folly::Try<ssize_t>
|
|
wrapFull(void* buf, ssize_t size, bool isRead, bool onlyOnce) const;
|
|
folly::Try<ssize_t> wrapvFull(struct iovec* iov, size_t numIov, bool isRead)
|
|
const;
|
|
};
|
|
|
|
} // namespace eden
|
|
} // namespace facebook
|