2. Interacting with GemBuilder

Previous chapter

Next chapter

This chapter describes how GemBuilder C functions are used to interact between a client application using a C API and the GemStone server’s Smalltalk environment.

Session Control
Logging in to acquire a session, and committing and aborting.

Representing Objects in C
How Smalltalk objects are handled in C and GemStone server data types.

Performing work in GemStone
How to execute code in GemStone or send messages to GemStone objects.

Structural Access to Objects
How to update GemStone objects by fetching from and storing to objects according to their implementation.

Nonblocking Functions
Using the non-blocking GCI calls

Operating System Considerations for C Applications
How to handle requirements from the operating system.

Error Handling and Recovery
Handling Errors in your application

2.1  Session Control

All interactions with the GemStone repository monitor occur within the scope of a user’s GemStone session, which may encapsulate one or more individual transactions. GemBuilder provides functions for obtaining and managing GemStone repository sessions, such as logging in and logging out, committing and aborting transactions, and connecting to a different session.

Prepare for Login

Before you can login, you need a running GemStone repository Stone and, usually, a NetLDI process. Setting up the repository and NetLDI, the options for RPC vs. linked logins, and the login parameters to login a Gem session process for either kind of login, is described in the System Administration Guide for GemStone/S 64 Bit.

While linked logins may have performance advantages under some circumstances, RPC logins have advantages, and are required when the application is on a different node than the Stone, and RPC logins avoid the risk of invalid memory references causing repository corruption. While performing development and debugging you should log in RPC, to avoid the risk of repository corruption. This discussion will primarily describe the process for RPC logins.

As described in the System Administration Guide for GemStone/S 64 Bit, you will need to determine the following:

  • Network Resource Syntax (NRS) for the Stone. By default, this is gs64stone.
  • NRS for the Gem. This is not set for a linked login, and may be ’gemnetobject’ for a local RPC login.
  • Authentication information for the Stone (GemStone userId and password).
  • Authentication information for the NetLDI (UNIX username and password). This is not required if the NetLDI is running in guest mode.

Logging In and Out

The functions GciInitAppName and GciInit initialize GemBuilder. When it is used, your application should call GciInitAppName before calling GciInit. Your C application must not call any GemBuilder functions other than this until it calls GciInit.

The GciSetNet or GciSetNetEx functions set the values for the Stone and Gem NRS, and NetLDI authentication information if required.

Then, to do any useful repository work, you must create a session with the GemStone system by calling GciLogin, GciLoginEx, or GciNbLoginEx; these functions have arguments for providing the GemStone username and password.

The *Ex variants support encrypting passwords before passing these from the GCI application to the Gem, which is useful if you are operating over an unsecure network. Encryption is done using the GciEncrypt function.

If your application calls GciLogin again after you are already logged in, GemBuilder will create an additional, independent, GemStone session for you. Multiple sessions can be attached to the same GemStone repository, or they can be attached to different repositories. The maximum number of sessions that may be logged in at one time depends upon your version of GemStone and the terms of your license agreement.

From the point of view of GemBuilder’s classic API, only a single session is active at any one time. It is known as the current session. Any time you execute code that communicates with the repository, it talks to the current session only. Other sessions are unaffected.

Each session is assigned a number by GemBuilder as it is created. Your application can call GciGetSessionId to inquire about the number of the current session, or GciSetSessionId to make another session the current one. The GemBuilder for C functions operate on the current session, and if you login multiple sessions, your application is responsible for treating each session distinctly.

An application can terminate a session by calling GciLogout. After that call returns, the current session no longer exists.

Transaction Management

Committing a Transaction

The GemStone repository proceeds from one stable state to the next by committing a series of sequentialized transactions. In Smalltalk, the message System commitTransaction attempts to commit changes to the repository. Similarly, when your C application calls the function GciCommit, GemStone will attempt to commit any changes to objects occurring within the current session.

A session within a transaction views the repository as it existed when the transaction started. By the time you are ready to commit a transaction, other sessions or users may have changed the state of the repository through intervening commit operations. If other sessions have made changes, your attempt to commit may conflict, and the commit will therefore fail. If an attempt to commit fails, your application must call GciAbort to discard the transaction.

As mentioned earlier, if your client code has created any new objects or has modified any objects whose representation you have imported, those objects must be exported to the GemStone repository in their new state before the transaction is committed. This ensures that the committed repository properly reflects the intended state.

Aborting a Transaction

By calling GciAbort, an application can discard from its current session all the changes to persistent objects that were made since the last successful commit or since the beginning of the session (whichever is later). This has exactly the same effect as sending the Smalltalk message

System abortTransaction.

After the application aborts a transaction, it must reread any object whose state has changed.

Controlling Transactions Manually

