# eXtremeDB Error Handling in C

> As explained in the Error Handling page, the C API runtime functions return three categories of return codes: Status, Error and Fatal Error. The actual values of these return codes are enumerated in `mco.h`. Status codes are return codes that are less than or equal to 50 and have `#define` names that are prefixed with `MCO_S_`. Error codes are return codes that are greater than 50 and have `#define` names that are prefixed with `MCO_E_`. (Please refer to the C API Return Codes for a complete list.)

> ## Handling Status Codes

> Status codes don’t indicate an error but merely the status after an operation has been performed. For example, most eXtremeDB functions, if successful, return `MCO_S_OK,` or if a search function finds no objects corresponding to the specified key value, the status code `MCO_S_NOTFOUND` is returned. A status code returned by a function does not affect the state of the transaction context within which the function was executed.

> ## Handling Error Codes

> Error codes, in contrast, indicate the runtime’s failure to successfully complete an operation. For example, if an invalid handle has been passed to a method, `MCO_E_INVALID_HANDLE` is returned. A function returning an error code causes the enclosing transaction to enter an error state.

> The error state of the transaction is remembered by the runtime and any subsequent call to a runtime function within that transaction will not execute but simply return the `MCO_E_TRANSACT` code. Being aware of this can greatly simplify your application code, while keeping the code size to a minimum. For example, it is not uncommon (and many vendors recommend) to check the return code after every call to a library function. This leads to source code that looks like one of:

>         int foo()
>         {
>             int rc, i;
>             for( i=0; i < 10; i++ )
>             {
>                 if((rc=func1()) != 0)
>                     break;
>                 if((rc=func2()) != 0)
>                     break;
>                 if((rc=func3()) != 0)
>                     break;
>             }
>             return rc;
>         }

> or

