![]() |
Error Handling |
<<< Introduction To Error Handling | Chapters | Signal Handling >>> |
This chapter describes how to handle errors and other failure status codes returned by Parasolid functions. It is essential that your application intercepts and deals with these errors.
radius
or
height
would return PK_ERROR_distance_le_0. Note that in the event of an error, the values of any returned arguments are undefined and should not be used.
fault
argument to PK_blend_fault_overlap_c.A complete list of all Parasolid error codes, together with descriptions of each error code, and information about which functions can raise the error, can be found in the PK Interface Programming Reference Manual. Common errors returned by a function are listed as “specific errors” in the documentation for that function, although this list is not exhaustive. Further frustrum-specific error information is available from the Downward Interfaces manual.
For examples of handling PK errors, see the code examples in the
C++\Code Examples\Application
support\Error
Handling
folder, located in
example_applications
in your Parasolid installation folder.
Note: None of these strategies is generally applicable to KI routines. For information on handling errors from these routines (which are indicated by a non-zero value for
ifail
) see Section 1.5, “Error returns” of the KI Programming Reference Manual. Further information is available from Appendix B, “Kernel Interface Error Codes” in this manual. |
There are two primary decisions to make when deciding how to handle PK errors:
Your application can register a particular function, known as an error handler, with Parasolid. This function is supplied by you and will be called whenever an error occurs, to perform certain recovery tasks. Parasolid automatically invokes this error-handling function just before returning from a failing PK function. If control is then passed back to the application (through the failing PK function), the values of any return arguments of the PK function are undefined.
In addition, your application can choose whether to throw an exception when it encounters an error, via the
try/throw/catch
statements in C++ (or the equivalent
setjmp/longjmp
functions in C). If your application uses a registered error handler combined with exceptions, it does not need to check the return status of each PK function before proceeding.
Whichever strategy you choose, the same recovery action needs to be taken at some point. This action depends on the severity of the error, as described in Section 120.2.2, “Error severity”.
To provide a context for comparing ways of handling PK errors, a simplified outline of a C++ driver routine for an interactive Parasolid application is given in Figure 120-1. Execution proceeds in an infinite loop that starts by waiting for an instruction from the user. Once given, the instruction is processed according to its type, which may invoke one or more calls to Parasolid functions.
The code for processing the instructions is wrapped in a
try
statement, with corresponding
catch
blocks to deal with any types of error that arise (from Parasolid or otherwise). These blocks inform the user that the instruction has not been carried out, and make sure that the application is ready to receive the next instruction: this could be to try the previous instruction again, with different input data.
Figure 120-1 Simplified outline of the main loop in a Parasolid application
PK errors (i.e., when a function returns a value other than PK_ERROR_no_errors) can be classified by their severity, which is either mild, serious or fatal. The consequences of the error, and steps necessary for recovery, depend on the severity, as described in the following table.
If your application does not carry out the necessary action after a serious or fatal error, data structures may become corrupt, leading to run-time errors at a later stage. You are therefore strongly advised to implement rollback functionality in your application, in order to restore a session after serious errors: see Chapter 97, “Rollback”, for details. Note that if you have more than one partition in your session, you should roll back every partition that contains entities affected by the error.
If you are dealing with multiple threads in your application and a serious error occurs in one or more of the threads you should call PK_THREAD_ask_exclusion to assess whether the error needs to be handled. You can then either roll back or use PK_THREAD_clear_exclusion. See Section 113.6.1, “Excluding application threads after a serious or fatal error” for information on PK_THREAD_clear_exclusion.
The ways to handle errors described in this section are:
You register an error-handling function with Parasolid by calling PK_ERROR_register_callbacks. Your error handler must receive a pointer to a PK_ERROR_sf_t structure and have the return type PK_ERROR_code_t, although it need not necessarily return a value, as this is not used by Parasolid. The PK_ERROR_sf_t structure contains details of the error such as the name of the failing function, the error code, and the severity of the error. For more details, see Section 120.2.5, “PK_ERROR functions and their use” and also the error-handling code examples in the Example Application within the Jumpstart Kit.
Note that an error handler that uses exceptions does not have to throw an exception for every type of error. For example, you can design it to return to the failing PK function for certain errors and throw an exception for the rest.
Warning: Your error handler should not attempt to alter any details of the current error (by modifying the PK_ERROR_sf_t structure, or returning a different error code): Parasolid stores this information separately, and any such alterations have no effect. |
The strategies here also apply to re-entrant function calls, where a PK function is called from the GO or some other application function that has itself been called from Parasolid. They work in the same way, although any exceptions must be thrown back to a point outside of any PK function calls; they must not be thrown to functions called from Parasolid, as this could leave the PK kernel in an inconsistent state.
When dealing with multiple threads in an application, PK_THREAD_xxx functions should be used instead of PK_SESSION_xxx or PK_ERROR_xxx functions. For example, you should use PK_THREAD_ask_last_error in preference to PK_ERROR_ask_last. See Section 113.2, “How to set up and use application threads”for a list of these functions.
Warning: Calling PK functions other than PK_SESSION_tidy within an error handler that returns to your application via Parasolid may lead to unpredictable results. |
The example code of Figure 120-2 and Figure 120-3 illustrates handling errors without registering an error handler.
A sequence of PK function calls takes place. After each call, the application checks the return status of the function before the next call is made. If an error is encountered somewhere, the subsequent
if
statements ensure that no further PK function calls are made until the error is dealt with. In the event of an error, the function PK_ERROR_ask_last is called to obtain the relevant details (see Section 120.2.5, “PK_ERROR functions and their use”), which are recorded by the application for later use (see
Figure 120-1). The application then calls its own function, handlePKError, which has not been registered with Parasolid.
// call sequence of (related) PK functions PK_ERROR_t error = PK_XXX_fun_1( arg1, ..., argn ); // check the error return code each time before continuing if (error == PK_ERROR_no_errors) error = PK_XXX_fun_2( arg1, ..., argm ); if (error == PK_ERROR_no_errors) error = PK_XXX_fun_3( arg1, ..., argr ); ... // perform error handling after PK calls if (error != PK_ERROR_no_errors) { PK_LOGICAL_t was_error; PK_ERROR_sf_t error_sf; // get details of the error PK_THREAD_ask_last_error( &was_error, &error_sf ); // set error information for the application and //take appropriate action myApp->setErrorStatus( true ); myApp->setError( “PK”, error_sf.severity, ... ); myApp->handlePKError( &error_sf ); } |
Figure 120-2 Sequence of PK calls with no registered error handler
The function handlePKError performs certain PK-specific recovery tasks according to the severity of the error, as described in Section 120.2.2, “Error severity”, and also any application-specific tidying up that needs to be done. Note that for mild and serious errors, the failing operation might be repeated (with different data); hence further action to prepare for this would be taken by the application in the main loop of the code (see Figure 120-1).
void myApp::handlePKError( PK_ERROR_sf_t* error_sf ) { if (error_sf->severity == PK_ERROR_mild) { // warn user of a mild error Message( "Mild error in Parasolid function %s", error_sf->function ); ... // perform any further application-specific clean-up tasks } else if (error_sf->severity == PK_ERROR_serious) { // warn user of a serious error Message( "Serious error in Parasolid function %s", error_sf->function ); // roll back the current partition (and any other affected partitions) PK_SESSION_ask_curr_partition( &partition ); PK_PARTITION_ask_pmark( partition, &pmark1, ... );
// free allocated memory where necessary if (n_new) PK_MEMORY_free( new_entities ); ... // perform any further application-specific clean-up tasks } else if (error_sf->severity == PK_ERROR_fatal) { // warn user of a fatal error Message( "Fatal error in Parasolid function %s", error_sf->function ); // stop the current session PK_SESSION_stop(); //call application function containing PK calls to start a new session
restartParasolid(); ... // perform any further application-specific clean-up tasks } } |
Figure 120-3 Example of a non-registered application function to handle errors
The example code of Figure 120-4 and Figure 120-5 illustrates handling errors with a registered error handler that does not use exceptions.
A sequence of PK function calls is shown in Figure 120-4. After each call, the application checks the return status of the function before the next call is made. If an error is encountered somewhere, the registered error handler is invoked automatically just before the end of the failing PK function, and subsequently returns to that function.
// call sequence of (related) PK functions PK_ERROR_t error = PK_XXX_fun_1( arg1, ..., argn ); // check the error return code each time before continuing if (error == PK_ERROR_no_errors) error = PK_XXX_fun_2( arg1, ..., argm ); if (error == PK_ERROR_no_errors) error = PK_XXX_fun_3( arg1, ..., argr ); ... // call error-processing PK functions outside of the error handler if (error != PK_ERROR_no_errors) { PK_LOGICAL_t was_error; PK_ERROR_sf_t error_sf; // get details of the error PK_THREAD_ask_last_error( &was_error, &error_sf ); // process serious or fatal errors if (error_sf.severity == PK_ERROR_serious) { // roll back the current partition (and any other affected partitions) PK_SESSION_ask_curr_partition( &partition ); PK_PARTITION_ask_pmark( partition, &pmark1, ... );
// free memory where necessary if (n_new) PK_MEMORY_free( new_entities ); } else if (error_sf.severity == PK_ERROR_fatal) { // stop the current session PK_SESSION_stop(); // call application function containing PK calls to start a new session //and re-register the frustrum and delta frustrum restartParasolid(); } } |
Figure 120-4 Sequence of PK calls with a registered error handler that does not use exceptions (the code for the error handler is not shown in this figure)
The example error handler illustrated in Figure 120-5 provides information and performs application-specific clean-up tasks, but does not call any PK functions: the PK calls are made after returning to the application (as shown in Figure 120-4), in order to avoid leaving the Parasolid kernel in an inconsistent state. The actual flow of execution resembles that of the previous case (with no registered error handler).
PK_ERROR_code_t myApp::handlePKError( PK_ERROR_sf_t* error_sf ) { // set error information for the application myApp->setErrorStatus( true ); myApp->setError( “PK”, error_sf.severity, ... ); // report severity of error and failing function if (error_sf->severity == PK_ERROR_mild) { // warn user of a mild error Message( "Mild error in Parasolid function %s", error_sf->function ); ... // perform any further application-specific clean-up tasks } else if (error_sf->severity == PK_ERROR_serious) { // warn user of a serious error: for safety, do not call the PK here Message( "Serious error in Parasolid function %s", error_sf->function ); ... // perform any further application-specific clean-up tasks } else if (error_sf->severity == PK_ERROR_fatal) { // warn user of a fatal error: for safety, do not call the PK here Message( "Fatal error in Parasolid function %s", error_sf->function ); ... // perform any further application-specific clean-up tasks } } |
Figure 120-5 Example of a registered application function to handle errors without using exceptions
The example code of Figure 120-6 and Figure 120-7 illustrates handling errors using a registered error handler that uses exceptions.
try { // call sequence of (related) PK functions PK_XXX_fun_1( arg1, ..., argn ); // no need to check the error return code each time PK_XXX_fun_2( arg1, ..., argm ); PK_XXX_fun_3( arg1, ..., argr ); ... } ... // could combine this with the catch statements in Figure 120-1 catch(PK_ERROR_sf_t* error_sf) { if (error_sf->severity == PK_ERROR_mild) { // warn user of a mild error Message( "Mild error in Parasolid function %s", error_sf->function ); ... // perform any further application-specific clean-up tasks } else if (error_sf->severity == PK_ERROR_serious) { // warn user of a serious error Message( "Serious error in Parasolid function %s", error_sf->function ); // roll back the current partition (and any other affected partitions) PK_SESSION_ask_curr_partition( &partition ); PK_PARTITION_ask_pmark( partition, &pmark1, ... );
// free memory where necessary if (n_new) PK_MEMORY_free( new_entities ); ... // perform any further application-specific clean-up tasks } else if (error_sf->severity == PK_ERROR_fatal) { // warn user of a fatal error Message( "Fatal error in Parasolid function %s", error_sf->function ); // stop the current session PK_SESSION_stop(); // call application function containing PK calls to start a new session //and re-register the frustrum and delta frustrum restartParasolid(); ... // perform any further application-specific clean-up tasks } } |
Figure 120-6 Sequence of PK calls with a registered error handler using exceptions
A sequence of PK function calls takes place within a
try
block, as shown in
Figure 120-6. There is no need for the application to check the return status each time, however, because in the event of an error, execution does not resume at the next call in the sequence. Instead, the error handler is automatically invoked. After first preparing the Parasolid kernel with a call to PK_THREAD_tidy, the error handler throws an exception that is caught by the
catch
statement shown in
Figure 120-6.
In a real application, the error handler might throw different exceptions according to the severity of the error and other circumstances. The
catch
statements for these could be combined with those in
Figure 120-1.
PK_ERROR_code_t myApp::handlePKError( PK_ERROR_sf_t* error_sf ) { // set the error information for the application setErrorStatus( true ); setError( “PK”, error_sf->severity, ... ); // tidy the Parasolid session before throwing an exception if (PK_THREAD_tidy() != PK_ERROR_no_errors) setPKTidyStatus( false ); // throw an exception throw error_sf; } |
Figure 120-7 Example of a registered application function to handle errors using exceptions
Note: When your registered error handler throws an exception (and therefore passes control back to your application instead of Parasolid), you must call PK_THREAD_tidy before calling another PK function. This tidies the Parasolid kernel and internal memory; it must be done before the exception is thrown. |
Registering an error handler allows Parasolid to report errors immediately, and perform any application-specific processing automatically. Better still, using an error handler that throws exceptions allows you to process PK errors fully automatically; your application no longer needs to check the return values of the PK calls.
This section describes some unusual Parasolid errors that require special treatment, or arise under particular circumstances.
Parasolid has failed in an unexpected way. This error can arise when argument checking has been turned off and incorrect data are passed to a PK function. |
Your application should proceed according to the severity reported. |
|
A fatal error has occurred; the Parasolid session has been so badly corrupted that you cannot make any further PK calls. This error can arise when run-time errors are processed by a signal handler; for details, see Chapter 121, “Signal Handling”. |
Your application must stop and restart the Parasolid session in order to make further PK calls. |
|
An unforeseeable error has occurred that Parasolid cannot diagnose and process internally; hence no specific information can be given in the PK_ERROR_sf_t structure (see Section 120.2.5, “PK_ERROR functions and their use” for details). This error can arise when run-time errors or user interrupts are processed by a signal handler. See Section 121, “Signal Handling” for details |
If your application uses a registered error handler with exceptions, it must throw an exception back to the application code. If the error can be dealt with, it should be possible to continue the Parasolid session afterwards (having called PK_SESSION_tidy). If your application does not use a registered error handler with exceptions, Parasolid will flush the output buffers, close open files and call exit() to terminate the current process. |
|
A run-time error in the Parasolid kernel is being processed by a signal handler; for details, see Chapter 121, “Signal Handling”. |
Your application should proceed according to the severity reported. |
|
PK_SESSION_abort has been called by a signal handler as a result of a user interrupt; for details, see Chapter 121, “Signal Handling”. |
Your application should proceed according to the severity reported. |
|
A user interrupt has been attempted during execution of a PK function that cannot be safely aborted; for details, see Chapter 121, “Signal Handling”. |
No specific recovery action is required in your application. |
Therefore a further advantage of using a registered error handler with exceptions is that Parasolid can recover from the error code PK_ERROR_unhandleable_condition. See Chapter 121, “Signal Handling” for further details
Note: If you encounter a system error and argument checking was not enabled, you should first enable it with a call to PK_SESSION_set_check_arguments and try the operation again before investigating further. |
The error manipulation and enquiry functions available in Parasolid are described in the table below. They are used in situations where it is necessary to override the current error handler: for example, you might be developing a third-party plug-in for an existing Parasolid application, and have no knowledge of the master application’s error-handling strategy.
Note: When using multiple thread applications, it is recommended that you use PK_THREAD_ functions instead of PK_ERROR_ functions. See Section 113.2, “How to set up and use application threads” for a list of these functions. |
This function returns information about the last PK error raised. The information is returned (via the arguments) in a PK_ERROR_sf_t structure that includes:
If there is no error, or an error has been cleared (
|
|
This function clears the information about the last PK error raised. Subsequent calls to PK_ERROR_ask_last will not return any information until another error is raised. |
|
This function raises an (artificial) PK error, given as input in a PK_ERROR_sf_t structure. If your application has registered an error handler, it is invoked, and appropriate action taken according to the severity of the new error. If the error handler uses exceptions, then this function may not return; otherwise, it returns the error code corresponding to the new error. |
|
This function works in exactly the same way as PK_ERROR_raise, only it raises the last error that occurred (i.e., that which would be returned by PK_ERROR_ask_last).
The logical argument |
|
This function registers the error handler given as input in a PK_ERROR_frustrum_t structure. Your error handler must return a PK_ERROR_code_t and receive a pointer to a PK_ERROR_sf_t structure. A different error handler can be registered at any time simply by calling this function with an appropriate pointer. |
|
This function returns the current error handler as a pointer to a PK_ERROR_frustrum_t structure, if there is one. If not, it returns NULL. |
The example third-party function in Figure 120-8 illustrates how the PK_ERROR_xxx functions and PK_THREAD_ xxx functions can be used. This function makes several calls to PK functions, and is designed not to be disrupted by mild errors. The behaviour of the master application’s error handler is not known, so it first replaces the current error handler (if any) with its own by calling PK_THREAD_register_error_cbs. The new error handler throws an exception for serious and fatal errors but not mild errors, as shown in Figure 120-9.
PK_ERROR_code_t thirdPartyApp::functionThatMakesPKCalls( void ) { PK_ERROR_frustrum_t master_app_frustrum; PK_LOGICAL_t was_error; // main body of function wrapped in a ‘try’ block try { PK_ERROR_frustrum_t tp_app_frustrum; // save the frustrum from the master application PK_THREAD_ask_error_cbs( &master_app_frustrum ); // temporarily replace the current error handler //with ‘thirdPartyApp’ member function ‘handleError_NoMild’ tp_app_frustrum.handler_fn = &handleError_NoMild; PK_THREAD_register_error_cbs( tp_app_frustrum ); // clear the last error raised PK_THREAD_clear_last_error( &was_error ); // call PK functions PK_XXX_fun_1( arg1, ..., argn ); // no need to check the error return code each time PK_XXX_fun_2( arg1, ..., argm ); PK_XXX_fun_3( arg1, ..., argr ); ... // re-register the frustrum from the master application PK_THREAD_register_error_cbs( master_app_frustrum ); // check to see if a mild error occurred somewhere PK_ERROR_sf_t error_sf; PK_THREAD_ask_last_error( &was_error, &error_sf ); // alert the master application if an error occurred if (was_error) { // if here, the error is mild, //so there is no need to re-raise it return error_sf.code; } } ... // catch all exceptions (serious and fatal errors) here catch(...) { // re-register the frustrum from the master application PK_THREAD_register_error_cbs( master_app_frustrum ); // re-raise the error with the original error handler; // if it returns (or does not exist), return the error code return PK_ERROR_reraise( &was_error ); } // if here, no errors took place return PK_ERROR_no_errors; } |
Figure 120-8 Example of a third-party function that makes Parasolid calls
The third-party function clears the memory of the last error raised using PK_THREAD_clear_last_error, and then makes a series of calls to PK functions. If a serious or fatal error is encountered, the error handler throws an exception, and execution continues inside the
catch(...)
block in the function. Otherwise, execution continues in the
try
block; in each case, the master application’s error handler is re-registered.
If a mild error has occurred (as indicated by PK_THREAD_ask_last_error), the function returns the corresponding error code to the master application (for information), but does not raise an error. After a serious or fatal error, however, the function calls PK_ERROR_reraise to handle the error with the master application’s error handler (if one exists); if the error handler returns (or does not exist), the function returns the corresponding error code to the master application.
PK_ERROR_code_t thirdPartyApp::handleError_NoMild( PK_ERROR_sf_t* error_sf ) { // respond to serious and fatal errors only if (error_sf->severity != PK_ERROR_mild) { // tidy the session before throwing the exception PK_THREAD_tidy() throw error_sf->severity; } } |
Figure 120-9 Example of a third-party error handler that ignores mild errors
Warning: If you register an error handler with Parasolid, you must not call any of the PK_ERROR functions or PK_THREAD_equivalent functions described in this section from within the error handler. |
Some PK functions have the ability to return detailed diagnostic information following any failure to complete an operation in the manner intended. In these situations, it is not possible to return this information through the general PK_ERROR_sf structure; PK functions therefore return an error code of PK_ERROR_no_errors, and send error diagnostics back via a particular variable or structure among their output arguments known as a status code.
The documentation for each PK function indicates whether it may return failure information via a status code; if so, it tells you which variable to examine in order to determine the status (i.e., success or failure) of the operation.
Types of status code that are commonly used to indicate failures include:
(Please note that this is not a complete list.) This status code may appear explicitly among the output arguments of the function, or as a field within a larger data structure describing the results of the operation.
In many cases when a function returns a failure status code, the model has not been changed, and hence no Parasolid-specific action is required; this is analogous to situations in which PK functions return a mild error. In the case of local operations, however, it is possible that the model has been corrupted, and hence deleting it afterwards could lead to a run-time error. Specifically, the functions that may have corrupted the model after a failure are those that return a PK_local_status_t code.
Warning: If a function returns a failure status code of type PK_local_status_t, we recommend that you roll back to a valid state of the model, in order to avoid any possibility of a run-time error later. |
In addition, for boolean operations, the return of the failure status code PK_boolean_result_failed_c may indicate that extra entities have been produced, causing undesirable changes to the model. The data structures should not be corrupt, but a call to PK_BODY_check or PK_FACE_check may show the model to be invalid. Therefore we also recommend that if PK_boolean_result_failed_c is returned from a call to PK_BODY_boolean_2 or PK_FACE_boolean_2, the application should roll back to a valid state of the model.
If you are dealing with multiple threads in your application and a failure status code is returned, you should roll the session back. See Section 113.6.1, “Excluding application threads after a serious or fatal error”.
<<< Introduction To Error Handling | Chapters | Signal Handling >>> |