Pyrogenesis  trunk
status.h
Go to the documentation of this file.
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 {
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.
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.
193 {
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  **/
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
Definition: status.h:192
const Status _7
Definition: status.h:447
const Status LOGIC
Definition: status.h:409
int64_t i64
Definition: types.h:35
const Status _29
Definition: status.h:467
const Status _14
Definition: status.h:453
const Status _4
Definition: status.h:444
const Status _1
Definition: status.h:441
const Status _6
Definition: status.h:446
const Status _24
Definition: status.h:462
const Status OK
Definition: status.h:386
const Status _13
Definition: status.h:452
const Status _9
Definition: status.h:449
const Status _16
Definition: status.h:455
StatusDefinitionBucket * next
Definition: status.h:196
const Status TIMED_OUT
Definition: status.h:411
const Status _3
Definition: status.h:443
const Status CORRUPTED
Definition: status.h:413
const Status CANNOT_HANDLE
Definition: status.h:396
Status StatusFromErrno()
Definition: status.cpp:105
const Status INVALID_HANDLE
Definition: status.h:419
const Status ALL_COMPLETE
Definition: status.h:400
const Status AGAIN
Definition: status.h:427
Definition: debug.h:420
const Status _25
Definition: status.h:463
const Status ABORTED
Definition: status.h:414
const Status NOT_SUPPORTED
Definition: status.h:429
const Status INVALID_OFFSET
Definition: status.h:418
const Status _17
Definition: status.h:456
const Status INVALID_VERSION
Definition: status.h:424
LIB_API StatusDefinitionBucket * StatusAddDefinitions(StatusDefinitionBucket *bucket)
(called via STATUS_ADD_DEFINITIONS)
Definition: status.cpp:40
const Status _21
Definition: status.h:459
const wchar_t * description
Definition: status.h:179
const Status LIMIT
Definition: status.h:428
const Status INVALID_POINTER
Definition: status.h:420
const Status _5
Definition: status.h:445
const Status _27
Definition: status.h:465
const Status REENTERED
Definition: status.h:412
size_t numDefinitions
Definition: status.h:195
const Status _15
Definition: status.h:454
const Status _19
Definition: status.h:458
const Status INVALID_PARAM
Definition: status.h:423
i64 Status
Error handling system.
Definition: status.h:171
const Status INVALID_SIZE
Definition: status.h:421
const Status INVALID_ALIGNMENT
Definition: status.h:417
int ErrnoFromStatus(Status status)
Definition: status.cpp:93
Introduction
Definition: debug.h:404
const Status SKIPPED
Definition: status.h:392
const Status _26
Definition: status.h:464
const Status _11
Definition: status.h:450
const Status _28
Definition: status.h:466
const Status _18
Definition: status.h:457
Definition: status.h:174
const StatusDefinition * definitions
Definition: status.h:194
Status status
Definition: status.h:176
const Status _8
Definition: status.h:448
const Status INVALID_FLAG
Definition: status.h:422
const Status _22
Definition: status.h:460
LIB_API wchar_t * StatusDescription(Status status, wchar_t *buf, size_t max_chars)
generate textual description of a Status.
Definition: status.cpp:79
const Status FAIL
Definition: status.h:406
const Status _2
Definition: status.h:442
const Status EXCEPTION
Definition: status.h:410
const Status NO_MEM
Definition: status.h:430
const Status _23
Definition: status.h:461
const Status _12
Definition: status.h:451
int errno_equivalent
Definition: status.h:182