Previous section   Next section

The Callin Interface

Caché offers a Callin interface you can use from within C programs to execute Caché commands and evaluate Caché expressions. This chapter describes this interface and includes the following sections:
The Callin interface permits a wide variety of applications. For example, you can use it to make ObjectScript available from an integrated menu or GUI. If you gather information from an external device, such as an Automatic Teller Machine or piece of laboratory equipment, the Callin interface lets you store this data in a Caché database. Although Caché currently supports only C and C++ programs, any language that uses the calling standard for that platform (UNIX®, Windows) can invoke the Callin functions.
See Using the Callin Functions for a quick review of Callin functions. For detailed reference material on each Callin function, see the Callin Function Reference.

The callin.h Header File

The callin.h header file defines prototypes for these functions, which allows your C compiler to test for valid parameter data types when you call these functions within your program. You can add this file to the list of #include statements in your C program:
#include "callin.h"
The callin.h file also contains definitions of parameter values you use in your calls, and includes various
#define
s that may be of use. These include operating-system–specific values, error codes, and values that determine how Caché behaves.
You can translate the distributed header file, callin.h. However, callin.h is subject to change and you must track any changes if you create a translated version of this file. InterSystems Worldwide Support Center does not handle calls about unsupported languages.
Return values and error codes
Most Callin functions return values of type int, where the return value does not exceed the capacity of a 16-bit integer. Returned values can be
CACHE_SUCCESS
, a Caché error, or a Callin interface error.
There are two types of errors:
  • Caché errors — The return value of a Caché error is a positive integer.
  • Interface errors — The return value of an interface error is 0 or a negative integer.
callin.h defines symbols for all Caché and interface errors, including
CACHE_SUCCESS
(
0
) and
CACHE_FAILURE
(
-1
). You can translate Caché errors (positive integers) by making a call to the Callin function CacheErrxlate.

8-bit and Unicode String Handling

Caché Callin functions that operate on strings have both 8-bit and Unicode versions. These functions use a suffix character to indicate the type of string that they handle:
  • Names with an “A” suffix or no suffix at all (for example,CacheEvalA or CachePopStr) are versions that operate on local 8-bit encoded character strings.
  • Names with a “W” suffix (for example,CacheEvalW or CachePopStrW) are versions for Unicode character strings on platforms that use 2–byte Unicode characters.
  • Names with an “H” suffix (for example,CacheEvalH or CachePopStrH) are versions for Unicode character strings on platforms that use 4–byte Unicode characters.
For best performance, use the kind of string native to your installed version of Caché.

8-bit String Data Types

Caché supports the following data types that use local 8-bit string encoding:
  • CACHE_ASTR — counted string of 8-bit characters
  • CACHE_ASTRP — Pointer to an 8-bit counted string
The type definition for these is:
#define CACHE_MAXSTRLEN 32767
typedef struct {
   unsigned short  len;
   Callin_char_t   str[CACHE_MAXSTRLEN];
} CACHE_ASTR, *CACHE_ASTRP;
The
CACHE_ASTR
and
CACHE_ASTRP
structures contain two elements:
  • len
    — An integer. When used as input, this element specifies the actual length of the string whose value is supplied in the
    str
    element. When used as output, this element specifies the maximum allowable length for the
    str
    element; upon return, this is replaced by the actual length of
    str
    .
  • str
    — A input or output string.
CACHE_MAXSTRLEN
is the maximum length of a string that is accepted or returned. A parameter string need not be of length
CACHE_MAXSTRLEN
nor does that much space have to be allocated in the program.

2–byte Unicode Data Types

Caché supports the following Unicode-related data types on platforms that use 2–byte Unicode characters:
  • CACHEWSTR — Unicode counted string
  • CACHEWSTRP — Pointer to Unicode counted string
The type definition for these is:
typedef struct { 
   unsigned short len; 
   unsigned short str[CACHE_MAXSTRLEN]; 
} CACHEWSTR, *CACHEWSTRP;
The
CACHEWSTR
and
CACHEWSTRP
structures contain two elements:
  • len
    — An integer. When used as input, this element specifies the actual length of the string whose value is supplied in the
    str
    element. When used as output, this element specifies the maximum allowable length for the
    str
    element; upon return, this is replaced by the actual length of
    str
    .
  • str
    — A input or output string.
CACHE_MAXSTRLEN
is the maximum length of a string that is accepted or returned. A parameter string need not be of length
CACHE_MAXSTRLEN
nor does that much space have to be allocated in the program.
On Unicode-enabled versions of Caché, there is also the data type CACHE_WSTRING, which represents the native string type on 2–byte platforms. CacheType returns this type. Also, CacheConvert can specify CACHE_WSTRING as the data type for the return value; if this type is requested, the result is passed back as a counted Unicode string in a CACHEWSTR buffer.