>         int foo()
>         {
>             int rc, i;
>             for( i = 0; i < 10; i++ )
>             {
>                 if( (rc = func1()) == 0 )
>                     if( (rc = func2()) == 0 )
>                         if( (rc = func3() == 0 )
>                             . . .
>                         else
>                             break;
>                     else
>                         break;
>                 else
>                     break;
>             }
>             return rc;
>         }

> In contrast, with eXtremeDB the application may simply check the return code on each iteration of the loop:

>         uint foo()
>         {
>             int rc, i;
>             for( i = rc = 0; i < 10 && MCO_S_OK == rc ; i++ )
>             {
>                 rc = func1();
>                 rc |= func2();
>                 rc |= func3();
>             }
>             return rc;
>         }

> The resulting implementation is clearly tighter code and easier to read.

> ## Handling Fatal Errors

> The third category of errors, fatal errors, are unrecoverable and cause the eXtremeDB runtime to call the internal function `mco_stop()`. This function performs the role of an assertion internal to eXtremeDB. If an error handler has been registered (via `mco_error_set_handler()`), `mco_stop()` will call this custom error handler. Otherwise `mco_stop()` will enter an infinite loop. If your application appears to “hang” inexplicably, it is probably because you have not registered an error handler, something went wrong, and `mco_stop()` entered this infinite loop.

> The `mco_stop()` function is only called when the runtime detects an unrecoverable error, such as corruption within its metadata. In such a case, restarting the process is the only viable course of action. As well, any runtime function call can be asserted and `mco_stop()` called if the assertion fails. This usually means that the application did something illegal from the runtime’s point of view, such as passed an invalid transaction or object handle to a runtime function, or corrupted the runtime internals in some way.

> Further, any runtime function might perform a number of validations that can result in a failed assertion. These validations vary depending on the `CHECK_LEVEL` set when the eXtremeDB library is compiled. The eXtremeDB object code packages include two sets of runtime libraries: the debug version, which has the highest `CHECK_LEVEL` and the release version, which has the lowest (minimal validations are performed). Although the release version does some validations, these have no negative impact on the overall performance. Developers are strongly advised to use the debug version during the development cycle. Then, only when no fatal errors are reported by eXtremeDB, switch to the release version. The only reason one would use the release version during the development phase is to measure application performance.

> ## Debugging during the Development Cycle

> The recommended method of debugging fatal errors for developers without a source code license is as follows:

> - First, be sure to register a custom error handler (see function `mco_set_error_handler()`).
> - Set a breakpoint inside the custom error handler, and run the application in the debugger to examine the application’s call stack at the point where the error occurs.
> - Note the last runtime function called and any other relevant information in the stack trace.
> - Consult the error code description in to see why the runtime assertion failed; for example, an error in the transaction manager, heap corruption, a cursor is corrupted, etc.
> - Check the appropriate application entity right before the fatal runtime call was issued and make sure that the entity - transaction handle, object handle, heap memory, etc. - is in fact corrupted.
> - Go back through the stack and try to find the application code where the entity was corrupted.

> The following example demonstrates this procedure (note that this code is taken from the `06_errorhandling_fatalerr` sample):

>         static void errhandler( int n )
>         {
>             printf( "\n eXtremeDB runtime fatal error: %d", n );
>             getchar();
>             exit( -1 );
>         }

>         void main()
>         {
>             ...
>             mco_error_set_handler( &errhandler );
>             ...
>             rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
>             if ( MCO_S_OK == rc )
>             {
>                 printf("\n\n\tThe following attempt to create a new record\n"
>                     "\tshould cause the Error handler to be called with Fatal\n"
>                     "\tError 340049 because it requires a READ_WRITE transaction.\n"
>                     "\tThe type of transaction started was MCO_READ_ONLY...\n"
>                     "\tNote: you will get error code instead of fatal error if\n"
>                     "\tthe program was linked not against _check runtime\n");
>                 /* anObject_new() should fail with error code 340049 = MCO_ERR_TRN+49 */
>                 rc = anObject_new(t, &rec);
>                 if ( MCO_S_OK == rc )
>                 {
>                     rc = anObject_data_put(&rec, data);
>                     /* the following code will not be reached unless the transaction is
>                         changed to MCO_READ_WRITE */
>                     if ( MCO_S_OK == rc )
>                     {
>                         rc = mco_trans_commit(t);
>                     }
>                 }
>                 else if (rc == MCO_E_ACCESS)
>                 {
>                     printf("\nThe sample was linked with no-check runtime\n");
>                     rc = MCO_S_OK;
>                 }
>                 ...
>             }
>         }

> When the above code is executed it causes the error handler to be called with the error code 340049 which generates the following output:

>         eXtremeDB runtime fatal error: 340049

> Checking the error code, the value of `340000` corresponds to the constant `MCO_ERR_TRN`. This indicates an error in the transaction being performed. (The added value of `49` indicates the line within the runtime function where the assertion failed, causing `mco_stop()` to be called. This is useful if it is necessary to contact McObject Support – or if the developer has a source code license. For a more detailed explanation of error codes see C API Return Codes.)

> Following is the call stack (as displayed by the Visual Studio 2008 debugger):

>         06-errorhandling-fatalerr.exe!mco_w_new_obj_noid(mco_trans_t_ * t=0x004e0378,
>             unsigned int init_size=4, unsigned short class_code=1, mco_objhandle_t_ * ret=0x0012facc)
>             Line 494 + 0x14 bytes

>         06-errorhandling-fatalerr.exe!anObject_new(mco_trans_t_ * t=0x004e0378,
>             anObject_ * handle=0x0012facc)
>              Line 120 + 0x2f bytes

>         06-errorhandling-fatalerr.exe!main(int argc=1, char * * argv=0x00343250)
>             Line 60 + 0x13 bytes

>         06-errorhandling-fatalerr.exe!__tmainCRTStartup()
>             Line 582 + 0x19 bytes

>         06-errorhandling-fatalerr.exe!mainCRTStartup()
>             Line 399

> It’s apparent that the function `mco_w_new_obj_noid()` called by `anObject_new()` caused the runtime assertion to fail. Knowing that error code `MCO_ERR_TRN` indicates a problem with the transaction and that the `_new()` functions require a `READ_WRITE` transaction, the solution is obvious.

> Note that the eXtremeDB generated functions like `anObject_new()` interface with the runtime through “wrapper” functions like `mco_w_new_obj_noid()`. Though it is not necessary for developers to delve into the eXtremeDB internals, it can be instructive to examine the `.c` interface file generated by the `mcocomp` schema compiler and notice how the compiler generates the calling parameters to these “wrapper” functions from the corresponding “dictionary” values.

> If you have a source code license, the debugging technique is slightly different. In this case it would be prudent to set a breakpoint in the `mco_stop()` function itself. This results in the following call stack:

>         06-errorhandling-fatalerr.exe!mco_stop__(int n=340049,
>             const char * file=0x004d437c, int line=494)
>             Line 61

>         06-errorhandling-fatalerr.exe!mco_w_new_obj_noid(mco_trans_t_ * t=0x004e0378,
>             unsigned int init_size=4, unsigned short class_code=1, mco_objhandle_t_ * ret=0x0012facc)
>             Line 494 + 0x14 bytes

>         06-errorhandling-fatalerr.exe!anObject_new(mco_trans_t_ * t=0x004e0378,
>             anObject_ * handle=0x0012facc)
>             Line 120 + 0x2f bytes

>         06-errorhandling-fatalerr.exe!main(int argc=1, char * * argv=0x00343250)
>              Line 60 + 0x13 bytes

>         06-errorhandling-fatalerr.exe!__tmainCRTStartup()
>             Line 582 + 0x19 bytes

>         06-errorhandling-fatalerr.exe!mainCRTStartup()
>             Line 399

> Here again the preceding line in the call stack indicates that function `mco_w_new_obj_noid()` failed and the same chain of logic makes it clear that the solution is to correct the transaction type.