1 /* Copyright (C) 2011 Wildfire Games.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * error handling system: defines status codes, translates them to/from
25  * other schemes (e.g. errno), associates them with descriptive text,
26  * simplifies propagating errors / checking if functions failed.
27  */
28 
29 /**
30 
31 Error handling system
32 
33 
34 Why Error Codes?
35 ----------------
36 
37 To convey information about what failed, the alternatives are unique
38 integral codes and direct pointers to descriptive text. Both occupy the
39 same amount of space, but codes are easier to internationalize.
40 
41 
42 Method of Propagating Errors
43 ----------------------------
44 
45 When a low-level function has failed, this must be conveyed to the
46 higher-level application logic across several functions on the call stack.
47 There are two alternatives:
48 1) check at each call site whether a function failed;
49    if so, return to the caller.
50 2) throw an exception.
51 
52 We will discuss the advantages and disadvantages of exceptions,
53 which are the opposites of call site checking.
54 - performance: they shouldn't be used in time-critical code.
55 - predictability: exceptions can come up almost anywhere,
56   so it is hard to say what execution path will be taken.
57 - interoperability: not compatible with other languages.
58 + readability: cleans up code by separating application logic and
59   error handling. however, this is also a disadvantage because it
60   may be difficult to see at a glance if a piece of code does
61   error checking at all.
62 + visibility: errors are more likely to be seen than relying on
63   callers to check return codes; less reliant on discipline.
64 
65 Both have their place. Our recommendation is to throw error code
66 exceptions when checking call sites and propagating errors becomes tedious.
67 However, inter-module boundaries should always return error codes for
68 interoperability with other languages.
69 
70 
71 Simplifying Call-Site Checking
72 ------------------------------
73 
74 As mentioned above, this approach requires discipline. We provide
75 "enforcer" macros to simplify this task by propagating errors to
76 the calling function.
77 
78 Consider the following example:
79   Status status = doWork();
80   if(status != INFO::OK)
81     return status;
82 This can be replaced by:
83   RETURN_STATUS_IF_ERR(doWork());
84 
85 This provides a visible sign that the code handles errors but
86 reduces clutter.
87 
88 
89 When to warn the user?
90 ----------------------
91 
92 When a function fails, there are 2 places we can raise a warning:
93 as soon as the error condition is known, or higher on the call stack.
94 
95 We prefer the former because it is easier to ensure that all
96 possible return paths have been covered: search for all "return ERR::*"
97 or "return StatusFrom*" that are not followed by a "// NOWARN" comment.
98 The latter approach also risks multiple warnings along the
99 call stack for the same error.
100 
101 Note the special case of "validator" functions that e.g. verify the
102 state of an object: we now discuss pros/cons of just returning errors
103 without warning, and having their callers take care of that.
104 + they typically have many return paths (-> increased code size)
105 - this is balanced by validators that have many call sites.
106 - we want all return statements wrapped for consistency and
107   easily checking if any were forgotten
108 - adding // NOWARN to each validator return statement would be tedious.
109 - there is no advantage to checking at the call site; the call stack
110   indicates which caller of the validator failed anyway.
111 Validator functions should therefore also use WARN_RETURN.
112 
113 
114 Numbering Scheme
115 ----------------
116 
117 Each module header defines its own error codes to avoid a full rebuild
118 whenever a new code is added.
119 
120 Error codes start at -100000 (warnings are positive, but the
121 corresponding negative value should not be used to avoid confusion).
122 This scheme avoids collisions with all other known error codes.
123 
124 Each header gets 100 possible values; the tens value may be
125 used to denote groups within that header.
126 
127 The subsystem is denoted by the ten-thousands digit:
128 0 general
129 1 file
130 2 res (resource management)
131 3 sysdep (system-dependent)
132 4 win (Windows-specific)
133 
134 To summarize: +/-1SHHCC (S=subsystem, HH=header, CC=code number)
135 
136 10     general
137   00CC misc
138   03CC path
139   04CC debug
140   05CC debug_stl
141   06CC secure_crt
142   07CC wchar
143 
144 11     file
145   01CC vfs
146   03CC file
147   04CC archive
148 
149 12     res
150   00CC h_mgr
151   01CC tex
152 
153 13     sysdep
154   00CC cpu
155   01CC os_cpu
156 
157 14     win
158   00CC whrt
159 **/
160 
161 #ifndef INCLUDED_STATUS
162 #define INCLUDED_STATUS
163 
164 #include "lib/lib_api.h"
165 
166 // an integral type allows defining error codes in separate headers,
167 // but is not as type-safe as an enum. use Lint's 'strong type' checking
168 // to catch errors such as Status Func() { return 1; }.
169 // this must be i64 because some functions may multiplex Status with
170 // file offsets/sizes in their return value.
171 typedef i64 Status;
172 
173 // associates a status code with a description [and errno_equivalent].
174 struct StatusDefinition	// POD
175 {
176 	Status status;
177 
178 	// typically a string literal; must remain valid until end of program.
179 	const wchar_t* description;
180 
181 	// omit initializer (or initialize to 0) if there is no errno equivalent.
182 	int errno_equivalent;
183 };
184 
185 // retrieving description and errno_equivalent requires going through all
186 // StatusDefinition instances. we avoid dynamic memory allocation (which
187 // is problematic because status codes may be needed before _cinit) by
188 // organizing them into a linked list, with nodes residing in static storage.
189 // since modules may introduce many status codes, they are stored in an
190 // array, aka "bucket", which includes a link to the next bucket.
191 // initialized via STATUS_ADD_DEFINITIONS; opaque.
192 struct StatusDefinitionBucket	// POD
193 {
194 	const StatusDefinition* definitions;
195 	size_t numDefinitions;
196 	StatusDefinitionBucket* next;
197 };
198 
199 /**
200  * (called via STATUS_ADD_DEFINITIONS)
201  *
202  * @param bucket is being added; its definitions and numDefinitions must
203  *   already be initialized.
204  * @return previous bucket in list, suitable for initializing bucket->next.
205  *
206  * (this function must be callable as a static initializer; initializing
207  * next avoids the need for a separate dummy variable)
208  **/
209 LIB_API StatusDefinitionBucket* StatusAddDefinitions(StatusDefinitionBucket* bucket);
210 
211 /**
212  * add a module's array of StatusDefinition to the list.
213  * typically invoked at file scope.
214  * @param definitions name (identifier) of the array
215  **/
216 #define STATUS_ADD_DEFINITIONS(definitions) static StatusDefinitionBucket definitions##_bucket = { definitions, ARRAY_SIZE(definitions), StatusAddDefinitions(&definitions##_bucket) }
217 
218 
219 /**
220  * generate textual description of a Status.
221  *
222  * @param buf destination buffer (allows generating strings with
223  *   the code's numerical value if no definition is found)
224  * @param max_chars size of buffer [characters]
225  * @return buf (allows using this function in expressions)
226  **/
227 LIB_API wchar_t* StatusDescription(Status status, wchar_t* buf, size_t max_chars);
228 
229 /**
230  * @return the errno equivalent of a Status.
231  *
232  * used in wposix - underlying functions return Status but must be
233  * translated to errno at e.g. the mmap interface level. higher-level code
234  * that calls mmap will in turn convert back to Status.
235  **/
236 extern int ErrnoFromStatus(Status status);
237 
238 /**
239  * @return Status equivalent of errno, or ERR::FAIL if there's no equivalent.
240  *
241  * NB: reset errno to 0 before calling POSIX functions to avoid confusion
242  * with previous errors.
243  **/
244 extern Status StatusFromErrno();
245 
246 // note: other conversion routines (e.g. to/from Win32) are implemented in
247 // the corresponding modules to keep this header portable.
248 
249 
250 //-----------------------------------------------------------------------------
251 // propagation macros
252 
253 // warn and return a status. use when an error is first detected to
254 // begin propagating it to callers.
255 #define WARN_RETURN(status)\
256 	do\
257 	{\
258 		DEBUG_WARN_ERR(status);\
259 		return status;\
260 	}\
261 	while(0)
262 
263 // warn if expression is negative, i.e. an error.
264 // (this macro is more convenient than ENSURE)
265 #define WARN_IF_ERR(expression)\
266 	do\
267 	{\
268 		const Status status_ = (expression);\
269 		if(status_ < 0)\
270 			DEBUG_WARN_ERR(status_);\
271 	}\
272 	while(0)
273 
274 // return expression if it is negative, i.e. pass on errors to
275 // the caller. use when failures are common/expected.
276 #define RETURN_STATUS_IF_ERR(expression)\
277 	do\
278 	{\
279 		const Status status_ = (expression);\
280 		if(status_ < 0)\
281 			return status_;\
282 	}\
283 	while(0)
284 
285 // warn and return expression if it is negative.
286 // use if a function doesn't raise warnings when it returns errors.
287 #define WARN_RETURN_STATUS_IF_ERR(expression)\
288 	do\
289 	{\
290 		const Status status_ = (expression);\
291 		if(status_ < 0)\
292 		{\
293 			DEBUG_WARN_ERR(status_);\
294 			return status_;\
295 		}\
296 	}\
297 	while(0)
298 
299 // warn and throw a status. use when an error is first detected to
300 // begin propagating it to callers.
301 #define WARN_THROW(status)\
302 	do\
303 	{\
304 		DEBUG_WARN_ERR(status);\
305 		throw status;\
306 	}\
307 	while(0)
308 
309 // throw expression if it is negative. use to propagate
310 // expected errors from constructors.
311 #define THROW_STATUS_IF_ERR(expression)\
312 	do\
313 	{\
314 		const Status status_ = (expression);\
315 		if(status_ < 0)\
316 			throw status_;\
317 	}\
318 	while(0)
319 
320 // warn and throw expression if it is negative. use to propagate
321 // errors from constructors.
322 #define WARN_THROW_STATUS_IF_ERR(expression)\
323 	do\
324 	{\
325 		const Status status_ = (expression);\
326 		if(status_ < 0)\
327 		{\
328 			DEBUG_WARN_ERR(status_);\
329 			throw status_;\
330 		}\
331 	}\
332 	while(0)
333 
334 // if expression (typically the invocation of a callback) evaluates to:
335 // - INFO::OK, do nothing;
336 // - INFO::ALL_COMPLETE, return INFO::OK;
337 // - anything else, return that.
338 #define RETURN_STATUS_FROM_CALLBACK(expression)\
339 	do\
340 	{\
341 		const Status status_ = (expression);\
342 		if(status_ == INFO::ALL_COMPLETE)\
343 			return INFO::OK;\
344 		else if(status_ != INFO::OK)\
345 			return status_;\
346 	}\
347 	while(0)
348 
349 // return 0 if expression is negative. use in functions that return pointers.
350 #define RETURN_0_IF_ERR(expression)\
351 	do\
352 	{\
353 		const Status status_ = (expression);\
354 		if(status_ < 0)\
355 			return 0;\
356 	}\
357 	while(0)
358 
359 // warn if expression is false, i.e. zero.
360 #define WARN_IF_FALSE(expression)\
361 	do\
362 	{\
363 		if(!(expression))\
364 			debug_warn(L"FYI: WARN_IF_FALSE reports that a function failed. Feel free to ignore or suppress this warning.");\
365 	}\
366 	while(0)
367 
368 // warn and return 0 if expression is false, i.e. zero.
369 #define WARN_RETURN_0_IF_FALSE(expression)\
370 	do\
371 	{\
372 		if(!(expression))\
373 		{\
374 			debug_warn(L"FYI: WARN_RETURN_0_IF_FALSE reports that a function failed. Feel free to ignore or suppress this warning.");\
375 			return 0;\
376 		}\
377 	}\
378 	while(0)
379 
380 
381 //-----------------------------------------------------------------------------
382 // shared status code definitions
383 
384 namespace INFO {
385 
386 	const Status OK = 0;
387 
388 	// note: these values are > 100 to allow multiplexing them with
389 	// coroutine return values, which return completion percentage.
390 
391 	// notify caller that nothing was done.
392 	const Status SKIPPED       = +100001;
393 
394 	// function is incapable of doing the requested task with the given inputs.
395 	// this implies SKIPPED, but also conveys a bit more information.
396 	const Status CANNOT_HANDLE = +100002;
397 
398 	// function is meant to be called repeatedly, and now indicates that
399 	// all jobs are complete.
400 	const Status ALL_COMPLETE  = +100003;
401 
402 }	// namespace INFO
403 
404 namespace ERR {
405 
406 	const Status FAIL = -1;	// unknown failure
407 
408 	// general
409 	const Status LOGIC     = -100010;
410 	const Status EXCEPTION = -100011;
411 	const Status TIMED_OUT = -100012;
412 	const Status REENTERED = -100013;
413 	const Status CORRUPTED = -100014;
414 	const Status ABORTED   = -100015;
415 
416 	// invalid values (usually function arguments)
417 	const Status INVALID_ALIGNMENT = -100020;
418 	const Status INVALID_OFFSET    = -100021;
419 	const Status INVALID_HANDLE    = -100022;
420 	const Status INVALID_POINTER   = -100023;
421 	const Status INVALID_SIZE      = -100024;
422 	const Status INVALID_FLAG      = -100025;
423 	const Status INVALID_PARAM     = -100026;
424 	const Status INVALID_VERSION   = -100027;
425 
426 	// system limitations
427 	const Status AGAIN           = -100030;
428 	const Status LIMIT           = -100031;
429 	const Status NOT_SUPPORTED   = -100032;
430 	const Status NO_MEM          = -100033;
431 
432 	// these are for cases where we just want a distinct value to display and
433 	// a symbolic name + string would be overkill (e.g. the various
434 	// test cases in a validate() call). they are shared between multiple
435 	// functions; when something fails, the stack trace will show in which
436 	// one it was => these errors are unambiguous.
437 	// there are 3 tiers - 1..9 are used in most functions, 11..19 are
438 	// used in a function that calls another validator and 21..29 are
439 	// for for functions that call 2 other validators (this avoids
440 	// ambiguity as to which error actually happened where)
441 	const Status _1  = -100101;
442 	const Status _2  = -100102;
443 	const Status _3  = -100103;
444 	const Status _4  = -100104;
445 	const Status _5  = -100105;
446 	const Status _6  = -100106;
447 	const Status _7  = -100107;
448 	const Status _8  = -100108;
449 	const Status _9  = -100109;
450 	const Status _11 = -100111;
451 	const Status _12 = -100112;
452 	const Status _13 = -100113;
453 	const Status _14 = -100114;
454 	const Status _15 = -100115;
455 	const Status _16 = -100116;
456 	const Status _17 = -100117;
457 	const Status _18 = -100118;
458 	const Status _19 = -100119;
459 	const Status _21 = -100121;
460 	const Status _22 = -100122;
461 	const Status _23 = -100123;
462 	const Status _24 = -100124;
463 	const Status _25 = -100125;
464 	const Status _26 = -100126;
465 	const Status _27 = -100127;
466 	const Status _28 = -100128;
467 	const Status _29 = -100129;
468 
469 }	// namespace ERR
470 
471 #endif	// #ifndef INCLUDED_STATUS
472