ObjectStore C++ API Reference

Appendix A

Exception Facility

ObjectStore supports the use of two kinds of exceptions:

C++ exceptions are a part of the C++ language supported by some C++ compilers. On any platform whose compiler supports exceptions, you can signal and catch C++ exceptions in ObjectStore applications.

TIX exceptions are part of the ObjectStore application programming interface. ObjectStore API functions sometimes signal predefined TIX exceptions when error conditions arise, and your programs can handle these exceptions with macros and member functions provided by ObjectStore. These predefined exceptions are listed in Appendix B, Predefined TIX Exceptions.

In addition, you can

Macros

The macros described below provide a means of handling TIX exceptions, and defining and declaring variables of type tix_exception (see tix_exception). They also provide a way of specifying a handler for a TIX exception signaled by a given block of code.

For an example involving signaling and handling TIX exceptions, see Sample TIX Exception-Handling Routine.

TIX_HANDLE, TIX_EXCEPTION, and TIX_END_HANDLE

Three macros are used in conjunction to specify a handler for an exception signaled by a given code block:

TIX_HANDLE ( identifier)
       statement-1... statement-n 
TIX_EXCEPTION
       statement 
TIX_END_HANDLE
identifier is the name of the TIX exception to be handled. This should not be err_internal - you cannot handle ObjectStore internal errors.

statement-1 ... statement-n are considered to be within their own block, as is statement. This code sequence indicates that if the exception identifier (or a descendent of it - see tix_exception) is signaled while control is dynamically within the block containing statement-1... statement-n, control is transferred to statement, unless another appropriate handler for the exception is found first (see tix_exception, tix_exception::signal()). If no exception is signaled, then, when the block containing statement-1... statement-n completes, control flows to the statement immediately following TIX_END_HANDLE.

Example
extern char db_name_buf[];
extern os_database *db;
void input_and_open()
{
      db = 0;
      while (!db) {
            get_db_name(db_name_buf) ;
            TIX_HANDLE (err_database_not_found) 
                  db = os_database::open(db_name_buf);
            TIX_EXCEPTION
                  printf("Sorry, no such database. Try again.\n");
            TIX_END_HANDLE
      }
}
The call to the ObjectStore-supplied function os_database::open() can result in signaling err_database_not_found. In such a case, the call does not return, but rather control is transferred nonlocally to the printf() call, which constitutes the exception handler. Once the handler has been executed, control flows to the top of the loop.

These constructs can be nested, allowing handling of multiple exceptions. For example:

TIX_HANDLE ( exc1)
      TIX_HANDLE ( exc2)
             statements 
      TIX_EXCEPTION
             handler statement for exc2 
      TIX_END_HANDLE
TIX_EXCEPTION
       handler statement for exc1 
TIX_END_HANDLE

TIX_HANDLE_IF

TIX_HANDLE_IF ( flag,  identifier)
       statement-1... statement-n 
TIX_EXCEPTION
       statement 
TIX_END_HANDLE
TIX_HANDLE_IF is similar to TIX_HANDLE, but it takes two arguments: flag and identifier. flag should be a C++ Boolean expression, as would be specified in if or when statements. If the expression evaluates to true (nonzero), the handler is established around the body; otherwise, no handler is established. identifier identifies the exception, just like the TIX_HANDLE argument.

DEFINE_EXCEPTION

#define DEFINE_EXCEPTION(name, message, pointer_to_parent)\
objectstore_exception name("name", message, pointer_to_parent)
To define a TIX exception, you call the macro DEFINE_EXCEPTION() as follows:

      DEFINE_EXCEPTION( name,  message,  pointer-to-parent)
name is the name (not in quotation marks) of the exception to be created.

message is a string to be printed when the exception is raised but there is no handler to catch it. Use quotation marks for this argument.

pointer-to-parent is the address of the existing exception you want to serve as the new exception's parent. Supply 0 if you do not want the exception to have a parent.

                                          DEFINE_EXCEPTION(bad_char_input, "BAD CHARACTER", &bad_input)

DECLARE_EXCEPTION

#define DECLARE_EXCEPTION(name) \
extern objectstore_exception name
This macro is used to declare a variable of type objectstore_exception (see tix_exception).

Classes

The exception facility provides five classes: objectstore_exception, tix_exception, tix_handler, basic_undo, and tix_context.

The types os_int32 and os_boolean, used throughout this manual, are each defined as a signed 32-bit integer type. The type os_unsigned_int32 is defined as an unsigned 32-bit integer type.

objectstore_exception

When you use the macro DEFINE_EXCEPTION or DECLARE_EXCEPTION you define or declare a variable whose type is objectstore_exception. The class tix_exception is a base class of objectstore_exception, which inherits several useful functions from it.

objectstore_exception::objectstore_exception()

objectstore_exception(
      char *name, 
      char const *message, 
      tix_exception const *exc
);
Returns a newly constructed instance of objectstore_exception. name is the name of the exception. message is a message to be printed if the exception is raised but not handled. exc specifies the new exception's parent.

