sapling/eden/fs/utils/Bug.h
Xavier Deguillard 288a56327b utils: EDEN_BUG_FUTURE returns a Try<T>
Summary:
This allows the macro to be used for both folly::Future, but also with
ImmediateFuture, reducing the cost of migrating to the latter.

Reviewed By: chadaustin

Differential Revision: D28302478

fbshipit-source-id: d8470428ecaa6aaccf2aa884437ca97d4b45806f
2021-05-13 16:51:34 -07:00

162 lines
4.3 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/Conv.h>
#include <folly/CppAttributes.h>
#include <folly/futures/Future.h>
#include <atomic>
#include <string>
/**
* EDEN_BUG() should be used to log logic errors that should not happen unless
* there is a bug in the code.
*
* In debug builds this macro will cause the program to crash.
* However, in production builds crashing the program is fairly harsh, as this
* will destroy the client mount points, causing problems for any open programs
* or shells the user had that were using eden mounts. Therefore in production
* builds EDEN_BUG() just logs the error and then throws an exception that can
* be handled by the calling code.
*
* Use XLOG(FATAL) if you want to crash the program even in production builds.
*/
#define EDEN_BUG() \
::facebook::eden::EdenBugThrow() & \
::facebook::eden::EdenBug(__FILE__, __LINE__)
/**
* EDEN_BUG_FUTURE() is similar to EDEN_BUG() but should be used in context
* where a Future should be returned.
*
* In debug builds this will crash, but in production builds this will return a
* folly::Future<Type> containing an exception.
*/
#define EDEN_BUG_FUTURE(Type) \
::facebook::eden::EdenBugTry<Type>() & \
::facebook::eden::EdenBug(__FILE__, __LINE__)
/**
* EDEN_BUG_EXCEPTION() is similar to EDEN_BUG() but returns an exception.
*/
#define EDEN_BUG_EXCEPTION() \
::facebook::eden::EdenBugException() & \
::facebook::eden::EdenBug(__FILE__, __LINE__)
namespace folly {
class exception_wrapper;
}
namespace facebook {
namespace eden {
/**
* A helper class returned by the EDEN_BUG() macro.
*
* toException() can be called to convert it to a folly::exception_wrapper
* If toException() has not been called, it will throw an exception when it is
* destroyed.
*
* In debug builds EdenBug causes the program to abort rather than throwing or
* returning an exception.
*/
class EdenBug {
public:
FOLLY_COLD EdenBug(const char* file, int lineNumber);
FOLLY_COLD EdenBug(EdenBug&& other) noexcept;
EdenBug& operator=(EdenBug&& other) = delete;
~EdenBug();
/**
* Append to the bug message.
*/
template <typename T>
EdenBug&& operator<<(T&& t) && {
using folly::toAppend;
toAppend(std::forward<T>(t), &message_);
return std::move(*this);
}
/**
* Convert this EdenBug object to a folly::exception_wrapper
*
* If toException() is never called on an EdenBug object, it will throw on
* destruction.
*/
folly::exception_wrapper toException();
/**
* A wrapper for toException().throw_exception(). A typical use of EDEN_BUG()
* where the bug is captured is actually noreturn, but the compiler can't see
* that because moved-from EdenBug doesn't throw.
*
* To avoid compiler warnings, write:
* auto bug = EDEN_BUG() << "...";
* bug.throwException();
*/
[[noreturn]] void throwException();
/**
* Prevent EDEN_BUG() from crashing the program, even in debug builds.
*
* This is intended to allow unit tests to disable crashing.
* This generally shouldn't ever be called from normal production code.
*/
static void acquireDisableCrashLease();
static void releaseDisableCrashLease();
private:
void logError();
const char* file_;
int lineNumber_;
bool processed_{false};
std::string message_;
};
class EdenBugThrow {
public:
// We use operator&() here since it binds with lower precedence than the <<
// operator used to construct the EdenBug message.
[[noreturn]] void operator&(EdenBug&& bug) const {
bug.throwException();
}
};
template <typename T>
class EdenBugTry {
public:
FOLLY_NODISCARD
folly::Try<T> operator&(EdenBug&& bug) const {
return folly::Try<T>(bug.toException());
}
};
class EdenBugException {
public:
FOLLY_NODISCARD
folly::exception_wrapper operator&(EdenBug&& bug) const {
return bug.toException();
}
};
/**
* EdenBugDisabler temporarily disables crashing on EDEN_BUG as long as it
* exists.
*/
class EdenBugDisabler {
public:
EdenBugDisabler();
~EdenBugDisabler();
EdenBugDisabler(const EdenBugDisabler&) = delete;
EdenBugDisabler operator=(const EdenBugDisabler&) = delete;
};
} // namespace eden
} // namespace facebook