Under automatic transaction control, which is the default, a transaction is started when a user logs in to the repository. The transaction then continues until it is either committed or aborted. The call to GciAbort or GciCommit automatically starts a new transaction when it finishes processing the previous one. Thus, the user is always operating within a transaction.

Automatic transaction control is the default control mode in GemStone. However, there is some overhead associated with transactions that an application can avoid by changing the transaction mode to manualBegin. For example:

// set aSession in manual transaction mode
GciExecuteStr("System transactionMode: #manualBegin", OOP_NIL);
  
// set aSession in automatic transaction mode
GciExecuteStr("System transactionMode: #autoBegin", OOP_NIL);

In manual mode, the application starts a new transaction manually by calling the GciBegin function. The GciAbort and GciCommit functions complete the current transaction, but do not start a new transaction. Thus, they leave the user session operating outside of a transaction, without its attendant overhead. The session views the repository as it was when the last transaction was completed, or when the mode was last reset, whichever is later.

Since automatic transaction control is the default, a transaction is always started when a user logs in. To operate outside a transaction initially, an application must explicitly set the mode to manualBegin.

For a list of functions that are useful for managing sessions, see Table 7.1.

2.2  Representing Objects in C

The GemStone data model provides each object with an object-oriented pointer (OOP), a 64-bit integer. This OOP preserves an object’s identity distinct from its state.

In your client application, the OOP is represented in variables of type OopType. Whenever the client attempts to access or modify the state of a GemStone object, it uses the OOP to identify that object. Both the OOP and a representation of the object’s state may be imported into the client application.

The GemBuilder include file gci.ht defines type OopType, along with other types used by GemBuilder functions. For more information, see Chapter 6, “GemBuilder for C Files and Data Structures”.

Your C program can only use predefined OOPs, or OOPs that it has received from the GemStone. Your C program cannot create new OOPs directly — it must ask GemStone to create new OOPs for it.

The state of the GemStone object—the data in its instance variables—can be imported into the C application in a number of ways, depending on the specific type of data.

GemStone-Defined Object Macros

The GemBuilder include file gcioop.ht defines macros for a number of frequently-used objects, such as the GemStone objects nil, true, and false, and kernel classes in the GemStone repository.

In addition, it defines OOP_ILLEGAL. This represents a value that will never be used to represent any object in the repository. You can thus initialize the state of an OOP variable to OOP_ILLEGAL, and test later in your program to see if that variable contains valid information.

GemStone Object Implementation Types

Objects in GemStone can be divided up into four different types, based on the internal implementation. These are handled and structure internally differently, and handled differently by some GemBuilder functions. These types are Special, Byte, Pointer, and NSC.

More detail is provided in the GemStone Programming Guide.

Special

Special objects are those in which the OOP itself encodes the value. These objects are fast, canonical (each is identical to another of the same value), and do not use repository space. Specials include:

  • SmallInteger objects (for example, the number 5)
  • SmallDouble objects (for example, the number 0.5)
  • SmallFraction objects (for example, the number 1/3)
  • Character (for example, the letter ‘b’)
  • Boolean values (true and false)
  • The instance of class UndefinedObject (nil)
  • SmallDate
  • SmallTime
  • SmallDateAndTime
  • Customizable specials Special56bit0 through Special56bit15

For numeric and time related spcials, the specials itself represents a limited range of values, with another class, such as LargeInteger, Double, Date, etc., representing objects outside the range of the special.

Instances of specials cannot be modified. Conversion between a special and a C data type does not require making a call to the GemStone server.

Byte

Byte classes are collection classes (classes with indexed, unnamed instance variables) that contain objects that use one byte of storage space. The most obvious is String. Strings that require more than one byte of storage or individual characters are byte objects, but two or more bytes are logically required for elements stored in the collection. Byte objects do not have named instance variables.

Pointer

Pointer classes are collection classes that contain objects (such as Array) that have an internal ordering, and ordinary classes with instance variables. Pointer objects use 8 bytes of space per element.

NSC

NSC - Non-sequential collection, instances of the Smalltalk class UnorderedCollection - are stored internally in specialized leaf structures, are unordered, and may also have instance variables.

GemStone Data Types

When moving data between GemStone and your C application, the GemStone object representation must be converted to structures consisting of C data types.

For basic data types such as integers, floating points, and strings, you must convert between the OOP representation in GemStone and the basic C data type.

GemBuilder provides a number of functions that allow you to test for the type of data and perform conversion. These are listed in Functions and Macros for Testing and Converting Objects and Values.

In Example 2.1, assume that you have defined a Smalltalk class called Address that represents a mailing address. If the class has five instance variables, the OOPs of one instance of Address can be imported into a C array called address. Assume that the fifth instance variable represents the zip code of the address.

