1 /*
2  * Error.h
3  *
4  * This source file is part of the FoundationDB open source project
5  *
6  * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #ifndef FLOW_ERROR_H
22 #define FLOW_ERROR_H
23 #pragma once
24 
25 #include <exception>
26 #include <map>
27 #include <boost/preprocessor/assert_msg.hpp>
28 #include <boost/preprocessor/facilities/is_empty.hpp>
29 #include <boost/preprocessor/control/if.hpp>
30 #include "flow/Platform.h"
31 #include "flow/Knobs.h"
32 
33 enum { invalid_error_code = 0xffff };
34 
35 class ErrorCodeTable : public std::map<int, std::pair<const char*, const char*>> {
36 public:
37 	ErrorCodeTable();
38 	void addCode(int code, const char *name, const char *description);
39 };
40 
41 class Error {
42 public:
code()43 	int code() const { return error_code; }
44 	const char* name() const;
45 	const char* what() const;
isInjectedFault()46 	bool isInjectedFault() const { return flags & FLAG_INJECTED_FAULT; }  // Use as little as possible, so injected faults effectively test real faults!
isValid()47 	bool isValid() const { return error_code != invalid_error_code; }
48 
49 	template <class Ar>
serialize(Ar & ar)50 	void serialize( Ar& ar ) {
51 		serializer(ar, error_code);
52 	}
53 
Error()54 	Error() : error_code(invalid_error_code), flags(0) {}
55 	explicit Error(int error_code);
56 
57 	static void init();
58 	static std::map<int, int>& errorCounts();
59 	static ErrorCodeTable& errorCodeTable();
fromCode(int error_code)60 	static Error fromCode(int error_code) { Error e; e.error_code = error_code; return e; }  // Doesn't change errorCounts
61 	static Error fromUnvalidatedCode(int error_code);  // Converts codes that are outside the legal range (but not necessarily individually unknown error codes) to unknown_error()
62 
63 	Error asInjectedFault() const;  // Returns an error with the same code() as this but isInjectedFault() is true
64 private:
65 	uint16_t error_code;
66 	uint16_t flags;
67 
68 	enum Flags { FLAG_INJECTED_FAULT=1 };
69 };
70 
71 Error systemErrorCodeToError();
72 
73 #undef ERROR
74 #define ERROR(name, number, description) inline Error name() { return Error( number ); }; enum { error_code_##name = number };
75 #include "error_definitions.h"
76 
77 //actor_cancelled has been renamed
actor_cancelled()78 inline Error actor_cancelled() { return Error( error_code_operation_cancelled ); }
79 enum { error_code_actor_cancelled = error_code_operation_cancelled };
80 
81 extern Error internal_error_impl( const char* file, int line );
82 #define internal_error() internal_error_impl( __FILE__, __LINE__ )
83 
84 extern bool isAssertDisabled( int line );
85 //#define ASSERT( condition ) ((void)0)
86 #define ASSERT(condition)                                                                                              \
87 	do {                                                                                                               \
88 		if (!((condition) || isAssertDisabled(__LINE__))) {                                                            \
89 			throw internal_error();                                                                                    \
90 		}                                                                                                              \
91 	} while (false);
92 #define ASSERT_ABORT(condition)                                                                                        \
93 	do {                                                                                                               \
94 		if (!((condition) || isAssertDisabled(__LINE__))) {                                                            \
95 			internal_error();                                                                                          \
96 			abort();                                                                                                   \
97 		}                                                                                                              \
98 	} while (false) // For use in destructors, where throwing exceptions is extremely dangerous
99 #define UNSTOPPABLE_ASSERT(condition)                                                                                  \
100 	do {                                                                                                               \
101 		if (!(condition)) {                                                                                            \
102 			throw internal_error();                                                                                    \
103 		}                                                                                                              \
104 	} while (false)
105 #define UNREACHABLE()                                                                                                  \
106 	{ throw internal_error(); }
107 
108 // ASSERT_WE_THINK() is to be used for assertions that we want to validate in testing, but which are judged too
109 // risky to evaluate at runtime, because the code should work even if they are false and throwing internal_error() would
110 // result in a bug.  Don't use it for assertions that are *expensive*; look at EXPENSIVE_VALIDATION.
111 #define ASSERT_WE_THINK( condition ) ASSERT( !g_network->isSimulated() || condition )
112 
113 #define ABORT_ON_ERROR( code_to_run ) \
114 	try { code_to_run; } \
115 	catch(Error &e) { criticalError(FDB_EXIT_ABORT, "AbortOnError", e.what()); } \
116 	catch(...) { criticalError(FDB_EXIT_ABORT, "AbortOnError", "Aborted due to unknown error"); }
117 
118 EXTERNC void breakpoint_me();
119 
120 #ifdef FDB_CLEAN_BUILD
121 #  define NOT_IN_CLEAN BOOST_STATIC_ASSERT_MSG(0, "This code can not be enabled in a clean build.");
122 #else
123 #  define NOT_IN_CLEAN
124 #endif
125 
126 #define FDB_EXPAND(...) __VA_ARGS__
127 #define FDB_STRINGIZE(...) BOOST_PP_IIF(BOOST_PP_IS_EMPTY(FDB_EXPAND(__VA_ARGS__)), "", #__VA_ARGS__)
128 #define ENABLED(...) BOOST_PP_IIF(BOOST_PP_IS_EMPTY(FDB_EXPAND(__VA_ARGS__)), 1, BOOST_PP_ASSERT_MSG(0, FDB_STRINGIZE(__VA_ARGS__)))
129 #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__)))
130 
131 /* Windows compilers won't allow the syntax of:
132 	 #if 0 && ENABLED(x)
133    So these macros replicate that, you instead do:
134 	 #if CENABLED(0, x)
135  */
136 #define CENABLED(x,y) BOOST_PP_IF(x, ENABLED(y), 0)
137 #define CDISABLED(x,y) BOOST_PP_IF(x, DISABLED(y), 0)
138 
139 #endif
140