// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. // // A Status encapsulates the result of an operation. It may indicate success, // or it may indicate an error with an associated error message. // // Multiple threads can invoke const methods on a Status without // external synchronization, but if any of the threads may call a // non-const method, all threads accessing the same Status must use // external synchronization. #ifndef STORAGE_ROCKSDB_INCLUDE_STATUS_H_ #define STORAGE_ROCKSDB_INCLUDE_STATUS_H_ #include #include "rocksdb/slice.h" namespace rocksdb { class Status { public: // Create a success status. Status() : code_(kOk), subcode_(kNone), state_(nullptr) {} ~Status() { delete[] state_; } // Copy the specified status. Status(const Status& s); Status& operator=(const Status& s); Status(Status&& s) #if !(defined _MSC_VER) || ((defined _MSC_VER) && (_MSC_VER >= 1900)) noexcept #endif ; Status& operator=(Status&& s) #if !(defined _MSC_VER) || ((defined _MSC_VER) && (_MSC_VER >= 1900)) noexcept #endif ; bool operator==(const Status& rhs) const; bool operator!=(const Status& rhs) const; enum Code { kOk = 0, kNotFound = 1, kCorruption = 2, kNotSupported = 3, kInvalidArgument = 4, kIOError = 5, kMergeInProgress = 6, kIncomplete = 7, kShutdownInProgress = 8, kTimedOut = 9, kAborted = 10, kBusy = 11, kExpired = 12, kTryAgain = 13 }; Code code() const { return code_; } enum SubCode { kNone = 0, kMutexTimeout = 1, kLockTimeout = 2, kLockLimit = 3, kNoSpace = 4, kDeadlock = 5, kStaleFile = 6, kMemoryLimit = 7, kMaxSubCode }; SubCode subcode() const { return subcode_; } // Returns a C style string indicating the message of the Status const char* getState() const { return state_; } // Return a success status. static Status OK() { return Status(); } // Return error status of an appropriate type. static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kNotFound, msg, msg2); } // Fast path for not found without malloc; static Status NotFound(SubCode msg = kNone) { return Status(kNotFound, msg); } static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kCorruption, msg, msg2); } static Status Corruption(SubCode msg = kNone) { return Status(kCorruption, msg); } static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kNotSupported, msg, msg2); } static Status NotSupported(SubCode msg = kNone) { return Status(kNotSupported, msg); } static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kInvalidArgument, msg, msg2); } static Status InvalidArgument(SubCode msg = kNone) { return Status(kInvalidArgument, msg); } static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kIOError, msg, msg2); } static Status IOError(SubCode msg = kNone) { return Status(kIOError, msg); } static Status MergeInProgress(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kMergeInProgress, msg, msg2); } static Status MergeInProgress(SubCode msg = kNone) { return Status(kMergeInProgress, msg); } static Status Incomplete(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kIncomplete, msg, msg2); } static Status Incomplete(SubCode msg = kNone) { return Status(kIncomplete, msg); } static Status ShutdownInProgress(SubCode msg = kNone) { return Status(kShutdownInProgress, msg); } static Status ShutdownInProgress(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kShutdownInProgress, msg, msg2); } static Status Aborted(SubCode msg = kNone) { return Status(kAborted, msg); } static Status Aborted(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kAborted, msg, msg2); } static Status Busy(SubCode msg = kNone) { return Status(kBusy, msg); } static Status Busy(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kBusy, msg, msg2); } static Status TimedOut(SubCode msg = kNone) { return Status(kTimedOut, msg); } static Status TimedOut(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kTimedOut, msg, msg2); } static Status Expired(SubCode msg = kNone) { return Status(kExpired, msg); } static Status Expired(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kExpired, msg, msg2); } static Status TryAgain(SubCode msg = kNone) { return Status(kTryAgain, msg); } static Status TryAgain(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kTryAgain, msg, msg2); } static Status NoSpace() { return Status(kIOError, kNoSpace); } static Status NoSpace(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kIOError, kNoSpace, msg, msg2); } static Status MemoryLimit() { return Status(kAborted, kMemoryLimit); } static Status MemoryLimit(const Slice& msg, const Slice& msg2 = Slice()) { return Status(kAborted, kMemoryLimit, msg, msg2); } // Returns true iff the status indicates success. bool ok() const { return code() == kOk; } // Returns true iff the status indicates a NotFound error. bool IsNotFound() const { return code() == kNotFound; } // Returns true iff the status indicates a Corruption error. bool IsCorruption() const { return code() == kCorruption; } // Returns true iff the status indicates a NotSupported error. bool IsNotSupported() const { return code() == kNotSupported; } // Returns true iff the status indicates an InvalidArgument error. bool IsInvalidArgument() const { return code() == kInvalidArgument; } // Returns true iff the status indicates an IOError. bool IsIOError() const { return code() == kIOError; } // Returns true iff the status indicates an MergeInProgress. bool IsMergeInProgress() const { return code() == kMergeInProgress; } // Returns true iff the status indicates Incomplete bool IsIncomplete() const { return code() == kIncomplete; } // Returns true iff the status indicates Shutdown In progress bool IsShutdownInProgress() const { return code() == kShutdownInProgress; } bool IsTimedOut() const { return code() == kTimedOut; } bool IsAborted() const { return code() == kAborted; } bool IsLockLimit() const { return code() == kAborted && subcode() == kLockLimit; } // Returns true iff the status indicates that a resource is Busy and // temporarily could not be acquired. bool IsBusy() const { return code() == kBusy; } bool IsDeadlock() const { return code() == kBusy && subcode() == kDeadlock; } // Returns true iff the status indicated that the operation has Expired. bool IsExpired() const { return code() == kExpired; } // Returns true iff the status indicates a TryAgain error. // This usually means that the operation failed, but may succeed if // re-attempted. bool IsTryAgain() const { return code() == kTryAgain; } // Returns true iff the status indicates a NoSpace error // This is caused by an I/O error returning the specific "out of space" // error condition. Stricto sensu, an NoSpace error is an I/O error // with a specific subcode, enabling users to take the appropriate action // if needed bool IsNoSpace() const { return (code() == kIOError) && (subcode() == kNoSpace); } // Returns true iff the status indicates a memory limit error. There may be // cases where we limit the memory used in certain operations (eg. the size // of a write batch) in order to avoid out of memory exceptions. bool IsMemoryLimit() const { return (code() == kAborted) && (subcode() == kMemoryLimit); } // Return a string representation of this status suitable for printing. // Returns the string "OK" for success. std::string ToString() const; private: // A nullptr state_ (which is always the case for OK) means the message // is empty. // of the following form: // state_[0..3] == length of message // state_[4..] == message Code code_; SubCode subcode_; const char* state_; static const char* msgs[static_cast(kMaxSubCode)]; explicit Status(Code _code, SubCode _subcode = kNone) : code_(_code), subcode_(_subcode), state_(nullptr) {} Status(Code _code, SubCode _subcode, const Slice& msg, const Slice& msg2); Status(Code _code, const Slice& msg, const Slice& msg2) : Status(_code, kNone, msg, msg2) {} static const char* CopyState(const char* s); }; inline Status::Status(const Status& s) : code_(s.code_), subcode_(s.subcode_) { state_ = (s.state_ == nullptr) ? nullptr : CopyState(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 (this != &s) { code_ = s.code_; subcode_ = s.subcode_; delete[] state_; state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_); } return *this; } inline Status::Status(Status&& s) #if !(defined _MSC_VER) || ((defined _MSC_VER) && (_MSC_VER >= 1900)) noexcept #endif : Status() { *this = std::move(s); } inline Status& Status::operator=(Status&& s) #if !(defined _MSC_VER) || ((defined _MSC_VER) && (_MSC_VER >= 1900)) noexcept #endif { if (this != &s) { code_ = std::move(s.code_); s.code_ = kOk; subcode_ = std::move(s.subcode_); s.subcode_ = kNone; delete[] state_; state_ = nullptr; std::swap(state_, s.state_); } return *this; } inline bool Status::operator==(const Status& rhs) const { return (code_ == rhs.code_); } inline bool Status::operator!=(const Status& rhs) const { return !(*this == rhs); } } // namespace rocksdb #endif // STORAGE_ROCKSDB_INCLUDE_STATUS_H_