The fifth element of address is the OOP of the SmallInteger object that represents the zip code, not the zip code itself. Example 2.1 imports the value of the zip code object to the C variable zip.

Example 2.1 

int64 example_zipcode(OopType addressId)
{
  // returns the zipcode or -1 if an error occurred, 
 
  enum { addr_num_instVars = 5 };
 
  OopType instVars[addr_num_instVars];
 
  int numRet = GciFetchOops(addressId, 1, instVars, addr_num_instVars);
  if (numRet != (int)addr_num_instVars)
    return -1;
 
  BoolType conversionError = FALSE;
  int64 zip = GciOopToI64_(instVars[4], &conversionError);
  if (! conversionError)
    return -1;
 
  // zip now contains an integer that has the same
  //  value as the GemStone object represented by address[4] 
 
  return zip;
}
 

Integers

Integer types in Smalltalk are either SmallIntegers, which have the range -260 to 260 -1, or LargeIntegers. SmallIntegers are specials, but LargeIntegers have OOPs and must be resolved using a GemStone call.

For convenience, gcioop.ht defines a number of SmallInteger mnemonics, including OOP_Zero, OOP_One, OOP_Two, and OOP_MinusOne. See gcioop.ht for all mnemonics.

Floating Points

Floating point types in Smalltalk are either SmallDouble or Floats. SmallDoubles are specials and can represent C doubles that have value zero or that have exponent bits in range 0x381 to 0x3ff, which corresponds to about 5.0e-39 to 6.0e38; approximately the range of C 4-byte floats. While SmallDouble are specials, Float have OOPs and must be resolved using a GemStone call.

Byte-Swizzling of Binary Floating-Point Values

If an application is running on a different machine than its Gem, the byte ordering of binary floating-point values may differ on the two machines. To ensure the correct interpretation of non-special floating-point objects when they are transferred between such machines, the bytes need to be reordered (swizzled) to match the machine to which they are transferred.

Binary floats other than SmallDouble are represented in GemStone as 8-byte instances of Float or 4-byte instances of SmallFloat. The programmer must supply all the bytes, or provide a C double, to create or store a Float or SmallFloat.

Creating Floats or SmallDoubles using GciFltToOop or Gci_doubleToSmallDouble and fetched using GciOopToFlt avoids byte order problems.

Most GemBuilder functions provide automatic byte swizzling for instances of Float and SmallFloat, or raise an error. GciFetchBytes_ does not raise an error, but it also does not provide automatic byte swizzling. It is intended primarily for use with other kinds of byte objects, such as strings.

Other Numeric Types

Smalltalk also includes a number of other non-integer numerical types, including SmallFraction and Fraction, DecimalFloats, ScaledDecimals, and others. These require additional effort to represent in C, similar to how application objects are handled.

Character

Character types in Smalltalk are specials, with the full Unicode range. Conversions to C are to int types to allow the full range of Unicode characters.

Boolean

Booleans—true and false—are specials in Smalltalk. GemStone uses BoolType to represent these in C. true and false defined by the macros OOP_TRUE and OOP_FALSE.

nil

The Smalltalk special nil is an instance of the class UndefinedObject and has the non-zero value defined by the macro OOP_NIL.

Strings

GemStone provides a number of classes to support working with collections of characters, particularly to support UTF-8 encoding.

  • Traditional strings include String (Characters requiring 8 bits), DoubleByteString (16 bits), and QuadByteString (32 bits).
  • Unicode Strings rely on the ICU libraries, and include Unicode7 (7 bits), Unicode16 (16 bits) and Unicode32 (32 bits).
  • Traditional and Unicode strings can be encoded to UTF-8 and put into instances of Utf8, a specialized subclass of ByteArray.
  • Symbols are canonical CharacterCollections, and include Symbol, DoubleByteSymbol and QuadByteSymbol.

GemBuilder provides the following functions to create a variety of String object from a C string, and to fetch the contents. See Functions for Creating and Initializing Objects and Structural Access Functions and Macros.

Object Creation

The following GemBuilder functions allow your C program to create one or more new instances of one or more Smalltalk classes:

GciNewOop

GciNewOops

GciNewOopUsingObjRep

Once your application has created a new object, it can export the object to the repository by making sure some committed object in the repository references the new object. For example, add the object to a collection, or set the value of an instance variable in an existing object. This can be done with a call to one of the GciStore... functions.

Your C application may also create a new object by executing Smalltalk code, and create the reference from a persistent object in Smalltalk code.

Once the new object is created and the reference is committed, the object has been exported to the repository.

2.3  Performing work in GemStone

GemBuilder provides functions that allow C applications to execute Smalltalk code in the repository, and to send messages directly to GemStone objects.

You can also compile Smalltalk methods using GciCompileMethod, GciClassMethodForClass or GciInstMethodForClass; and the function GciStoreTravDoTravRefs_ includes a way to perform code execution.

