Error Handling   

<<< Session Support Chapters Glossary >>>

Contents

[back to top]


59.1 Introduction

This chapter tells you about the different types of error that your application might encounter, and how you can handle them. If you have not done so already, you might find it useful to read the introduction to error handling in the frustrum in the Getting Started With Parasolid manual before continuing with this chapter.

Your application is responsible for intercepting and handling error conditions that may arise when calling Parasolid. This may include any of the following types of error:

You must handle the first of these, and, depending on the design of your application, you may need to handle one or both of the others.

The way that you handle any given error depends on both the design of your application, and the type of error being handled.

If you are handling both user interrupts and RTEs, you can use the same signal handler for both types of error, if you wish.

You are highly recommended to implement rollback functionality in your application in order to tidy up a session after severe errors. See Chapter 41, "Partitions and Rollback", for details. The rollback functions are supplied to Parasolid using PK_DELTA_register_callbacks.

The rest of this chapter is structured as follows:

Further information

You can find more information about error handling in the following places:

[back to top]


59.2 Handling errors from Parasolid

Every PK function returns an error code as the function value to indicate the result of calling that function. If this value is PK_ERROR_no_errors (0) the function has completed successfully and your application can proceed.

If the return value is anything else, then the function has failed to complete. Each numerical value indicates a predictable error, and your application needs to take one of the following courses of action, depending on what that value is, and how severe the error is in this context:

 

Note: Sometimes, a function might "succeed" (that is, return PK_ERROR_no_errors), but the modeling operation itself might fail. In such cases, information about the failure is returned in a token, usually in the return structure for the function. This chapter does not deal with this type of modeling failure.

Choosing how to handle Parasolid errors

There are two approaches to handling errors from Parasolid.

The strategy you choose probably depends on how errors are handled by the rest of your application. If possible, you are recommended not to mix the two strategies, unless you never longjump in your application code.

Whichever strategy you choose, the same action needs to be taken. This action depends on the severity of the error, as supplied in the error standard form. See Section 59.2.3, "What to do when an error occurs" for more details.

Looking up PK error code values

The numerical values and meanings of PK error codes are listed in two formats in the PK Interface Programming Reference Manual (Part 2 - Interface Structures, Tokens & Error Codes):

In order to handle a given error, you must look up the appropriate error code in one of these lists and write your error handling code appropriately. There is no way to extract the meaning of a particular error code programmatically from the PK. You can also find PK error codes listed in the file parasolid_kernel.h .

 

Note: Error codes that are specifically associated with a given PK function are listed in the header documentation for that function as "specific errors". Lists of specific errors do not represent a complete list of all the errors that might be raised on that function.

[back to top]

59.2.1 Using a registered error handler

In order to use a registered error handler, you need to do the following:

Once registered, the error handler is automatically invoked if a PK function is about to return an error. The error handler is passed a standard structure (PK_ERROR_sf_t) containing the following data:

The error handler then takes appropriate action on the basis of this information. See Section 59.2.3, "What to do when an error occurs".

[back to top]

59.2.2 Letting the application check for non-zero error returns after each function call

The main difference between using a registered error handler and letting your application handle PK errors is that a registered error handler is invoked automatically by Parasolid before a non-zero error code is returned, whereas your code needs to examine error codes explicitly if no error handler is registered.

When you are not using a registered error handler, you need to do the following:

You should use PK_ERROR_ask_last and PK_ERROR_clear_last (see Section 59.2.4) to manage information about PK errors. As with a registered error handler, your code should then take appropriate action on the basis of this information. See Section 59.2.3, "What to do when an error occurs".

[back to top]

59.2.3 What to do when an error occurs

Although the general principles are the same, the precise details of what you need to do when a Parasolid error occurs depend on whether you use a registered error handler, and whether you choose to longjump on an error.

There are three error severity levels that your application has to handle:

 

Severity Current State Required Action

Fatal

Modeler memory has been corrupted; rolling back, if implemented, will not be effective. You may not be able to restart Parasolid

Your application should shut down Parasolid

Serious

The parts loaded in Parasolid may be corrupt

Your application should roll back to a valid state, or shut down Parasolid if rollback is not implemented. If neither occurs, then the data structure may be corrupted, leading to a crash, hanging, or other unpredictable behavior at some later stage.

Mild

The operation failed; the parts involved were not corrupted

Your application can continue with any Parasolid operation