4–byte Unicode Data Types

Caché supports the following Unicode-related data types on platforms that use 4–byte Unicode characters:
  • CACHEHSTR — Extended Unicode counted string
  • CACHEHSTRP — Pointer to Extended Unicode counted string
The type definition for these is:
typedef struct {
   unsigned int len;
   wchar_t str[CACHE_MAXSTRLEN];
} CACHEHSTR, *CACHEHSTRP;

The
CACHEHSTR
and
CACHEHSTRP
structures contain two elements:
  • len
    — An integer. When used as input, this element specifies the actual length of the string whose value is supplied in the
    str
    element. When used as output, this element specifies the maximum allowable length for the
    str
    element; upon return, this is replaced by the actual length of
    str
    .
  • str
    — A input or output string.
CACHE_MAXSTRLEN
is the maximum length of a string that is accepted or returned. A parameter string need not be of length
CACHE_MAXSTRLEN
nor does that much space have to be allocated in the program.
On Unicode-enabled versions of Caché, there is also the data type CACHE_HSTRING, which represents the native string type on 4–byte platforms. CacheType returns this type. Also, CacheConvert can specify CACHE_HSTRING as the data type for the return value; if this type is requested, the result is passed back as a counted Unicode string in a CACHEHSTR buffer.

System-neutral Symbol Definitions

The allowed inputs and outputs of some functions vary depending on whether they are running on an 8-bit system or a Unicode system. For many of the “A” (ASCII) functions, the arguments are defined as accepting a
CACHESTR
,
CACHE_STR
,
CACHESTRP
, or
CACHE_STRP
type. These symbol definitions (without the “A” , “W”, or “H”) can conditionally be associated with either the 8-bit or Unicode names, depending on whether the symbols
CACHE_UNICODE
and
CACHE_WCHART
are defined at compile time. This way, you can write source code with neutral symbols that works with either local 8-bit or Unicode encodings.
The following excerpt from callin.h illustrates the concept:
#if defined(CACHE_UNICODE) /* Unicode character strings */
#define   CACHESTR      CACHEWSTR
#define   CACHE_STR     CACHEWSTR
#define   CACHESTRP     CACHEWSTRP
#define   CACHE_STRP    CACHEWSTRP
#define   CACHE_STRING  CACHE_WSTRING

#elif defined(CACHE_WCHART)  /* wchar_t character strings */
#define   CACHESTR      CACHEHSTR
#define   CACHE_STR     CACHEHSTR
#define   CACHESTRP     CACHEHSTRP
#define   CACHE_STRP    CACHEHSTRP
#define   CACHE_STRING  CACHE_HSTRING

#else                  /* 8-bit character strings */
#define   CACHESTR      CACHE_ASTR
#define   CACHE_STR     CACHE_ASTR
#define   CACHESTRP     CACHE_ASTRP
#define   CACHE_STRP    CACHE_ASTRP
#define   CACHE_STRING  CACHE_ASTRING
#endif

Using Caché Security Functions

Two functions are provided for working with Caché passwords:
  • CacheSecureStart — Similar to CacheStart, but with additional parameters for password authentication. The CacheStart function is now deprecated. If used, it will behave as if CacheSecureStart has been called with NULL for Username, Password, and ExeName. You cannot use CacheStart if you need to use some form of password authentication.
  • CacheChangePassword — This function will change the user's password if they are using Caché authentication (it is not valid for LDAP/DELEGATED/Kerberos etc.). It must be called before a Callin session is initialized.
There are CacheSecureStart and CacheChangePassword functions for ASCII "A", Unicode "W", and Unicode "H" installs. The new functions either narrow, widen or "use as is" the passed in parameters, store them in the new Callin data area, then eventually call the CacheStart entry point.
CacheStart and CacheSecureStart pin and pout parameters can be passed as NULL, which indicates that the platform's default input and output device should be used.

Using Callin with Multithreading