Functions to perform Smalltalk code execution include:

Table 2.1 Summary of functions to execute in Smalltalk

GciExecute* and GciNbExecute*

Execute Smalltalk code that is contained in a GemStone string object.

GciExecuteStr* and GciNbExecuteStr*

Execute Smalltalk code that is contained in a C string.

GciPerform* and GciNbPerform*

Send a message to a GemStone Object

Executing Smalltalk Code in GemStone

Your C application can perform Smalltalk code execution using the GemBuilder functions since as GciExecute*and GciExecuteStr*.

The Smalltalk code may be a message expression, a statement, or a series of statements; any self-contained section of code that you could, for example, execute within a Topaz run command. The code can be in a GemStone string object and executed using a GciExecute function, or a C string and performed using a GciExecuteStr function.

These functions expect a SymboList, which is used to bind any symbols contained in the Smalltalk source. If the symbol list is OOP_NIL, GemStone uses the symbol list associated with the currently logged-in user.

Example 2.2 demonstrates the use of GciExecuteStr.

Example 2.2 

OopType example_execute(void)
{
  // Pass the String to GemStone for compilation and execution. 
  // If it succeeds, return the result of the expression 
  // otherwise OOP_NIL will be returned.
  
   OopType result = GciExecuteStr("Globals keys size", OOP_NIL);
   return result; 
}
 
 

Your Smalltalk code has the same format as a method, and may include temporaries. You may include a caret in the smalltalk code to indicate an explicit return, but this is not required. GemStone returns the result of the last Smalltalk statement executed.

There are a number of variants of GciExecute, including ones that return specific types of results, and nonblocking variants. See Functions for Compiling and Executing Smalltalk Code in the Database

Sending Messages to GemStone Objects

To send a message to a GemStone object, use GciPerform or one of its variants. GciPerform takes a object, a selector, and arguments; when GemStone receives this, it invokes and executes the method associated with that message.

Code execution occurs in the repository server, not in the application.

The following example shows two statements, one using GsExecuteStr and the other using GsPerform.

Example 2.3 

void example_messageSends(void)
{
   OopType userGlobals = GciResolveSymbol("UserGlobals", OOP_NIL);
   OopType aKey = GciNewSymbol("myNumber"); 
   OopType aValue = GciI32ToOop(55);
 
   OopType argList[2];
   argList[0] = aKey;
   argList[1] = aValue;
 
  // both the following have the same effect 
  
   GciExecuteStr("UserGlobals at: #myNumber put: 55", OOP_NIL);
  
   GciPerform(userGlobals, "at:put:", argList, 2);
}
 
 

GciPerform is useful when your C code already has GemStone objects that it is manipulating, and when you have specific selectors and arguments to send.

Interrupting GemStone Execution

You can interrupt GemBuilder execution in two different ways:

  • GciSoftBreak sends a soft break, which interrupts the Smalltalk virtual machine only. The GemBuilder functions that can recognize a soft break are GciPerform, GciContinue, GciExecute, GciExecuteFromContext, GciExecuteStr, and GciExecuteStrFromContext.
  • GciHardBreak sends a hard break, which interrupts the Gem process itself, and is not trappable through Smalltalk exception handlers.

Issuing a soft break may be desirable if, for example, your application is executing code in Smalltalk, and enters an infinite loop.

In order for GemBuilder functions in your program to recognize interrupts, your program usually needs a signal handler that can call the functions GciSoftBreak and GciHardBreak. Since GemBuilder generally does not relinquish control to an application until it has finished its processing, soft and hard breaks are then initiated from an interrupt service routine. Alternatively, if you are calling the non-blocking GemBuilder functions, you can service interrupts directly within your event loop, while awaiting the completion of a function.

If GemStone is executing when it receives the break, it replies with an error message. If it is not executing, it ignores the break.

2.4  Structural Access to Objects

As mentioned earlier in this chapter, GemBuilder provides a set of C functions that enable you to do the following:

You may need to use GemBuilder’s “structural access” functions for either of two reasons:

Because they call on GemStone’s internal object manager without using the Smalltalk virtual machine, the structural access functions provide the most efficient possible access to individual objects.

If your C application must handle GemStone objects that it did not create, using the structural access functions may be the only way you can be sure that the components of those objects will be accessible to the application. A user might, for example, define a subclass of Array in which at: and at:put: were disallowed or given new meanings. In that case, your C application could not rely on the standard GemStone kernel class methods to read and manipulate the contents of such a collection.

Despite their advantages, you should use these structural access functions only if you’ve determined that Smalltalk message-passing won’t do the job at hand. GemBuilder’s structural access functions violate the principles of abstract data types and encapsulation, and they bypass the consistency checks encoded in the Smalltalk kernel class methods. If your C application unwisely alters the structure of a GemStone object (by, for example, storing bytes directly into a floating-point number), the object will behave badly and your application will break.