objectstore_exception::signal()

void signal(char const *format ...);
Signals the TIX exception for which the function is called. The arguments work as with the function printf(). A format string is supplied, followed by any number of additional arguments. Control is transferred to the most recently established handler for the exception or one of its ancestors. If there is no such handler, the exception's associated message is issued, together with the message specified by format and associated arguments. The process is then aborted or exited from, as determined by the environment variable OS_DEF_EXCEPT_ACTION (see ObjectStore Management).

void signal(os_int32 value, char const *format ...);
Signals the TIX exception for which the function is called. The first argument can be used to associate an error code with the signaling of the exception. The value can be retrieved with the function tix_handler::get_value(). The subsequent arguments work as with the function printf(). A format string is supplied, followed by any number of additional arguments. Control is transferred to the most recently established handler for the exception or one of its ancestors. If there is no such handler, the exception's associated message is issued, together with the message specified by format and associated arguments, and the process is aborted.

objectstore_exception::vsignal()

void vsignal(char const *format, va_list args);
Signals the TIX exception for which the function is called. The arguments work as with the function vprintf(). A format string is supplied, followed by a va_list. Control is transferred to the most recently established handler for the exception or one of its ancestors. If there is no such handler, the exception's associated message is issued, together with the message specified by format and associated arguments. The process is then aborted or exited from, as determined by the environment variable OS_DEF_EXCEPT_ACTION (see ObjectStore Management).

void vsignal(os_int32 value, char const *format, va_list args);
Signals the TIX exception for which the function is called. The first argument can be used to associate an error code with the signaling of the exception. The value can be retrieved with the function tix_handler::get_value(). The subsequent arguments work as with the function vprintf(). A format string is supplied, followed by a va_list. Control is transferred to the most recently established handler for the exception or one of its ancestors. If there is no such handler, the exception's associated message is issued, together with the message specified by format and associated arguments, and the process is aborted.

tix_exception

Every TIX exception is an instance of tix_exception, and represents a particular type of error condition or exceptional circumstance. Every exception has a parent, except the exception err_objectstore (see Appendix B, Predefined TIX Exceptions). The parent of a given exception is an ancestor of that exception. The parent of any ancestor of a given exception is also an ancestor of that exception. If one exception is an ancestor of another, the other exception is said to be a descendent of the first.

tix_exception::ancestor_of()

os_int32 ancestor_of(tix_exception const *e);
Returns nonzero if this is an ancestor of e, and 0 otherwise. Any parent of e is an ancestor of e, and any parent of an ancestor of e is also an ancestor of e. In addition, every exception is considered to be its own ancestor, so if this is the same as e, the function returns nonzero.

tix_exception::set_unhandled_exception_hook()

static tix_unhandled_exception_hook_t 
      set_unhandled_exception_hook(
      tix_unhandled_exception_hook_t new_hook
);
Registers a function that is called if a tix_exception is unhandled. I You must be sure not to access persistent memory while in err_deadlock when this function is in use. If this happens it can lead to a recursive exception error message.

tix_handler

A tix_handler carries information about the particular exceptional circumstances leading to the signaling of the current exception.

tix_handler::get_current()

static tix_handler *get_current();
Returns the tix_handler for the current exception.

tix_handler::get_exception()

static tix_exception *get_exception();
Returns the current TIX exception.

tix_handler::get_report()

static char *get_report();
Returns the format string used to signal the current exception. The string is suitable for further processing or writing to a file, as opposed to being displayed on a screen. See also tix_handler_get_report0().

tix_handler::get_report0()

static char *get_report0();
Returns the format string used to signal the current exception. The string is suitable for displaying on a screen. See also tix_handler_get_report().

tix_handler::get_value()

static os_int32 get_value();
Returns the error code associated with the current exception.

basic_undo

In C++, the destructor for an automatic object is run whenever a stack frame containing it is unwound. When a C++ exception is signaled and control is transferred to a handler in an enclosing scope, the intervening stack frames are unwound. When a TIX exception is signaled and control is transferred to a handler in an enclosing scope, the intervening stack frames are unwound on some platforms, but not on others.

On the following platforms, the intervening stack frames are unwound:

On other platforms, you can derive a class from basic_undo to partially simulate the unwinding of the intervening stack frames. ObjectStore runs the destructors for instances of basic_undo contained in those stack frames.

The destructor for basic_undo is virtual. So the destructor for instances of classes derived from basic_undo is run when the unwinding is simulated. The destructors for these derived classes can perform any desired undo processing. You must create an instance of the derived class in the desired scope before an exception can be raised. Note that this object must be stack-allocated, not heap-allocated. The object's destructor will be run whenever the signaling of an exception transfers control out of or through its stack frame. (Of course, the destructor will also be run if the block is exited from normally.)

A class derived from basic_undo must have a virtual destructor if (and only if) further classes are derived from it.

basic_undo::basic_undo()

basic_undo();
Constructs a new instance of basic_undo.