The error severity is returned in the PK_ERROR_sf_t structure that is passed to the error handler. You can also use PK_ERROR_ask_last to determine the severity of a given error: see Section 59.2.4.

Using no error handler

If you decide to examine error codes in your application code, the sequence of events is as follows:

 

Figure 59-1 Handling a Parasolid error when there is no registered error handler

When no error handler is registered, the return value of every PK function call must be examined, and appropriate action taken if the result is anything other than PK_ERROR_no_errors. Calls to PK functions in your application code can be structured as shown in the pseudo-code below:

 

result = PK_function_1
if (result != PK_ERROR_no_errors)
    <handle error>

result = PK_function_2
if (result != PK_ERROR_no_errors)
    <handle error>

result = PK_function_3
if (result != PK_ERROR_no_errors)
    <handle error>

. . .

Using an error handler that returns to the function

If you register an error handler, and decide to return to the failing PK function whenever an error occurs, the sequence of events is as follows.

 

Figure 59-2 Handling a Parasolid error with a registered error handler that returns to the failing function

 

Note: You have to longjump when the error code is PK_ERROR_unhandleable_condition. You may also choose to longjump on other error codes if you wish.

When you register an error handler and then return to the failing PK function, you still need to check the return value of every PK function call. The main difference to the structure of your code between this strategy and using no error handler is that you do not need to explicitly handle every error in your application code. Calls to PK functions in your application code can be structured as shown in the pseudo-code below:

 

<register error handler>

result = PK_function_1

if (result == PK_ERROR_no_errors)
result = PK_function_2

if (result == PK_ERROR_no_errors)
result = PK_function_3

. . .

If you return to the failing PK function, then

Using an error handler that longjumps

If you register an error handler, and decide to longjump whenever an error occurs, the sequence of events is as follows.

 

Figure 59-3 Handling a Parasolid error with a registered error handler always longjumps

The advantage of this method is that it allows you to group calls to PK functions into meaningful clusters in your application. You do not need to explicitly examine the return value of each function in your application. Calls to PK functions in your application code can be structured as shown in the pseudo-code below:

 

<register error handler>

if (setjmp(&setjmp_buffer) == 0)

  { /* group of PK function calls */
    PK_function_1
    PK_function_2
    PK_function_3
  }

else
  { /* safe point - get here from error handler */
    <tidy-up code>
  }

 

Note: If you longjump out of Parasolid, then you must call PK_SESSION_tidy before the next PK call, either before or after longjumping. For more information, see Section 59.5, "Longjumping".

[back to top]

59.2.4 Useful error tracking tools

This section describes a number of error tracking tools that are particularly useful in the following cases:

 

Function Description

PK_ERROR_ask_last

This function returns information (using PK_ERROR_sf_t) about the last PK error that was raised. The information returned includes:

  • The name of the PK function that raised the error
  • The error code
  • The severity of the error
  • The numbers and names of any invalid arguments.

A registered error hander receives this information in a PK_ERROR_sf_t. You should use this function to request error information if handling PK errors in your application code.

PK_ERROR_clear_last

This function clears the saved information from the last PK error that was raised.

PK_ERROR_raise

This function raises an appropriate error when passed a PK_ERROR_sf_t standard form.

PK_ERROR_raise and PK_ERROR_reraise can be used together to temporarily suppress the standard application error, perform multiple PK calls to encapsulate a piece of functionality, then restore the original handler and (optionally) resignal a PK error to your application. You can use them when you want to perform calls to certain PK functions using your own error handler, while using the registered error handler for calls to other PK functions.

PK_ERROR_reraise

This function repeats the most recent PK error. It should be used in conjunction with PK_ERROR_raise to temporarily replace the registered error handler with your own error handler for certain calls, as described above.

PK_ERROR_ask_callbacks

Returns the current application error handler, if there is one.

[back to top]


59.3 User interrupt and RTE handling

As well as handling Parasolid errors, your application can also handle user interrupts and run-time errors (RTEs). Unlike Parasolid errors, this is not mandatory, so you can ignore this section if it is not relevant to your application design.

 

Note: If you wish to handle either user interrupts or run-time errors, you are strongly advised to register an error handler with Parasolid in order to deal with Parasolid errors.

You can provide one or more signal handlers to cope with user interrupts, various types of RTEs and, optionally, timers:

If you wish, you can use a single function to handle all these types of signal. Signal handling is specific to both the operating system and application language. Your application must register any signal handlers with the operating system. For applications written in C, please refer to the system documentation for signal-handling functionality in the C run-time library.