For security reasons, the GemStone object AllUsers cannot be modified using structural access. If you attempt to do so, GemStone raises the RT_ERR_OBJECT_PROTECTED error.

Direct Access to Metadata

Your C program can use GemBuilder’s structural access functions to request certain data about an object:

  • Class

Each object is an instance of some class. The class defines the behavior of its instances. To find an object’s class, call GciFetchClass.

  • Format

GemStone represents the state of an object in one of four different implementations (formats): byte, pointer, NSC (non-sequence able collection), or special, specified as GC_FORMAT_OOP, GC_FORMAT_BYTE, GC_FORMAT_NSC, and GC_FORMAT_SPECIAL. Data types are described under GemStone Object Implementation Types. To find an object’s implementation, call GciFetchObjImpl.

  • Size

The function GciFetchNamedSize returns the number of named instance variables in an object, while GciFetchVaryingSize_ returns the number of unnamed instance variables. GciFetchSize_ returns the object’s complete size (the sum of its named and unnamed variables).

The result of GciFetchSize_ depends on the object’s implementation format. For byte objects (such as instances of String or Float), GciFetchSize_ returns the number of bytes in the object’s representation. For pointer and NSC objects, this function returns the number of OOPs that represent the object. For “special” objects (Special), the size is always 0.

Byte Objects

GemStone byte objects (for example, instances of class String or Symbol) can be manipulated in C as arrays of characters. The following GemBuilder functions enable your C program to store into, or fetch from, GemStone byte objects such as Strings:

GciAppendBytes

GciAppendChars

GciFetchByte

GciFetchBytes_

GciFetchChars_

GciStoreByte

GciStoreBytes

GciStoreChars

Although instances of Float are implemented within GemStone as byte objects, you should not used the above functions to fetch and store values; use the functions GciOopToFlt and GciFltToOop to convert between floating point objects and their equivalent C values.

Example 2.4 demonstrates importing a String to a C variable. This code assumes that the C variable suppId contains an OOP representing an object of class String, and imports it into suppName.

Example 2.4 

void example_fetchBytes(OopType aGsString)
{
   char supplierName[1025];
   int64 size = GciFetchBytes_(aGsString, 1, (ByteType*)supplierName, 
		sizeof(supplierName) - 1);
   supplierName[size] = '\0';
 
   // supplierName now contains the bytes of the GemStone object 
   // aGsString, or the first 1024 bytes. 
}
 

Pointer Objects

In your C program, a GemStone pointer object is represented as an array of OOPs. The order of the OOPs within the GemStone pointer object is preserved in the C array. GemStone represents the following kinds of objects as arrays of OOPs:

Objects with Named Instance Variables

Any object with one or more named instance variables is represented as an array of OOPs. You can fetch one or more values by the index of the instance variable, using GciFetchNamedOop* or GciFetchOop*, and store them using GciStoreNamedOop* or GciStoreOop*.

Indexable Objects

Any indexable object not implemented as a byte object is represented as an array of OOPs. You can fetch one or more unnamed (indexable) instance variable values using GciFetchVaryingOop*, and store them using GciStoreIdxOop*.

If the indexable object also has instance variables, then you can access both named and unnamed (varying or indexable) instance variables using GciFetchOop* and GciStoreOop*. Using these functions, pointers to the named instance variables precede pointers to the indexable instance variables.

For example, assume that the C variable currSup contains an OOP representing an object of class Supplier (which defines seven named instance variables). Example 2.5 imports the state of the Supplier object (that is, the OOPs of its component instance variables) into the C variable instVar.

Example 2.5 

void example_fetchOops(OopType currSup)
{
  enum { num_ivs = 7 };
  OopType instVars[num_ivs];
 
  int numRet = GciFetchNamedOops(currSup, 1L, instVars, num_ivs);
  if (numRet == 7) {
    // instVars now contains the OOPs of the seven instance
    // variables of the GemStone object referenced by currSup
  } else {
    // error occurred or currSup is not of expected class or size
  }
}
 
 

Nonsequenceable Collections (NSC Objects)

NSC objects (for example, instances of class IdentityBag and IdentitySet) reference other objects in a manner similar to pointer objects, except that the notion of order is not preserved when objects are added to or removed from the collection.

To fetch the contents of an NSC, you may use GciFetchOops. While this provides an ordered array, GemStone preserves the position of objects in an NSC only until the NSC is modified, or until the session is terminated (whichever comes first).

While you can access named instance variables of NSC objects as you would any other object, and retrieve the contents, you must use special functions to add or remove elements from the collection:

GciAddOopToNsc