Caché has been enhanced so that Callin can be used by threaded programs running under some versions of Windows and UNIX® (see “Other Supported Features” in the online InterSystems Supported Platforms document for this release for a list). A program can spawn multiple threads (pthreads in a UNIX® environment) and each thread can establish a separate connection to Caché by calling CacheSecureStart. Threads may not share a single connection to Caché; each thread which wants to use Cache must call CacheSecureStart. If a thread attempts to use a Callin function and it has not called CacheSecureStart, a
CACHE_NOCON
error is returned.
A threaded application must link against cachet.o or the shared library, cachet.so. On UNIX® and Linux they may alternatively load the shared library dynamically. On Windows, due to the implementation of thread local storage the cachet.dll library cannot be dynamically loaded. The program should be careful not to exit until all of the threads which have entered Caché have called CacheEnd to shut down their connections. Failure to shut down each connection with CacheEnd may hang the instance, requiring a restart.
If CacheSecureStart is being used, to specify credentials as part of the login, each thread must call CacheSecureStart and provide the correct username/password for the connection, since credentials are not shared between the threads. There is a performance penalty within Caché using threads because of the extra code the C compiler has to generate to access thread local storage (which uses direct memory references in non-threaded builds).
A sample program, sampcallint.c, is provided on all platforms where this feature is supported. The vc8 project, and the UNIX® Makefiles, include instructions to build a sample threaded Callin application on the relevant platforms.

Threads and UNIX® Signal Handling

On UNIX®, Caché uses a number of signals. If your application uses the same signals, you should be aware of how Caché deals with them. All signals have a default action specified by the OS. Applications may choose to leave the default action, or can choose to handle or ignore the signal. If the signal is handled, the application may further select which threads will block the signal and which threads will receive the signal. Some signals cannot be blocked, ignored, or handled. Since the default action for many signals is to halt the process, leaving the default action in place is not an option. The following signals cannot be caught or ignored, and terminate the process:
SIGNAL DISPOSITION
SIGKILL
terminate process immediately
SIGSTOP
stop process for later resumption
The actions that an application establishes for each signal are process-wide. Whether or not the signal can be delivered to each thread is thread-specific. Each thread may specify how it will deal with signals, independently of other threads. One thread may block all signals, while another thread may allow all signals to be sent to that thread. What happens when a signal is sent to the thread depends on the process-wide handling established for that signal.

Caché Signal Processing

Caché integrates with application signal handling by saving application handlers and signal masks, then restoring them at the appropriate time. Caché processes signals in the following ways:
Generated signals
Caché installs its own signal handler for all generated signals. It saves the current (application) signal handler. If the thread catches a generated signal, the Caché signal handler disconnects the thread from Caché, calls the applications signal handling function (if any), then does
pthread_exit
.
Since signal handlers are process-wide, threads not connected to Caché will also go into the Caché handler. If Caché detects that the thread is not connected, it calls the application handler and then does
pthread_exit
.
Synchronous Signals
Caché establishes signal handlers for all synchronous signals, and unblocks these signals for each thread when the thread connects to Caché (see “Synchronous Signals” for details).
Asynchronous Signals
Caché handles all asynchronous signals that would terminate the process (see “Asynchronous Signals” for details).
Save/Restore Handlers
The system saves the signal state when the first thread connects to it. When the last thread disconnects, Caché restores the signal state for every signal that it has handled.
Save/Restore Thread Signal Mask
The thread signal mask is saved on connect, and restored when the thread disconnects.

Synchronous Signals

Synchronous signals are generated by the application itself (for example,
SIGSEGV
). Caché establishes signal handlers for all synchronous signals, and unblocks these signals for each thread when it connects to Caché.
Synchronous signals are caught by the thread that generated the signal. If the application has not specified a handler for a signal it has generated (for example,
SIGSEGV
), or if the thread has blocked the signal, then the OS will halt the entire process. If the thread enters the signal handler, that thread may exit cleanly (via
pthread_exit
) with no impact to any other thread. If a thread attempts to return from the handler, the OS will halt the entire process. The following signals cause thread termination:
SIGNAL DISPOSITION
SIGABRT
process abort signal
SIGBUS
bus error
SIGEMT
EMT instruction
SIGFPE
floating point exception
SIGILL
illegal instruction
SIGSEGV
access violation
SIGSYS
bad argument to system call
SIGTRAP
trace trap
SIGXCPU
CPU time limit exceeded (
setrlimit
)

Asynchronous signals

Asynchronous signals are generated outside the application (for example,
SIGALRM
,
SIGINT
, and
SIGTERM
). Caché handles all asynchronous signals that would terminate the process.
Asynchronous signals may be caught by any thread that has not blocked the signal. The system chooses which thread to use. Any signal whose default action is to cause the process to exit must be handled, with at least one thread eligible to receive it, or else it must be specifically ignored.
The application must establish a signal handler for those signals it wants to handle, and must start a thread that does not block those signals. That thread will then be the only one eligible to receive the signal and handle it. Both the handler and the eligible thread must exist before the application makes its first call to CacheStart. On the first call to CacheStart, the following actions are performed for all asynchronous signals that would terminate the process:
  • Caché looks for a handler for these signals. If a handler is found, Caché leaves it in place. Otherwise, Caché sets the signal to
    SIG_IGN
    (ignore the signal).
  • Caché blocks all of these signals for connected threads, whether or not a signal has a handler. Thus, if there is a handler, only a thread that is not connected to Caché can catch the signal.