For both user interrupt and RTE handling, the basic principle is to use the signal handler to determine what state the kernel is in when the error occurs (using PK_SESSION_is_in_kernel), and then to take appropriate action in Parasolid and your application in order to recover gracefully from the user interrupt or RTE.

[back to top]

59.3.1 Protected and unprotected sections of code

In order to determine what action needs to be taken when a user interrupt or RTE occurs, it is important to find out what type of kernel code was executing at the time.

PK functions can be categorised as either lightweight or heavyweight.

Heavyweight functions can be further divided into unprotected and protected sections of code:

Lightweight functions consist of only unprotected sections.

PK_SESSION_is_in_kernel establishes whether the current function is lightweight or heavyweight, and whether the error occurred in a protected or unprotected section of code. This information determines what action Parasolid takes.

[back to top]

59.3.2 Available functions

A number of functions are available to help handle user interrupts and RTEs:

PK_SESSION_is_in_kernel

Signal handlers can call PK_SESSION_is_in_kernel to determine the kernel's state when a user interrupt or RTE occurs. This function returns the following information:

This information is used to determine exactly what action is needed to recover gracefully from the error.

PK_SESSION_abort

Signal handlers can call PK_SESSION_abort to abort the kernel function that is executing when a user interrupt or RTE occurs. It takes a reason argument that describes the type of error for which an abort has been requested:

 

Value Description

PK_abort_user_interrupt_c

Abort requested to handle user interrupt

PK_abort_runtime_error_c

Abort requested to handle runtime error

PK_abort_frustrum_error_c

Abort requested to handle error in frustrum

PK_SESSION_ask_function

Returns the name of the PK function currently being called.

[back to top]

59.3.3 Processing interrupts

Error returns from both user interrupt requests and RTEs are processed as shown in Figure 59-4. This figure illustrates the sequence of events that is common to both UI requests and RTEs. You should also refer to Section 59.3.4, "Handling user interrupts" or Section 59.3.5, "Handling run time errors", as appropriate for the type of error you are handling.

 

Figure 59-4 Processing the interrupt from a user interrupt request or an RTE

 

Note: If you handle interrupts, but have not registered an error handler for Parasolid errors, your application may exit if a signal is handled in an unprotected section of code. This is undesirable, especially in the context of a user interrupt. If you cannot register an error handler, an alternative is to detect interrupts in unprotected sections of code (using the initial call to PK_SESSION_is_in_kernel, shown in Figure 59-5 and Figure 59-6), and then ignore them.

[back to top]

59.3.4 Handling user interrupts

If a user interrupt occurs when execution is in the kernel or the frustrum, and you have provided a signal handler to deal with user interrupts, the error handling process is as shown in Figure 59-5. User interrupt requests are made by calling PK_SESSION_abort with the error token PK_abort_user_interrupt_c.

User interrupts requests are handled by Parasolid at the next safe point in code, rather than immediately. In principle, this could mean that a user interrupt may not be acknowledged for some time, leading to delays and frustration for the user. In order to avoid this, you should set a timer before calling PK_SESSION_abort. If this timer expires before a user interrupt request is handled, then you can force Parasolid to process the request immediately by signalling a run-time error. This is done by calling PK_SESSION_abort with the error token PK_abort_runtime_error_c: see Section 59.3.5, "Handling run time errors".

If you have not provided a signal handler, then any user interrupt requests are processed in whatever manner your application's run-time library support specifies. This could mean, for instance, that user interrupt requests are ignored, or that your application exits.

 

Figure 59-5 Handling a user interrupt in the Parasolid kernel

[back to top]

59.3.5 Handling run time errors

If a run-time error occurs when execution is in the kernel, and you have provided a signal handler to deal with RTEs, the error handling process is as shown in Figure 59-6. Requests to handle run-time errors are made by calling PK_SESSION_abort with the error token PK_abort_runtime_error_c. Unlike user interrupts, requests to handle RTEs are processed immediately.

If an RTE occurs when execution is in the frustrum, the procedure is the same as shown in Figure 59-6, except that PK_SESSION_abort is called with the token PK_abort_frustrum_error_c, and the error code that can be returned is PK_ERROR_fru_error.

If no RTE signal handler is provided, then an RTE results in the default behavior of the run-time system. This is likely to exit your application.

 

Figure 59-6 Handling an RTE in the Parasolid kernel