GciAddOopsToNsc

GciRemoveOopFromNsc

GciRemoveOopsFromNsc

Efficient Fetching and Storing with Object Traversal

The functions described in the preceding sections allow your C program to import and export the components of a single GemStone object. When your application needs to obtain information about multiple objects in the repository, it can minimize the number of network calls by using GemBuilder’s object traversal functions.

Suppose, for example, that you had created a GemStone Employee class like the one in Example 2.6.

Example 2.6 

 Object subclass: 'Employee'
    instVarNames: #( 'name' 'empNum' 'jobTitle'
                     'department' 'address' 'favoriteTune')
    classVars: #()
    classInstVars: #()
    poolDictionaries: #()
    inDictionary: UserGlobals
 

Imagine that you needed to write C code to make a two-column display of job titles and favorite tunes. By using GemBuilder’s “object traversal” functions, you can minimize the number of network fetches and avoid running the Smalltalk virtual machine.

How Object Traversal Works

To understand the object traversal mechanism, think of each GemStone pointer object as the root of a tree (for now, ignore the possibility of objects containing themselves). The branches at the first level go to the object’s instance variables, which in turn are connected to their own instance variables, and so on.

Figure 2.1 illustrates a piece of the tree formed by an instance of Employee.

Figure 2.1   Object Traversal and Paths

In a single call, GemStone’s internal object traversal function walks such a tree post-depth-first to some specified level, building up a “traversal buffer” that is an array of “object reports” describing the classes of the objects encountered and the values of their contents. It then returns that traversal buffer to your application for selective extraction and processing of the contents.

Thus, to make your list of job titles and favorite tunes with the smallest possible amount of network traffic per employee processed, you could ask GemStone to traverse each employee to two levels (the first level is the Employee object itself and the second level is that object’s instance variables). You could then pick out the object reports describing jobTitle and favoriteTune, and extract the values stored by those reports (welder and Am I Blue respectively).

This approach would minimize network traffic to a single round trip.

One further optimization is possible: instead of fetching each employee and traversing it individually to level two, you could ask GemStone to begin traversal at the collection of employees and to descend three levels. That way, you would get information about the whole collection of employees with just a single call over the network.

The Object Traversal Functions

The function GciTraverseObjs traverses object trees rooted at a collection of one or more GemStone objects, gathering object reports on the specified objects into a traversal buffer.

  • Traversal buffers are instances of the C++ class GciTravBufType, which is defined in $GEMSTONE/include/gcicmn.ht. (For details about GciTravBufType, see Traversal Buffer - GciTravBufType.)
  • Object reports within the traversal buffer are described by the C++ classes GciObjRepSType and GciObjRepHdrSType, which are defined in $GEMSTONE/include/gci.ht. (For details about these classes, see Object Report Structure - GciObjRepSType.)

Each object report provides information about an object’s identity (its OOP), class, size (the number of instance variables, named plus unnamed), object security policy id, implementation (byte, pointer, NSC, or special), and the values stored in its instance variables.

When the amount of information obtained in a traversal exceeds the amount of available memory, your application can break the traversal into manageable amounts of information by issuing repeated calls to GciMoreTraversal. Generally speaking, an application can continue to call GciMoreTraversal until it has obtained all requested information.

Your application can call GciFindObjRep to scan a traversal buffer for an individual object report. Before it allocates memory for a copy of the object report, your program can call GciObjRepSize_ to obtain the size of the report.

The function GciStoreTrav allows you to store values into any number of existing GemStone objects in a single network round trip. That function takes a traversal buffer of object reports as its argument.

Functions such as GciStoreTravDo_ and GciStoreTravDoTrav_ are even more parsimonious of network resources. In a single network round trip, you can store values into any number of existing GemStone objects, then execute some code; the function returns a pointer to the resulting object. These functions takes a structure as an argument, which defines traversal buffer of object reports and an execution string or message. After the function has completed, the structure also contains information describing the GemStone objects that have changed.

Efficient Fetching And Storing with Path Access

As you’ve seen, object traversal is a powerful tool for fetching information about multiple objects efficiently. But writing the code for parsing traversal buffers and object reports may not always be simple. And even if you can afford the memory for importing unwanted information, the processing time spent in parsing that information into object reports may be unacceptable.

Consider the Employee object illustrated in the Figure 2.1. If your job were to extract a list of job titles and favorite tunes from a set of such Employees, it would be reasonable to use GemBuilder’s object traversal functions (as described above) to get the needed information. The time spent in building up object reports for the unwanted portions would probably be negligible. Suppose, however, that there were an additional 200 instance variables in each Employee. Then the time used in processing wasted object reports would far exceed the time spent in useful work.