The following signals are affected by this process:
SIGNAL DISPOSITION
SIGALRM
timer
SIGCHLD
blocked by threads
SIGDANGER
ignore if unhandled
SIGHUP
ignore if unhandled
SIGINT
ignore if unhandled
SIGPIPE
ignore if unhandled
SIGQUIT
ignore if unhandled
SIGTERM
If
SIGTERM
is unhandled, Cache will handle it. On receipt of a
SIGTERM
signal, the Cache handler will disconnect all threads and no new connections will be permitted. Handlers for
SIGTERM
are not stacked.
SIGUSR1
inter-process communication
SIGUSR2
inter-process communication
SIGVTALRM
virtual timer
SIGXFSZ
Caché asynchronous thread rundown

Callin Programming Tips

Topics in this section include:

Tips for All Callin Programs

Your external program must follow certain rules to avoid corrupting Caché data structures, which can cause a system hang.
  • Limits on the number of open files
    Your program must ensure that it does not open so many files that it prevents Caché from opening the number of databases or other files it expects to be able to. Normally, Caché looks up the user's open file quota and reserves a certain number of files for opening databases, allocating the rest for the Open command. Depending on the quota, Caché expects to have between 6 and 30 Caché database files open simultaneously, and from 0 to 36 files open with the Open command.
  • Maximum Directory Length for Callin Applications
    The directory containing any Callin application must have a full path that uses fewer than 232 characters. For example, if an application is in the
    C:\CacheApps\Accounting\AccountsPayable\
    directory, this has 40 characters in it and is therefore valid.
  • Call CacheEnd after CacheStart before halting
    If your Caché connection was established by a call to CacheStart, then you must call CacheEnd when you are done with the connection. You can make as many Callin function calls in between as you wish.
    You must call CacheEnd even if the connection was broken. The connection can be broken by a call to CacheAbort with the RESJOB parameter.
    CacheEnd performs cleanup operations which are necessary to prepare for another call to CacheStart. Calling CacheStart again without calling CacheEnd (assuming a broken connection) will return the code CACHE_CONBROKEN.
  • Wait until ObjectScript is done before exiting
    If you are going to exit your program, you must be certain ObjectScript has completed any outstanding request. Use the Callin function CacheContext to determine whether you are within ObjectScript. This call is particularly important in exit handlers and Ctrl-C or Ctrl-Y handlers. If CacheContext returns a non-zero value, you can invoke CacheAbort.
  • Maintaining Margins in Callin Sessions
    While you can set the margin within a Callin session, the margin setting is only maintained for the rest of the current command line. If a program (as with direct mode) includes the line:
    :Use 0:10 Write x
    
    the margin of 10 is established for the duration of the command line.
    Certain calls affect the command line and therefore its margin. These are the calls are annotated as "calls into Caché" in the function descriptions.
  • Avoid signal handling when using CacheStart()
    CacheStart sets handlers for various signals, which may conflict with signal handlers set by the calling application.

Tips for Windows

These tips apply only to Windows.
  • Limitations on building Callin applications using the cache shared library (cache.dll)
    If Callin applications are built using the shared library (cache.dll) rather that the static object (cache.obj), users who have large global buffer pools may see the Callin fail to initialize (in CacheStart) with an error:
    <Cache Startup Error: Mapping shared memory (203)>
    The explanation for this lies in the behavior of system DLLs loading in Windows. Applications coded in the Win 32 API or with the Microsoft Foundation Classes (the chief libraries that support Microsoft Visual C++ development) need to have the OS load the DLLs for that Windows code as soon as they initialize. These DLLs get loaded from the top of virtual storage (higher addresses), reducing the amount of space left for the heap. On most systems, there are also a number of other DLLs (for example, DLLs supporting the display graphics) that load automatically with each Windows process at locations well above the bottom of the virtual storage. These DLLs have a tendency to request a specific address space, most commonly 0X10000000 (256MB), chopping off a few hundred megabytes of contiguous memory at the bottom of virtual memory. The result may be that there is insufficient virtual memory space in the Callin executable in which to map the Cache shared memory segment.

Tips for UNIX®, Linux, and Mac OS