[back to top]


59.4 Dealing with re-entrant functions

If the failing function is a recursive (re-entrant) operation called from the GO, then:

 

Warning: PK_SESSION_is_in_kernel reports the protection status of the top-level function, not the failing re-entrant function, so take care when handling errors in re-entrant Parasolid operations.

[back to top]


59.5 Longjumping

If, when handling an error of any sort, your application longjumps out of Parasolid, it must call PK_SESSION_tidy. Doing this:

You can call PK_SESSION_tidy either before or after longjumping, but, unless you longjumped as a result of a mild error, you must call it before you call the next PK function (often a rollback command).

Many PK functions return information in an array whose space is allocated by Parasolid, together with the length of this array. If the function succeeds, your application should look at the array length to see if anything is returned. If the length is zero, then your application's pointer to the array should be set to NULL. If your application longjumps out of this function as a result of error handling, then the pointer to the array has an undefined value.

[back to top]


59.6 Examples

The examples in this section demonstrate how to handle a PK error by longjumping, and by returning to the function that has generated the error, respectively.

[back to top]

59.6.1 Using an error handler that long jumps

 

#include <setjmp.h>
#include <stdio.h>
#include "parasolid_kernel.h"

jmp_buf jmp_label_g;
  /* saved context to longjump to in application code */

PK_ERROR_code_t longjump_error_handler( PK_ERROR_sf_t * error_sf )
{
  /* insert application-specific tidying code here */
  PK_ERROR_code_t session_tidy_result = PK_SESSION_tidy();
  int jmp_code = (session_tidy_result == PK_ERROR_no_errors ? 1 : 2);
    /* if PK_SESSION_tidy returns an error, something is */
    /* seriously corrupted, and the application should */
    /* restart Parasolid */
  longjmp(jmp_label_g, jmp_code);
}

/* start of a sequence of possibly complex */
/* Parasolid operations                    */
PK_MARK_t mark;
error = PK_MARK_create(&mark); 
                     /* set a mark to rollback to on failure */
PK_ERROR_frustrum_t error_frustrum;
error_frustrum.handler_fn = longjump_error_handler;
PK_ERROR_register_callbacks(error_frustrum);

if ((jmp_result = setjmp(&jmp_label_g)) == 0)
  {
  PK_ATTRIB_create_empty(entity, attdef, &attrib);
  /* ... successful execution continues here,  */
  /* including other calls of PK functions ... */
  }
else
  {
  /* The error handler will longjump to here if an    */
  /* error occurs in any of the PK calls made above.  */
  if (jmp_result == 2)
    {
    /* severe error, perhaps longjump again to higher level? */
    }
  else
    {
    printf("Rolling back to before <xxx> operation");
    PK_MARK_goto(mark);
    }
  }

[back to top]

59.6.2 Using an error handler that doesn't long jump

 

#include <stdlib.h>
#include "parasolid_kernel.h"

PK_ERROR_code_t simple_error_handler(PK_ERROR_sf_t * error_sf)
{

/* There are some classes of errors for which local recovery */
/* is not possible, or at least not simple. These are handled*/
/* here by printing a message for the user and then exiting. */
/* More complex error recovery would in general require a    */
/* longjumping handler as described in Section 59.6.1.       */

  switch (error_sf->code)
  {
  case PK_ERROR_aborted:  /* user interrupt */
  case PK_ERROR_run_time_error:
  case PK_ERROR_system_error:
  case PK_ERROR_fatal_error:
  case PK_ERROR_unhandleable_condition:
  /* etc. */
    print_error_info
       ("About to exit after error %d\n", (int)error_sf->code);
    exit(EXIT_FAILURE);
  default:
    /* insert application-specific tidying code here */
    return error_sf->code;
  }
}

/* somewhere in the application initialization code ... */
PK_ERROR_frustrum_t error_frustrum;
error_frustrum.handler_fn = simple_error_handler;
PK_ERROR_register_callbacks(error_frustrum);
/* ... */

PK_ERROR_code_t result = PK_ATTRIB_create_empty(entity, attdef, &attrib);
if (result != PK_ERROR_no_errors)
  {
  /* Check the error code, and possibly severity (eg by   */
  /* calling PK_ERROR_ask_last), and take the appropriate */
  /* action. Errors which cannot be handled locally have  */
  /* already been dealt with */
  }

 

[back to top]

<<< Session Support Chapters Glossary >>>