Therefore, GemBuilder provides a set of path access functions that can fetch or store multiple objects at selected positions in an object tree with a single call across the network, bringing only the desired information back. The function GciFetchPaths lets you fetch selected components from a large set of objects with only a single network round trip. Similarly, your program can call GciStorePaths to store new values into disparate locations within a large number of GemStone objects.

2.5  Nonblocking Functions

Under most circumstances, when an application calls a GemBuilder function, the operation that the function specifies is completed before the function returns control to the application. That is, the GemBuilder function blocks the application from proceeding until the operation is finished. This effect guarantees a strict sequence of execution.

Nevertheless, in most cases a GemBuilder function calls upon GemStone (that is, the Gem) to perform some work. If the Gem and the application are running in different processes, especially on different machines, blocking implies that only one process can accomplish work at a time. GemBuilder’s nonblocking functions were designed to take advantage of the opportunity for concurrent execution in separate Gem and application processes.

The results of performing an operation through a blocking function or through its nonblocking twin are always the same. The difference is that the nonblocking function does not wait for the operation to complete before it returns control to the session. Since the results of the operation are probably not ready when a nonblocking function returns, all nonblocking functions but one (GciNbEnd) return void.

While a nonblocking operation is in progress an application can do any kind of work that does not require GemBuilder. In fact, it can also call a limited set of GemBuilder functions, listed as follows:

GciCallInProgress
GciErr
GciGetSessionId
GciHardBreak
GciNbEnd
GciSetSessionId
GciShutdown
GciSoftBreak

If the application first changes sessions, and that session has no nonblocking operation in progress, then the application can call any GemBuilder function, including a nonblocking function. GemBuilder supports one repository request at a time, per session. However, nonblocking functions do not implement threads, meaning that you cannot have multiple concurrent repository requests in progress within a single session. If an application calls any GemBuilder function besides those listed here while a nonblocking operation is in progress in the current session, the error GCI_ERR_OP_IN_PROGRESS is generated.

Once a nonblocking operation is in progress, an application must call GciNbEnd at least once to determine the operation’s status. Repeated calls are made if necessary, until the operation is complete. When it is complete, GciNbEnd hands the application a pointer to the result of the operation, the same value that the corresponding blocking call would have returned directly.

Nonblocking functions are not truly nonblocking if they are called from a linkable GemBuilder session, because the Gem and GemBuilder are part of the same process. However, those functions can still be used in linkable sessions. If they are, GciNbEnd must still be called at least once per nonblocking call, and it always indicates that the operation is complete.

All error-handling features are supported while nonblocking functions are used. Errors may be signalled either when the nonblocking function is called or later when GciNbEnd is called.

2.6  Operating System Considerations for C Applications

Like your C application, GemBuilder for C is, in itself, a body of C code. Some aspects of the interface must interact with the surrounding operating system. The purpose of this section is to point out a few places where you must code with caution in order to avoid conflicts.

Signal Handling in Your GemBuilder Application

Under UNIX, it is important that signals be enabled when your code calls GemBuilder functions. Disabling signals has the effect of disabling much of the error handling within GemBuilder. Because signal handlers can execute at arbitrary points during execution of your application, your signal handling code should not call any GemBuilder functions other than GciSoftBreak, GciHardBreak, or GciCallInProgress.

GciInit always installs a signal handler for SIGIO. This handler chains to any previous handler. Do not, under any circumstances, turn off SIGIO.

In the linkable (GciLnk) configuration, GciInit also does the following:

  • Installs handlers to service and ignore these signals (if no previous handler is found): SIGPIPE, SIGHUP, SIGDANGER
  • Installs handlers to treat the following signals as fatal errors if they are defined by the operating system: SIGTERM, SIGXCPU, SIGABRT, SIGXFSZ, SIGXCPU, SIGEMT, SIGLOST
  • Installs a handler for SIGUSR1. If you have a valid linkable session, SIGUSR1 will cause the Smalltalk interpreter to print the current Smalltalk stack to stdout or to the Topaz output file. This handler chains to any previous handler.
  • Installs a handler for SIGUSR2, which is used internally by a Gemstone session. This handler chains to any previous handler.
  • Installs a handler to gracefully handle SIGCHLD if no previous handler is found.
  • Installs a handler to treat SIGFPE as a fatal error if no previous handler is found.
  • Installs handlers for SIGSEGV, SIGILL and SIGBUS. If the program counter is found to be in libgci*.so code, or if no previous handler is available to chain to, these are fatal errors.

If your application installs a handler for SIGIO after calling GciInit, your handler must chain to the previously existing handler.

If your application uses Linkable GCI and installs any signal handlers after calling GciInit, you must chain to the previously existing handlers. If you install handlers for SIGSEGV, SIGILL or SIGBUS, your handler must determine if the program counter at the point of the signal is in your own C or C++ code and if not, must chain to the previously existing handler. You must only treat these signals as fatal if the program counter is in your own code.