These tips apply only to UNIX®, Linux, and Mac OS.
  • Do not disable interrupt delivery on UNIX®
    UNIX® uses interrupts. Do not prevent delivery of interrupts.
  • Use the correct version of XCode
    Versions of Caché for Mac OS X (32–bit) previous to 2010.2 were built using the Xcode 2.5 compiler. Callin programs for these versions of Caché must be built using the same compiler. If your development platform is Mac OS X 10.5 (Leopard) or later, you would have to load and use Xcode 2.5 in place of the default Xcode 3.0 compiler.
  • Avoid using reserved signals
    On UNIX®, Caché uses a number of signals. If possible, application programs linked with Caché should avoid using the following reserved signals:
    SIGABRT SIGDANGER SIGILL SIGQUIT SIGTERM SIGVTALRM
    SIGALRM SIGEMT SIGINT SIGSTOP SIGTRAP SIGXCPU
    SIGBUS SIGFPE SIGKILL SIGSEGV SIGUSR1 SIGXFSZ
    SIGCHLD SIGHUP SIGPIPE SIGSYS SIGUSR2  
    If your application uses these signals, you should be aware of how Caché deals with them. See Threads and UNIX® Signal Handling for details.

Running Sample Programs on Windows

The \dev\cache\callin directory contains source files, header files, and project directories for building Caché Callin applications. These projects provide a simple demonstration of how to use some high level Caché call-in functions.
In order to build these projects, open any of the .vcproj files (for Visual C++ 2005), or .dsp files (for Visual C++ 2003). Double-click on the file, or run your Visual C++ application and select
File>Open>Project/Solution
to open the project file.
Note:
You can run call-in programs on Windows 2000, but you have to compile them on Windows XP or newer, since Visual Studio 2008 and the Windows 2008 SDK only go back to Windows XP. The Visual Studio 2008 redistributables are supported on Windows 2000, but there does not appear to be a compatible compiler that is supported on Windows 2000.
The shdir.c file has been already initialized with the path to your Caché mgr directory. For a default installation, the shdir.c file will look like this:
char shdir[256] = "c:\\cachesys\\mgr";

The Callin interface provides the CACHESETDIR entry point to dynamically set the name of the manager directory at runtime. The shared library version of cache requires the use of this interface to find the installation’s manager’s directory.
Two sample C programs are provided. The sampcallin.c program is the standard Callin application example, and sampcallint.c is the thread-safe Callin application example.
There are two projects for sampcallin.c and a project for sampcallint.c. These projects are:
  • callin — builds a statically linked Callin application using cache.obj.
  • callinsh — builds a dynamically linked Callin application using cache.dll.
  • callint — builds a dynamically linked thread-safe Callin application, using cachet.dll.
After each of the projects is built, it may be run in the Visual C++ environment.
When a project is built from the cache shared library, using cache.dll, the location of cache.dll must be defined in the user's PATH environment variable, except when the file is located in the current directory.

Running Sample Programs on UNIX® and Linux

The directory dev/cache/callin/samples contains a complete Makefile to build Callin samples. This replaces the clink file found in previous releases.
A shared library version of cache is now provided in addition to the cache object file. The UNIX® Makefiles build two Callin sample applications: one using the cache object, and one using the libcache shared library.
Run make in the dev/cache/callin/samples directory. The supplied Makefile will build a cache using the czf interface, a standard Callin application, and a shared library Callin application.
The file shdir.c is set to the appropriate value during installation, so no editing is required.
The Callin interface provides the CACHESETDIR entry point to dynamically set the name of the manager directory at runtime.
Using Makefiles on UNIX®
The UNIX® Makefiles for building Callin samples and customer Callin programs are run by the make command. make automatically finds the file called Makefile in the current directory. Thus, running make in the samples directory produces a sample Callin executable.
When invoking make, use the SRC variable to specify the name of the source program. The default is sampcallin. To change the name of the source file being built, override the SRC variable on the command line. For example, with a Callin program called mycallin.c, the command is:
   make SRC=mycallin
Setting Permissions for Callin Executables on UNIX®
Caché executables, files, and resources such as shared memory and operating system messages, are owned by a user selected at installation time (the installation owner) and a group with a default name of cacheusr (you can choose a different name at installation time). These files and resources are only accessible to processes that either have this user ID or belong to this group. Otherwise, attempting to connect to Caché results in protection errors from the operating system (usually specifying that access is denied); this occurs prior to establishing any connection with Caché.
A Callin program can only run if its effective group ID is cacheusr. To meet this condition, one of the following must be true:
  • The program is run by a user in the cacheusr group (or an alternate run-as group if it was changed from cacheusr to something else).
  • The program sets its effective user or group by manipulating its uid or gid file permissions (using the UNIX® chgrp and chmod commands).
Previous section   Next section