2017-05-26 04:48:44 +08:00
/*
* Error . h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013 - 2018 Apple Inc . and the FoundationDB project authors
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* http : //www.apache.org/licenses/LICENSE-2.0
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
# ifndef FLOW_ERROR_H
# define FLOW_ERROR_H
# pragma once
# include <exception>
# include <map>
# include <boost/preprocessor/assert_msg.hpp>
# include <boost/preprocessor/facilities/is_empty.hpp>
# include <boost/preprocessor/control/if.hpp>
2018-10-20 01:30:13 +08:00
# include "flow/Platform.h"
# include "flow/Knobs.h"
2019-01-31 05:53:23 +08:00
# include "flow/FileIdentifier.h"
2019-01-29 11:38:13 +08:00
# include "flow/ObjectSerializerTraits.h"
2017-05-26 04:48:44 +08:00
enum { invalid_error_code = 0xffff } ;
2017-09-29 03:35:03 +08:00
class ErrorCodeTable : public std : : map < int , std : : pair < const char * , const char * > > {
2017-05-26 04:48:44 +08:00
public :
ErrorCodeTable ( ) ;
2017-09-29 03:35:03 +08:00
void addCode ( int code , const char * name , const char * description ) ;
2017-05-26 04:48:44 +08:00
} ;
class Error {
public :
2019-01-31 05:53:23 +08:00
constexpr static FileIdentifier file_identifier = 14065384 ;
2017-05-26 04:48:44 +08:00
int code ( ) const { return error_code ; }
2017-09-29 03:35:03 +08:00
const char * name ( ) const ;
2017-05-26 04:48:44 +08:00
const char * what ( ) const ;
bool isInjectedFault ( ) const { return flags & FLAG_INJECTED_FAULT ; } // Use as little as possible, so injected faults effectively test real faults!
bool isValid ( ) const { return error_code ! = invalid_error_code ; }
template < class Ar >
void serialize ( Ar & ar ) {
2018-12-29 02:49:26 +08:00
serializer ( ar , error_code ) ;
2017-05-26 04:48:44 +08:00
}
Error ( ) : error_code ( invalid_error_code ) , flags ( 0 ) { }
explicit Error ( int error_code ) ;
static void init ( ) ;
static ErrorCodeTable & errorCodeTable ( ) ;
2020-05-16 02:04:26 +08:00
static Error fromCode ( int error_code ) {
Error e ;
e . error_code = error_code ;
return e ;
}
2017-05-26 04:48:44 +08:00
static Error fromUnvalidatedCode ( int error_code ) ; // Converts codes that are outside the legal range (but not necessarily individually unknown error codes) to unknown_error()
Error asInjectedFault ( ) const ; // Returns an error with the same code() as this but isInjectedFault() is true
private :
uint16_t error_code ;
uint16_t flags ;
enum Flags { FLAG_INJECTED_FAULT = 1 } ;
} ;
2018-12-01 02:55:19 +08:00
Error systemErrorCodeToError ( ) ;
2017-05-26 04:48:44 +08:00
# undef ERROR
2017-09-29 03:35:03 +08:00
# define ERROR(name, number, description) inline Error name() { return Error( number ); }; enum { error_code_##name = number };
2020-06-24 08:06:29 +08:00
2017-05-26 04:48:44 +08:00
# include "error_definitions.h"
//actor_cancelled has been renamed
inline Error actor_cancelled ( ) { return Error ( error_code_operation_cancelled ) ; }
enum { error_code_actor_cancelled = error_code_operation_cancelled } ;
2020-09-25 23:39:34 +08:00
extern Error internal_error_impl ( const char * file , int line ) ;
2020-06-24 08:06:29 +08:00
extern Error internal_error_impl ( const char * msg , const char * file , int line ) ;
2020-07-03 02:28:08 +08:00
extern Error internal_error_impl ( const char * a_nm , long long a , const char * op_nm , const char * b_nm , long long b , const char * file , int line ) ;
2020-09-25 23:39:34 +08:00
# define internal_error() internal_error_impl(__FILE__, __LINE__)
# define internal_error_msg(msg) internal_error_impl(msg, __FILE__, __LINE__)
2017-05-26 04:48:44 +08:00
extern bool isAssertDisabled ( int line ) ;
//#define ASSERT( condition ) ((void)0)
2019-02-18 06:26:45 +08:00
# define ASSERT(condition) \
do { \
if ( ! ( ( condition ) | | isAssertDisabled ( __LINE__ ) ) ) { \
2020-06-24 08:06:29 +08:00
throw internal_error_impl ( # condition , __FILE__ , __LINE__ ) ; \
2019-02-18 06:26:45 +08:00
} \
2020-06-24 08:06:29 +08:00
} while ( false )
2019-02-18 06:26:45 +08:00
# define ASSERT_ABORT(condition) \
do { \
if ( ! ( ( condition ) | | isAssertDisabled ( __LINE__ ) ) ) { \
2020-06-24 08:06:29 +08:00
internal_error_impl ( # condition , __FILE__ , __LINE__ ) ; \
2019-02-18 06:26:45 +08:00
abort ( ) ; \
} \
} while ( false ) // For use in destructors, where throwing exceptions is extremely dangerous
# define UNSTOPPABLE_ASSERT(condition) \
do { \
if ( ! ( condition ) ) { \
2020-06-24 08:06:29 +08:00
throw internal_error_impl ( # condition , __FILE__ , __LINE__ ) ; \
2019-02-18 06:26:45 +08:00
} \
} while ( false )
# define UNREACHABLE() \
2020-06-24 08:06:29 +08:00
{ throw internal_error_impl ( " unreachable " , __FILE__ , __LINE__ ) ; }
2017-05-26 04:48:44 +08:00
2020-07-03 02:28:08 +08:00
enum assert_op { EQ , NE , LT , GT , LE , GE } ;
// TODO: magic so this works even if const-ness doesn not match.
template < typename T , typename U >
void assert_num_impl ( char const * a_nm ,
T const & a ,
assert_op op ,
char const * b_nm ,
U const & b ,
char const * file ,
int line ) {
bool success ;
char const * op_name ;
switch ( op ) {
case EQ :
success = a = = b ;
op_name = " == " ;
break ;
case NE :
success = a ! = b ;
op_name = " != " ;
break ;
case LT :
success = a < b ;
op_name = " < " ;
break ;
case GT :
success = a > b ;
op_name = " > " ;
break ;
case LE :
success = a < = b ;
op_name = " <= " ;
break ;
case GE :
success = a > = b ;
op_name = " >= " ;
break ;
default :
success = false ;
op_name = " UNKNOWN OP " ;
}
if ( ! success ) {
throw internal_error_impl ( a_nm , ( long long ) a , op_name , b_nm , ( long long ) b , file , line ) ;
}
}
# define ASSERT_EQ(a, b) \
do { \
assert_num_impl ( ( # a ) , ( a ) , assert_op : : EQ , ( # b ) , ( b ) , __FILE__ , __LINE__ ) ; \
} while ( 0 )
# define ASSERT_NE(a, b) \
do { \
assert_num_impl ( ( # a ) , ( a ) , assert_op : : NE , ( # b ) , ( b ) , __FILE__ , __LINE__ ) ; \
} while ( 0 )
# define ASSERT_LT(a, b) \
do { \
assert_num_impl ( ( # a ) , ( a ) , assert_op : : LT , ( # b ) , ( b ) , __FILE__ , __LINE__ ) ; \
} while ( 0 )
# define ASSERT_LE(a, b) \
do { \
assert_num_impl ( ( # a ) , ( a ) , assert_op : : LE , ( # b ) , ( b ) , __FILE__ , __LINE__ ) ; \
} while ( 0 )
# define ASSERT_GT(a, b) \
do { \
assert_num_impl ( ( # a ) , ( a ) , assert_op : : GT , ( # b ) , ( b ) , __FILE__ , __LINE__ ) ; \
} while ( 0 )
# define ASSERT_GE(a, b) \
do { \
assert_num_impl ( ( # a ) , ( a ) , assert_op : : GE , ( # b ) , ( b ) , __FILE__ , __LINE__ ) ; \
} while ( 0 )
2017-05-26 04:48:44 +08:00
// ASSERT_WE_THINK() is to be used for assertions that we want to validate in testing, but which are judged too
// risky to evaluate at runtime, because the code should work even if they are false and throwing internal_error() would
// result in a bug. Don't use it for assertions that are *expensive*; look at EXPENSIVE_VALIDATION.
2019-09-17 02:42:29 +08:00
# define ASSERT_WE_THINK( condition ) ASSERT( !g_network->isSimulated() || (condition) )
2017-05-26 04:48:44 +08:00
# define ABORT_ON_ERROR( code_to_run ) \
try { code_to_run ; } \
catch ( Error & e ) { criticalError ( FDB_EXIT_ABORT , " AbortOnError " , e . what ( ) ) ; } \
catch ( . . . ) { criticalError ( FDB_EXIT_ABORT , " AbortOnError " , " Aborted due to unknown error " ) ; }
2019-02-08 09:02:14 +08:00
EXTERNC void breakpoint_me ( ) ;
2017-05-26 04:48:44 +08:00
# ifdef FDB_CLEAN_BUILD
# define NOT_IN_CLEAN BOOST_STATIC_ASSERT_MSG(0, "This code can not be enabled in a clean build.");
# else
# define NOT_IN_CLEAN
# endif
# define FDB_EXPAND(...) __VA_ARGS__
# define FDB_STRINGIZE(...) BOOST_PP_IIF(BOOST_PP_IS_EMPTY(FDB_EXPAND(__VA_ARGS__)), "", #__VA_ARGS__)
# define ENABLED(...) BOOST_PP_IIF(BOOST_PP_IS_EMPTY(FDB_EXPAND(__VA_ARGS__)), 1, BOOST_PP_ASSERT_MSG(0, FDB_STRINGIZE(__VA_ARGS__)))
# define DISABLED(...) BOOST_PP_IIF(BOOST_PP_NOT(BOOST_PP_IS_EMPTY(FDB_EXPAND(__VA_ARGS__))), 1, BOOST_PP_ASSERT_MSG(0, FDB_STRINGIZE(__VA_ARGS__)))
/* Windows compilers won't allow the syntax of:
#if 0 && ENABLED(x)
So these macros replicate that , you instead do :
# if CENABLED(0, x)
*/
# define CENABLED(x,y) BOOST_PP_IF(x, ENABLED(y), 0)
# define CDISABLED(x,y) BOOST_PP_IF(x, DISABLED(y), 0)
# endif