Index: streamexecutor/CMakeLists.txt =================================================================== --- /dev/null +++ streamexecutor/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.1) + +option(STREAM_EXECUTOR_UNIT_TESTS "enable unit tests" ON) + +# First find includes relative to the streamexecutor top-level source path. +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include) + +# If we are not building as part of LLVM, build StreamExecutor as a standalone +# project using LLVM as an external library: +string( + COMPARE + EQUAL + "${CMAKE_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}" + STREAM_EXECUTOR_STANDALONE) + +if(STREAM_EXECUTOR_STANDALONE) + project(StreamExecutor) + + find_package(LLVM REQUIRED CONFIG) + message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") + message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + + include_directories(${LLVM_INCLUDE_DIRS}) + add_definitions(${LLVM_DEFINITIONS}) + + # Find the libraries that correspond to the LLVM components + # that we wish to use + llvm_map_components_to_libnames(llvm_libs support) + + if(STREAM_EXECUTOR_UNIT_TESTS) + enable_testing() + find_package(GTest REQUIRED) + include_directories(${GTEST_INCLUDE_DIRS}) + endif() +else(NOT STREAM_EXECUTOR_STANDALONE) + if(STREAM_EXECUTOR_UNIT_TESTS) + include_directories( + "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include") + endif() +endif(STREAM_EXECUTOR_STANDALONE) + +# Insist on C++ 11 features. +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Add warning flags. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter") + +add_subdirectory(lib) Index: streamexecutor/README.txt =================================================================== --- streamexecutor/README.txt +++ streamexecutor/README.txt @@ -1 +1,11 @@ -This directory will contain the StreamExecutor library. +StreamExecutor +============== + +StreamExecutor is a wrapper around CUDA and OpenCL (host-side) programming +models (runtimes). This abstraction cleanly permits host code to target either +CUDA or OpenCL devices with identically-functioning data parallel kernels. It +manages the execution of concurrent work targeting the accelerator, similar to a +host-side Executor. + +This version of StreamExecutor can be built either as a sub-project of the LLVM +project or as a standalone project depending on LLVM as an external package. Index: streamexecutor/include/streamexecutor/Utils/Demangle.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/Demangle.h @@ -0,0 +1,27 @@ +//===-- Demangle.h - Demangle interface -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Interface for demangling a mangled name. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_DEMANGLE_H +#define STREAMEXECUTOR_UTILS_DEMANGLE_H + +#include + +namespace streamexecutor { + +/// Demangle a mangled name. +std::string demangle(const char *Mangled); + +} // namespace streamexecutor + +#endif // STREAMEXECUTOR_UTILS_DEMANGLE_H Index: streamexecutor/include/streamexecutor/Utils/Error.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/Error.h @@ -0,0 +1,159 @@ +//===-- Error.h - Error codes -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the error codes used throughout the StreamExecutor +/// project. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_ERROR_H +#define STREAMEXECUTOR_UTILS_ERROR_H + +namespace streamexecutor { +namespace error { + +/// The canonical error codes for StreamExecutor API. +/// +/// Warnings: +/// +/// - Do not change any numeric assignments. +/// - Changes to this list should only be made if there is a compelling +/// need that can't be satisfied in another way. +/// +/// Sometimes multiple error codes may apply. Services should return the most +/// specific error code that applies. For example, prefer OUT_OF_RANGE over +/// FAILED_PRECONDITION if both codes apply. Similarly prefer NOT_FOUND or +/// ALREADY_EXISTS over FAILED_PRECONDITION. +enum Code { + /// Not an error; returned on success + OK = 0, + + /// The operation was cancelled (typically by the caller). + CANCELLED = 1, + + /// Unknown error. + /// + /// An example of where this error may be returned is if a Status value + /// received from another address space belongs to an error-space that is not + /// known in this address space. Also errors raised by APIs that do not return + /// enough error information may be converted to this error. + UNKNOWN = 2, + + /// Client specified an invalid argument. + /// + /// Note that this differs from FAILED_PRECONDITION. INVALID_ARGUMENT + /// indicates arguments that are problematic regardless of the state of the + /// system (e.g., a malformed file name). + INVALID_ARGUMENT = 3, + + /// Deadline expired before operation could complete. + /// + /// For operations that change the state of the system, this error may be + /// returned even if the operation has completed successfully. For example, a + /// successful response from a server could have been delayed long enough for + /// the deadline to expire. + DEADLINE_EXCEEDED = 4, + + /// Some requested entity (e.g., file or directory) was not found. + /// + /// For privacy reasons, this code *may* be returned when the client + /// does not have the access right to the entity. + NOT_FOUND = 5, + + /// Some entity that we attempted to create (e.g., file or directory) already + /// exists. + ALREADY_EXISTS = 6, + + /// The caller does not have permission to execute the specified operation. + /// + /// PERMISSION_DENIED must not be used for rejections caused by exhausting + /// some resource (use RESOURCE_EXHAUSTED instead for those errors). + /// PERMISSION_DENIED must not be used if the caller can not be identified + /// (use UNAUTHENTICATED instead for those errors). + PERMISSION_DENIED = 7, + + /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the + /// entire file system is out of space. + RESOURCE_EXHAUSTED = 8, + + /// Operation was rejected because the system is not in a state required for + /// the operation's execution. + /// + /// For example, directory to be deleted may be non-empty, an rmdir operation + /// is applied to a non-directory, etc. + /// + /// A litmus test that may help a service implementor in deciding between + /// FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + /// (a) Use UNAVAILABLE if the client can retry just the failing call. + /// (b) Use ABORTED if the client should retry at a higher-level + /// (e.g., restarting a read-modify-write sequence). + /// (c) Use FAILED_PRECONDITION if the client should not retry until + /// the system state has been explicitly fixed. E.g., if an "rmdir" + /// fails because the directory is non-empty, FAILED_PRECONDITION should + /// be returned since the client should not retry unless they have first + /// fixed up the directory by deleting files from it. + /// (d) Use FAILED_PRECONDITION if the client performs conditional + /// REST Get/Update/Delete on a resource and the resource on the server + /// does not match the condition. E.g., conflicting read-modify-write on + /// the same resource. + FAILED_PRECONDITION = 9, + + /// The operation was aborted, typically due to a concurrency issue like + /// sequencer check failures, transaction aborts, etc. + /// + /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, + /// and UNAVAILABLE. + ABORTED = 10, + + /// Operation was attempted past the valid range. E.g., seeking or reading + /// past end of file. + /// + /// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed + /// if the system state changes. For example, a 32-bit file system will + /// generate INVALID_ARGUMENT if asked to read at an offset that is not in the + /// range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from + /// an offset past the current file size. + /// + /// There is a fair bit of overlap between FAILED_PRECONDITION and + /// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error) + /// when it applies so that callers who are iterating through a space can + /// easily look for an OUT_OF_RANGE error to detect when they are done. + OUT_OF_RANGE = 11, + + /// Operation is not implemented or not supported/enabled in this service. + UNIMPLEMENTED = 12, + + /// Internal errors. + /// + /// Means some invariants expected by underlying system has been broken. If + /// you see one of these errors, something is very broken. + INTERNAL = 13, + + /// The service is currently unavailable. + /// + /// This is a most likely a transient condition and may be corrected by + /// retrying with a backoff. + /// + /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, + /// and UNAVAILABLE. + UNAVAILABLE = 14, + + /// Unrecoverable data loss or corruption. + DATA_LOSS = 15, + + /// The request does not have valid authentication credentials for the + /// operation. + UNAUTHENTICATED = 16, +}; + +} // namespace error +} // namespace streamexecutor + +#endif // STREAMEXECUTOR_UTILS_ERROR_H Index: streamexecutor/include/streamexecutor/Utils/Macros.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/Macros.h @@ -0,0 +1,55 @@ +//===-- Macros.h - DSO loader selector --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines various helper macros which are useful throughout the StreamExecutor +/// project. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_MACROS_H +#define STREAMEXECUTOR_UTILS_MACROS_H + +/// Returns the number of elements in an array. +/// +/// The expression SE_ARRAYSIZE(a) is a compile-time constant of type size_t. +#define SE_ARRAYSIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) + +/// Marks a function return value so that the compiler emits a warning if that +/// return value is unused. +/// +/// If a type is marked with this attribute, all functions that return that type +/// are implicitly marked with this attribute. +#if defined(__clang__) && !defined(SWIG) +#if __has_feature(cxx_attributes) +#define SE_WARN_UNUSED_RESULT [[clang::warn_unused_result]] +#else +#define SE_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#endif +#else +#define SE_WARN_UNUSED_RESULT +#endif + +/// Checks that the input boolean expression is true +/// +/// The check is performed using assert, but the boolean expression is +/// evaluated even if the assert check is disabled by NDEBUG. +/// +/// The cast to void is to prevent unused variable warnings when NDEBUG causes +/// assert calls to be removed. +#define SE_CHECK(bool_expression) \ + do { \ + bool ok = (bool_expression); \ + assert(ok); \ + static_cast(ok); \ + } while (false) + +#endif // STREAMEXECUTOR_UTILS_MACROS_H Index: streamexecutor/include/streamexecutor/Utils/Mutex.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/Mutex.h @@ -0,0 +1,50 @@ +//===-- Mutex.h - Mutex selector --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines mutex, mutex_lock, and shared_lock in namespace streamexecutor so +/// that mutex and shared_lock are std::shared_time_mutex and std::shared_lock +/// respectively if those two types are available, otherwise defaulting to +/// std::mutex and std::unique_lock, respectively. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_MUTEX_H +#define STREAMEXECUTOR_UTILS_MUTEX_H + +// std::shared_timed_mutex is a C++14 feature. +#if (__cplusplus >= 201402L) +#define STREAM_EXECUTOR_USE_SHARED_MUTEX +#endif // __cplusplus >= 201402L + +#ifdef STREAM_EXECUTOR_USE_SHARED_MUTEX +#include +#else +#include +#endif + +namespace streamexecutor { + +#ifdef STREAM_EXECUTOR_USE_SHARED_MUTEX +using mutex = ::std::shared_timed_mutex; +#else +using mutex = ::std::mutex; +#endif + +using mutex_lock = std::unique_lock; + +#ifdef STREAM_EXECUTOR_USE_SHARED_MUTEX +using shared_lock = std::shared_lock; +#else +using shared_lock = std::unique_lock; +#endif + +} // namespace streamexecutor + +#endif // STREAMEXECUTOR_UTILS_MUTEX_H Index: streamexecutor/include/streamexecutor/Utils/PtrUtil.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/PtrUtil.h @@ -0,0 +1,60 @@ +//===-- PtrUtil.h - C++ unique_ptr utils ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file delcares methods for making unique_ptrs in the style of C++ 14's +/// std::make_unique. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_PTRUTIL_H +#define STREAMEXECUTOR_UTILS_PTRUTIL_H + +#include + +namespace streamexecutor { + +/// Trait to select overloads and return types for MakeUnique. +template struct MakeUniqueResult { + using scalar = std::unique_ptr; +}; +template struct MakeUniqueResult { + using array = std::unique_ptr; +}; +template struct MakeUniqueResult { + using invalid = void; +}; + +/// makeUnique(...) is an early implementation of C++14 std::make_unique. +/// +/// It is designed to be 100% compatible with std::make_unique so that the +/// eventual switchover will be a simple renaming operation. +template +typename MakeUniqueResult::scalar makeUnique(Args &&... Arguments) { + return std::unique_ptr(new T(std::forward(Arguments)...)); +} + +/// Overload for array of unknown bound. +/// +/// The allocation of arrays needs to use the array form of new, and cannot take +/// element constructor arguments. +template +typename MakeUniqueResult::array makeUnique(size_t ArraySize) { + return std::unique_ptr(new + typename std::remove_extent::type[ArraySize]()); +} + +/// Reject arrays of known bound. +template +typename MakeUniqueResult::invalid +makeUnique(Args &&... /* args */) = delete; + +} // namespace streamexecutor + +#endif // STREAMEXECUTOR_UTILS_PTRUTIL_H Index: streamexecutor/include/streamexecutor/Utils/Status.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/Status.h @@ -0,0 +1,148 @@ +//===-- Status.h - Status type ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file declares the Status type that holds either a success condition or +/// a failure condition and an error message. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_STATUS_H +#define STREAMEXECUTOR_UTILS_STATUS_H + +#include +#include + +#include "streamexecutor/Utils/Error.h" +#include "streamexecutor/Utils/Macros.h" + +namespace streamexecutor { + +class SE_WARN_UNUSED_RESULT Status { +public: + /// Create a success status. + Status() : State(nullptr) {} + ~Status() { delete State; } + + /// \brief Create a status with the specified error code and an error message + /// as a human-readable string containing more detailed information. + Status(error::Code Code, const std::string &ErrorMessage); + + /// Copy construct the specified status. + Status(const Status &S); + + /// Copy assign the specified status. + Status &operator=(const Status &S); + + /// Gets a success status. + static Status createOk() { return Status(); } + + /// Returns true iff the status indicates success. + bool ok() const { return (State == nullptr); } + + /// Gets the error code. + error::Code code() const { return ok() ? error::OK : State->code; } + + /// Gets a reference to the error message. + const std::string &errorMessage() const { + return ok() ? emptyString() : State->error_message; + } + + /// Tests two status instances for equality. + bool operator==(const Status &S) const; + + /// Tests two status instances for inequality. + bool operator!=(const Status &S) const; + + /// \brief If `ok()`, stores `NewStatus` into `*this`. + /// + /// If `!ok()`, preserves the current status, but may augment with additional + /// information about `NewStatus`. + /// + /// Convenient way of keeping track of the first error encountered. + /// Instead of: + /// \code + /// if (OverallStatus.ok()) OverallStatus = NewStatus + /// \endcode + /// Use: + /// \code + /// OverallStatus.update(NewStatus); + /// \endcode + void update(const Status &NewStatus); + + /// \brief Return a string representation of this status suitable for + /// printing. + /// + /// Returns the string `"OK"` for success. + std::string toString() const; + +private: + static const std::string &emptyString(); + struct InternalState { + error::Code code; + std::string error_message; + }; + // OK status has a `NULL` State. Otherwise, `State` points to + // a `State` structure containing the error code and message(s) + InternalState *State; + + void slowCopyFrom(const InternalState *Src); +}; + +inline Status::Status(const Status &S) + : State((S.State == nullptr) ? nullptr : new InternalState(*S.State)) {} + +inline Status &Status::operator=(const Status &S) { + // The following condition catches both aliasing (when this == &S), and the + // common case where both S and *this are ok. + if (State != S.State) { + slowCopyFrom(S.State); + } + return *this; +} + +inline bool Status::operator==(const Status &S) const { + return (this->State == S.State) || (toString() == S.toString()); +} + +inline bool Status::operator!=(const Status &S) const { return !(*this == S); } + +std::ostream &operator<<(std::ostream &Os, const Status &S); + +} // namespace streamexecutor + +// Checks that the input status expression is OK +// +// The check is performed using assert, but the status_expression is evaluated +// even if the assert check is disabled by NDEBUG. +// +// The cast to void is to prevent unused variable warnings when NDEBUG causes +// assert calls to be removed. +#define SE_CHECK_OK(status_expression) \ + do { \ + bool ok = (status_expression).ok(); \ + assert(ok); \ + ((void)ok); \ + } while (false) + +/// Early-returns the status if it is in error; otherwise, proceeds. +/// +/// The argument expression is guaranteed to be evaluated exactly once. +#define SE_RETURN_IF_ERROR(__status) \ + do { \ + auto status = __status; \ + if (!status.ok()) { \ + return status; \ + } \ + } while (false) + +/// Use this macro to explicitly ignore a Status returned from a function call. +#define SE_IGNORE_RETURN_STATUS(status) static_cast(status) + +#endif // STREAMEXECUTOR_UTILS_STATUS_H Index: streamexecutor/include/streamexecutor/Utils/StatusOr.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/StatusOr.h @@ -0,0 +1,270 @@ +//===-- StatusOr.h - StatusOr class -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// StatusOr is the union of a Status object and a T object. StatusOr models +/// the concept of an object that is either a usable value, or an error Status +/// explaining why such a value is not present. To this end, StatusOr does +/// not allow its Status value to be OK. Further, StatusOr does not +/// allow the contained pointer to be nullptr. +/// +/// The primary use-case for StatusOr is as the return value of a function +/// which may fail. +/// +/// Example client usage for a StatusOr, where T is not a pointer: +/// +/// \code +/// StatusOr Result = doBigCalculationThatCouldFail(); +/// if (Result.ok()) { +/// float Answer = Result.valueOrDie(); +/// printf("Big calculation yielded: %f", Answer); +/// } else { +/// cerr << Result.status(); +/// } +/// \endcode +/// +/// Example client usage for a StatusOr: +/// +/// \code +/// StatusOr Result = FooFactory::makeNewFoo(Arg); +/// if (Result.ok()) { +/// std::unique_ptr Foo(Result.valueOrDie()); +/// Foo->doSomethingCool(); +/// } else { +/// cerr << Result.status(); +/// } +/// \endcode +/// +/// Example client usage for a StatusOr>: +/// +/// \code +/// StatusOr> Result = FooFactory::makeNewFoo(Arg); +/// if (Result.ok()) { +/// std::unique_ptr Foo = Result.consumeValueOrDie(); +/// Foo->doSomethingCool(); +/// } else { +/// cerr << Result.status(); +/// } +/// \endcode +/// +/// Example factory implementation returning StatusOr: +/// +/// \code +/// StatusOr FooFactory::makeNewFoo(int Arg) { +/// if (Arg <= 0) { +/// return Status(error::INVALID_ARGUMENT, +/// "Arg must be positive"); +/// } else { +/// return new Foo(Arg); +/// } +/// } +/// \endcode +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_STATUSOR_H +#define STREAMEXECUTOR_UTILS_STATUSOR_H + +#include +#include +#include +#include +#include + +#include "streamexecutor/Utils/Error.h" +#include "streamexecutor/Utils/Status.h" + +namespace streamexecutor { + +template class StatusOr { + template friend class StatusOr; + +public: + /// Construct a new StatusOr with Status::UNKNOWN status + StatusOr() : TheStatus(error::UNKNOWN, "") {} + + /// Construct a new StatusOr with the given non-ok status. + /// + /// After calling this constructor, calls to valueOrDie() are invalid. + /// + /// NOTE: Not explicit - we want to use StatusOr as a return value, so it + /// is convenient and sensible to be able to do 'return Status()' when the + /// return type is StatusOr. + /// + /// REQUIRES: status != OK. + /// + /// In optimized builds, passing OK here will have the effect of passing + /// error::INTERNAL as a fallback. + StatusOr(const Status &S); + + /// Construct a new StatusOr with the given value. + /// + /// If T is a plain pointer, value must not be nullptr. After calling this + /// constructor, calls to valueOrDie() will succeed, and calls to status() + /// will return OK. + /// + /// NOTE: Not explicit - we want to use StatusOr as a return type so it is + /// convenient and sensible to be able to do 'return T()' when when the return + /// type is StatusOr. + /// + /// REQUIRES: if T is a plain pointer, value != nullptr. + /// + /// In optimized builds, passing a null pointer here will have the effect of + /// passing error::UNKNOWN as a fallback. + StatusOr(const T &Value); + + ~StatusOr(); + + /// Conversion copy constructor, T must be copy constructible from U + template + StatusOr(const StatusOr &Other) : TheStatus(Other.TheStatus) { + if (TheStatus.ok()) { + new (&MaybeValue.value) T(Other.MaybeValue.value); + } + } + + /// Conversion assignment operator, T must be assignable from U + template StatusOr &operator=(const StatusOr &Other) { + TheStatus = Other.TheStatus; + if (TheStatus.ok()) { + new (&MaybeValue.value) T(Other.MaybeValue.value); + } + return *this; + } + + /// Rvalue-reference overloads of the other constructors and assignment + /// operators, to support move-only types and avoid unnecessary copying. + StatusOr(T &&Value); + + /// Move conversion operator to avoid unecessary copy. + /// + /// T must be assignable from U. + /// + /// Not marked with explicit so the implicit conversion can happen. + template + StatusOr(StatusOr &&Other) : TheStatus(std::move(Other.TheStatus)) { + if (TheStatus.ok()) { + new (&MaybeValue.value) T(std::move(Other.MaybeValue.value)); + } + } + + /// Move assignment operator to avoid unnecessary copy. + /// + /// T must be assignable from U + template StatusOr &operator=(StatusOr &&Other) { + TheStatus = std::move(Other.TheStatus); + if (TheStatus.ok()) { + new (&MaybeValue.value) T(std::move(Other.MaybeValue.value)); + } + return *this; + } + + /// Returns a reference to our status. + /// + /// If this contains a T, then returns OK. + const Status &status() const { return TheStatus; } + + /// Returns this->status().ok() + bool ok() const { return TheStatus.ok(); } + + /// Returns a reference to our current value, requires that this->ok(). + /// + /// If you need to initialize a T object from the stored value, + /// consumeValueOrDie() may be more efficient. + const T &valueOrDie() const; + + /// Returns our current value, requires this->ok(). + /// + /// Use this if you would otherwise want to say std::move(S.valueOrDie()), for + /// example if you need to initialize a T object from the stored value and you + /// don't need subsequent access to the stored value. It uses T's move + /// constructor, if it has one, so it will work with move-only types, and will + /// often be more efficient than valueOrDie, but may leave the stored value in + /// an arbitrary valid state. + T consumeValueOrDie(); + +private: + Status TheStatus; + + // Enclose the possible value in a union to get the right alignment with the + // flexibility of not having to initialize it. + union MaybeValue { + T value; + + // Default constructor is for the case where the status is an error and + // there is no value. + MaybeValue() {} + + // Don't do anything. The containing class is responsible for destructing + // value if needed. + ~MaybeValue(){}; + } MaybeValue; + + void checkValueNotNull(const T &Value); + + template struct IsNull { + // For non-pointer U, a reference can never be NULL. + static inline bool isValueNull(const U &Value) { return false; } + }; + + template struct IsNull { + static inline bool isValueNull(const U *Value) { return Value == nullptr; } + }; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Implementation details for StatusOr + +template StatusOr::StatusOr(const Status &S) : TheStatus(S) { + assert(!S.ok()); + if (S.ok()) { + TheStatus = + Status(error::INTERNAL, + "OK is not a valid status for constructing a StatusOr"); + } +} + +template StatusOr::~StatusOr() { + if (TheStatus.ok()) { + MaybeValue.value.~T(); + } +} + +template StatusOr::StatusOr(const T &Value) { + checkValueNotNull(Value); + new (&MaybeValue.value) T(Value); +} + +template StatusOr::StatusOr(T &&Value) { + checkValueNotNull(Value); + new (&MaybeValue.value) T(std::move(Value)); +} + +template const T &StatusOr::valueOrDie() const { + assert(TheStatus.ok()); + return MaybeValue.value; +} + +template T StatusOr::consumeValueOrDie() { + assert(TheStatus.ok()); + return std::move(MaybeValue.value); +} + +template void StatusOr::checkValueNotNull(const T &Value) { + assert(!IsNull::isValueNull(Value)); + if (IsNull::isValueNull(Value)) { + TheStatus = + Status(error::INTERNAL, + "NULL is not a valid constructor argument to StatusOr"); + } +} + +} // namespace streamexecutor + +#endif // STREAMEXECUTOR_UTILS_STATUSOR_H Index: streamexecutor/include/streamexecutor/Utils/StrUtil.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/StrUtil.h @@ -0,0 +1,90 @@ +//===-- StrUtil.h - Utilities for strings -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Utility methods for working with strings. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_STRUTIL_H +#define STREAMEXECUTOR_UTILS_STRUTIL_H + +#include +#include +#include + +namespace streamexecutor { + +/// Return lower-cased version of text +std::string lowercase(const std::string &Text); + +/// Return upper-cased version of text +std::string uppercase(const std::string &Text); + +/// Return whether text starts with prefix +bool startsWith(const std::string &Text, const std::string &Prefix); + +/// Return whether text ends with suffix +bool endsWith(const std::string &Text, const std::string &Suffix); + +/// Returns a copy of the input string text with the given suffix removed +/// +/// If the suffix doesn't match, returns a copy of the original text. +std::string stripSuffixString(const std::string &Text, + const std::string &Suffix); + +/// Join a collection of objects into a string using the given separator +/// +/// The range must be iterable with a range-based for loop and the objects must +/// be suitable arguments for std::ostringstream::operator<<. +template std::string join(const T &Range, const char *Separator); + +// Split on a delimiter +std::vector split(const std::string &Text, char Delim); + +// Split on a delimiter with a predicate +template +std::vector split(const std::string &Text, char Delim, + Predicate P); + +// ------------------------------------------------------------------ +// Implementation details below + +template std::string join(const T &Collection, const char *Sep) { + std::ostringstream Oss; + bool First = true; + for (const auto &Item : Collection) { + Oss << (First ? "" : Sep) << Item; + First = false; + } + return Oss.str(); +} + +template +std::vector split(const std::string &Text, char Delim, + Predicate P) { + std::vector Result; + size_t TokenStart = 0; + if (!Text.empty()) { + for (size_t I = 0; I < Text.size() + 1; I++) { + if ((I == Text.size()) || (Text[I] == Delim)) { + size_t TokenLength = I - TokenStart; + if (P(Text.data() + TokenStart, TokenLength)) { + Result.push_back(Text.substr(TokenStart, TokenLength)); + } + TokenStart = I + 1; + } + } + } + return Result; +} + +} // namespace streamexecutor + +#endif // STREAMEXECUTOR_UTILS_STRUTIL_H Index: streamexecutor/include/streamexecutor/Utils/ThreadAnnotations.h =================================================================== --- /dev/null +++ streamexecutor/include/streamexecutor/Utils/ThreadAnnotations.h @@ -0,0 +1,30 @@ +//===-- ThreadAnnotations.h - Thread annotations ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Declares macros for thread annotations. +/// +//===----------------------------------------------------------------------===// + +#ifndef STREAMEXECUTOR_UTILS_THREADANNOTATIONS_H +#define STREAMEXECUTOR_UTILS_THREADANNOTATIONS_H + +#if defined(__clang__) && (!defined(SWIG)) +#define SE_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define SE_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +/// Document if a shared variable/field needs to be protected by a mutex. +/// +/// SE_GUARDED_BY allows the user to specify a particular mutex that should be +/// held when accessing the annotated variable. +#define SE_GUARDED_BY(x) SE_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +#endif // STREAMEXECUTOR_UTILS_THREADANNOTATIONS_H Index: streamexecutor/lib/CMakeLists.txt =================================================================== --- /dev/null +++ streamexecutor/lib/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library( + utils + OBJECT + Utils/Demangle.cpp + Utils/Status.cpp + Utils/StrUtil.cpp) Index: streamexecutor/lib/Utils/Demangle.cpp =================================================================== --- /dev/null +++ streamexecutor/lib/Utils/Demangle.cpp @@ -0,0 +1,50 @@ +//===-- Demangle.cc - Demangling implementation ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the implementation details for the Demangle function. +/// +//===----------------------------------------------------------------------===// + +#include "streamexecutor/Utils/Demangle.h" + +#include + +#if (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \ + !defined(__mips__) +#define HAS_CXA_DEMANGLE 1 +#else +#define HAS_CXA_DEMANGLE 0 +#endif + +#if HAS_CXA_DEMANGLE +#include +#endif + +namespace streamexecutor { + +// The API reference of abi::__cxa_demangle() can be found in +// libstdc++'s manual. +// +// https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html +std::string demangle(const char *Mangled) { + std::string Demangled; + int Status = 0; + char *Result = nullptr; +#if HAS_CXA_DEMANGLE + Result = abi::__cxa_demangle(Mangled, nullptr, nullptr, &Status); +#endif + if (Status == 0 && Result != nullptr) { // Demangling succeeeded. + Demangled.append(Result); + free(Result); + } + return Demangled; +} + +} // namespace streamexecutor Index: streamexecutor/lib/Utils/Status.cpp =================================================================== --- /dev/null +++ streamexecutor/lib/Utils/Status.cpp @@ -0,0 +1,120 @@ +//===-- Status.cc - Status implementation ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the implementation details for the Status class. +/// +//===----------------------------------------------------------------------===// + +#include "streamexecutor/Utils/Status.h" + +#include + +namespace streamexecutor { + +Status::Status(error::Code Code, const std::string &ErrorMessage) { + assert(Code != error::OK); + State = new InternalState; + State->code = Code; + State->error_message = ErrorMessage; +} + +void Status::update(const Status &NewStatus) { + if (ok()) { + *this = NewStatus; + } +} + +void Status::slowCopyFrom(const InternalState *Src) { + delete State; + if (Src == nullptr) { + State = nullptr; + } else { + State = new InternalState(*Src); + } +} + +const std::string &Status::emptyString() { + static std::string *Empty = new std::string; + return *Empty; +} + +std::string Status::toString() const { + if (State == nullptr) { + return "OK"; + } else { + char Tmp[30]; + const char *Type; + switch (code()) { + case error::CANCELLED: + Type = "Cancelled"; + break; + case error::UNKNOWN: + Type = "Unknown"; + break; + case error::INVALID_ARGUMENT: + Type = "Invalid argument"; + break; + case error::DEADLINE_EXCEEDED: + Type = "Deadline exceeded"; + break; + case error::NOT_FOUND: + Type = "Not found"; + break; + case error::ALREADY_EXISTS: + Type = "Already exists"; + break; + case error::PERMISSION_DENIED: + Type = "Permission denied"; + break; + case error::UNAUTHENTICATED: + Type = "Unauthenticated"; + break; + case error::RESOURCE_EXHAUSTED: + Type = "Resource exhausted"; + break; + case error::FAILED_PRECONDITION: + Type = "Failed precondition"; + break; + case error::ABORTED: + Type = "Aborted"; + break; + case error::OUT_OF_RANGE: + Type = "Out of range"; + break; + case error::UNIMPLEMENTED: + Type = "Unimplemented"; + break; + case error::INTERNAL: + Type = "Internal"; + break; + case error::UNAVAILABLE: + Type = "Unavailable"; + break; + case error::DATA_LOSS: + Type = "Data loss"; + break; + default: + snprintf(Tmp, sizeof(Tmp), "Unknown code(%d)", static_cast(code())); + Type = Tmp; + break; + } + std::string Result(Type); + Result += ": "; + Result += State->error_message; + return Result; + } +} + +std::ostream &operator<<(std::ostream &Os, const Status &S) { + Os << S.toString(); + return Os; +} + +} // namespace streamexecutor Index: streamexecutor/lib/Utils/StrUtil.cpp =================================================================== --- /dev/null +++ streamexecutor/lib/Utils/StrUtil.cpp @@ -0,0 +1,58 @@ +//===-- StrUtil.cc - String utility implementations -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the implementation details for string utilities. +/// +//===----------------------------------------------------------------------===// + +#include "streamexecutor/Utils/StrUtil.h" + +#include +#include + +namespace streamexecutor { + +std::string lowercase(const std::string &Text) { + std::string Result = Text; + for (char &Char : Result) { + Char = tolower(Char); + } + return Result; +} + +std::string uppercase(const std::string &Text) { + std::string Result = Text; + for (char &Char : Result) { + Char = toupper(Char); + } + return Result; +} + +bool startsWith(const std::string &Text, const std::string &Prefix) { + return (Text.length() >= Prefix.length()) && + !Text.compare(0, Prefix.length(), Prefix); +} + +bool endsWith(const std::string &Text, const std::string &Suffix) { + return (Text.length() >= Suffix.length()) && + !Text.compare(Text.size() - Suffix.size(), Suffix.length(), Suffix); +} + +std::vector split(const std::string &Text, char Delim) { + return split(Text, Delim, [](const char *, size_t) { return true; }); +} + +std::string stripSuffixString(const std::string &Text, + const std::string &Suffix) { + return Text.substr(0, Text.length() - + (endsWith(Text, Suffix) ? Suffix.length() : 0)); +} + +} // namespace streamexecutor