basic_undo::exception

protected: tix_exception *exception;
Has as value a pointer to the TIX exception currently being signaled. If no exception is being signaled - that is, the block is being exited from normally - the value is 0. Your destructor can access this to learn what is going on.

basic_undo::~basic_undo()

virtual ~basic_undo();
This destructor is invoked whenever a frame is unwound by tix_exception::signal(). Overloadings of it defined by derived classes can perform undo processing. This function is also invoked in the normal way upon block exit.

tix_context

The class tix_context makes error messages more informative by providing contextual information.

In the following example, if you get the error Connection attempt timed out, you are also told that the error occurred while you were trying to create that database.

      { tix_context tc1("While creating database %s\n", the_pathname); 
server->create_database(the_pathname);
}
If any TIX exception is signaled during the execution of server->create_database, and the exception is not handled inside the server->create_database, the error message associated with the error will be extended to include the string "While creating database /a/b/c" at the end.

tix_context works as follows: when an exception is signaled, as the signaler moves up the stack searching for a handler, if it encounters a tix_context, it runs sprintf on the arguments, and adds this string to the end of the error message. If it does finally find a handler, the string returned by the "report" function member (and "report0") will include the context messages of all tix_context objects that were on the stack between the point where the exception was signaled and the point where it was handled. If no handler is found, the message that is issued will include the context messages of all tix_context objects that were on the stack.

The tix_context constructor has one required argument, a printf control string, and up to four additional arguments, which must all be strings. The printf control string should have as many occurrences of "%s" as there are arguments. You can only use strings and "%s", and no more than four.

C++ requires that you include a variable name for the tix_context object, such as tc1 in the example above.

Note that the sprintf is done only if the exception is actually signaled, so that establishing a tix_context that never gets used incurs little overhead.

The tix_context constructor does not copy any of the strings it is given, so if there is any chance that those strings might get altered during the execution of the scope of the tix_context, you should probably make copies. It is a good idea to delete them manually when you are done with them.

The current limit on the length of the report string is 4096 characters.

os_lock_timeout_exception

Instances of this class contain information on the circumstances preventing acquisition of a lock within a specified timeout period. An exception of this type can be signaled by processes that have called the set_readlock_timeout() or set_writelock_timeout() member of os_segment, os_database, or objectstore. A pointer to an instance of this class can be returned by objectstore::acquire_lock().

os_lock_timeout_exception::get_application_names()

char **get_application_names();
Returns an array of strings naming the applications preventing lock acquisition.

os_lock_timeout_exception::get_fault_addr()

void *get_fault_addr();
Returns the address on which ObjectStore faulted, causing the database access leading to the attempted lock acquisition.

os_lock_timeout_exception::get_hostnames()

char **get_hostnames();
Returns an array of strings naming the host machines running the applications preventing lock acquisition.

os_lock_timeout_exception::get_lock_type()

os_lock_type get_lock_type();
Returns an enumerator (os_read_lock or os_write_lock) indicating the type of lock ObjectStore was requesting when the timeout occurred. os_lock_type is a top-level enumeration.

os_lock_timeout_exception::get_pids()

typedef os_unsigned_int32 os_pid ;
os_pid *get_pids();
Returns an array of integers indicating the process IDs of the processes preventing lock acquisition.

os_lock_timeout_exception::number_of_blockers()

os_int32 number_of_blockers();
Returns the number of processes preventing lock acquisition.

Sample TIX Exception-Handling Routine

#include <stdlib.h>
#include <ostore/except.hh>
#include <iostream.h>
/* ObjectStore macro DEFINE_EXCEPTION takes three arguments:
            The formal name of the exception
            The generic message [a literal string] printed when the
exception is raised and there is no handler to catch it.
The parent exception ( = 0 when no parent); */ DEFINE_EXCEPTION(illegal_input_char, "ILLEGAL INPUT CHARACTER", 0); main() { char c = `\0'; while( c != `q' && c != `Q' ) { cout << "Enter vowel: " ; cin >> c; TIX_HANDLE (illegal_input_char) switch(c) { case `q': case `Q': break; case `A': case `a': case `E': case `e': case `I': case `O': case `o': case `U': case `u': cout << "You have entered character: " << c <<"\n"; break; default: illegal_input_char.signal(""); break; } TIX_EXCEPTION cout << "Only vowels are legal. "; /* as many statements as you may choose to write are
allowed here */
cout << "Try again\n"; TIX_END_HANDLE } c = `\0'; /* reset c */ while( c != `q' && c != `Q' ) { cout << "Enter consonant: " ; cin >> c; switch(c) { case `q': case `Q': break; case `A': case `a': case `E': case `e': case `I': case `O': case `o': case `U': case `u': illegal_input_char.signal("\tInput character `%c' not a consonant\n", c); break; default: cout << "You have entered character: " << c << "\n"; break; } } }



[previous] [next]

Copyright © 1997 Object Design, Inc. All rights reserved.

Updated: 03/31/98 17:34:53