SIGVTALRM is used by the ProfMonitor class. Similar chaining is required for SIGVTALRM if you intend to use ProfMonitor.

If you are linking with other shared libraries, it is recommended that GciInit be called after all other libraries are loaded.

Executing Host File Access Methods

If you use any of the GciPerform* or GciExecute* functions to execute a Smalltalk host file access method (as listed below), and you do not supply a full file pathname as part of the method argument, the default directory for the Smalltalk method depends on if you are running linked or RPC GemBuilder.

With GciLnk, the default directory is the directory in which the Gem (GemStone session) process was started. With GciRpc, the default directory is the home directory of the host user account, or the #dir specification of the network resource string.

The Smalltalk methods that are affected include System class>>performOnServer: and the file accessing methods implemented in GsFile. See the file I/O information in the Programming Guide.

Writing Portable Code

If you want to produce code that can run in both 32-bit and 64-bit environments, observe the following guidelines:

  • Don’t hard-code size computations. Instead, use sizeof operations, so that if some structure changes, your code will still return the correct values.
  • If you are using printf strings to print 64-bit integers, you might find it convenient to use the FMT_* macros in $GEMSTONE/include/gci.ht. Those macros help you to compose a format string for a printf that will be portable. In particular, use of the FMT_ macros make the printing of 64-bit integers portable between Windows and UNIX. These macros are recommended:
OopType – FMT_OID 
int64 – FMT_I64
uint64 – FMT_U64
intptr_t – FMT_PTR_T
uintptr_t – FMT_UPTR_T
  • To avoid discrepancies between 32-bit and 64-bit environments, avoid the use of long or unsigned long in your code. Instead, you can use the type intptr_t, which makes the variable the same size as a pointer, regardless whether your application is running in 32-bit or 64-bit. Alternatively, you can use int64 or int to fix the size of the variable explicitly.

2.7  Error Handling and Recovery

Your C program is responsible for processing any errors generated by GemBuilder function calls.

The GemBuilder include file gcierr.ht documents and defines mnemonics for all GemStone errors. Search the file for the mnemonic name or error number to locate an error in the file. The errors are divided into five groups: compiler, run-time (virtual machine), aborting, fatal, and event.

GemBuilder provides functions that allow you to poll for errors or to use error jump buffers. The following paragraphs describe both of these techniques.

Polling for Errors

Each call to GemBuilder can potentially fail for a number of reasons. Your program can call GciErr to determine whether the previous GemBuilder call resulted in an error. If so, GciErr will obtain full information about the error. If an error occurs while Smalltalk code is executing (in response to GciPerform or one of the GciExecute... functions), your program may be able to continue Smalltalk execution by calling GciContinue.

If you are in a UserAction and want to pass the error back to the Smalltalk client, you must execute GciRaiseException.

Error Jump Buffers

When your program makes three or more GemBuilder calls in sequence, jump buffers provide significantly faster performance than polling for errors.

When your C program calls Gci_SETJMP, the context of the current C environment is saved in a jump buffer designated by your program. GemBuilder maintains a stack of up to 20 error jump buffers. A buffer is pushed onto the stack when GciPushErrJump is called, and popped when GciPopErrJump is called. When an error occurs during a GemBuilder call, the GemBuilder implementation calls GciLongJmp using the buffer currently at the top of GemBuilder’s error jump stack, and pops that buffer from the stack.

For functions with local error recovery, your program can call GciSetErrJump to temporarily disable the GciLongJmp mechanism (and to re-enable it afterwards).

Whenever the jump stack is empty, the application must use GciErr to poll for any GemBuilder errors.

The Call Stack

The Smalltalk virtual machine creates and maintains a call stack that provides information about the state of execution of the current Smalltalk expression or sequence of expressions. The call stack includes an ordered list of activation records related to the methods and blocks that are currently being executed. The virtual machine ordinarily clears the call stack before each new expression is executed.

If a soft break or an unexpected error occurs, the virtual machine suspends execution, creates a Process object, and raises an error. The Process object represents both the Smalltalk call stack when execution was suspended and any information that the virtual machine needs to resume execution. If there was no fatal error, your program can call GciContinue to resume execution. Call GciClearStack instead if there was a fatal error, or if you do not want your program to resume the suspended execution.

GemStone System Errors

If your application receives a GemStone system error while linked with GciLnk, relink your application with GciRpc and run it again with an uncorrupted copy of your repository. Your GemStone system administrator can refer to the repository backup and recovery procedures in the System Administration Guide for GemStone/S 64 Bit.

If the error can be reproduced, contact GemStone Customer Support. Otherwise, the error is in your application, and you need to debug your application before using GciLnk again.

 

Previous chapter

Next chapter