Appendix B Open Server Migration To Jaguar


Coding changes and examples

The code changes required for your Open Server applications include:

Modifying main

You must move Open Server application code currently running in the main routine to one or more event handlers. Other routines, such as the srv_run routine are also removed. You must also remove routines that Jaguar automatically initiates, and remove properties and handlers that are configured through Jaguar Manager.

Traditional Open Server application

The following file is a traditional Open Server application that contains a main routine:

#include <ospublic.h>
#include <server.h>

/*
** File server.c containing a typical Open Server main() function. For
** simplicity, there is no error handling here.
**
** This builds into an executable, with the supporting code, such as
** event handlers, ending up as either static libraries that become part
** of the executable, or as dynamic libraries loaded at run time.
*/
main(int argc, char *argv[])
{
/*
** Variables.
*/
CS_INT conns;
CS_INT threads;
CS_CHAR *name;
CS_CONTEXT *context;

/*
** Process command line. Function get_params() is in file appl.c.
*/
get_params(argc, argv, &conns, &threads, &name);
if (name == (CS_CHAR *) NULL)
{
printf("Usage: %s [-os_conns=<conns>] [-os_threads=<threads>] "
"-os_name=<name>\n", argv[0]);
exit(1);
}
/*
** Initialize server.
*/
cs_ctx_alloc(CS_VERSION_100, &context);
srv_version(context, CS_VERSION_100);
if (conns > 0)
srv_props(context, CS_SET, SRV_S_NUMCONNECTIONS,
(CS_VOID *) &conns, sizeof(conns), (CS_INT *) NULL);
if (threads > 0)
srv_props(context, CS_SET, SRV_S_NUMTHREADS,
(CS_VOID *) &threads, sizeof(threads), (CS_INT *) NULL)
srv_init((SRV_CONFIG *) NULL, name, CS_NULLTERM);

/*
** Register handlers. Files handler1.c and handler2.c contain these
** functions.
*/
srv_handle((SRV_SERVER *) NULL, SRV_START, start_handler);
srv_handle((SRV_SERVER *) NULL, SRV_ATTENTION, attn_handler);
srv_handle((SRV_SERVER *) NULL, SRV_BULK, bulk_handler);
srv_handle((SRV_SERVER *) NULL, SRV_CONNECT, conn_handler);
srv_handle((SRV_SERVER *) NULL, SRV_CURSOR, cur_handler);
srv_handle((SRV_SERVER *) NULL, SRV_DISCONNECT, disc_handler);
srv_handle((SRV_SERVER *) NULL, SRV_DYNAMIC, dyn_handler);
srv_handle((SRV_SERVER *) NULL, SRV_LANGUAGE, lang_handler);
srv_handle((SRV_SERVER *) NULL, SRV_RPC, rpc_handler);
srv_handle((SRV_SERVER *) NULL, SRV_OPTION, opt_handler);

/*
** Start server.
*/
srv_run((SRV_SERVER *) NULL);
exit(0);
}

Moving main() to an event handler

The main code has been placed in an event handler, other routines and properties have been removed:

#include <ospublic.h>
#include <server.h>

/*
** File server.c. The original main() function becomes init_handler().
**
** Build this into a dynamic library, and register the
** init_handler() function in Jaguar Manager. Jaguar
** will call the function at runtime.
**
** You may build the supporting code into the same dynamic library, or into
** one or more different dynamic libraries. In either case, you'll need to
** register each handler separately with Jaguar, using the Jaguar Manager.
*/CS_RETCODE CS_PUBLIC init_handler(CS_CONTEXT *context,
int argc, char *argv[])
{
/*
** Variables.
**
** Jaguar initializes context and passes it to this function.
*/
CS_INT conns;
CS_INT threads;
CS_CHAR *name;

/*
** Process command line. Function get_params() is in file appl.c.
**
** Do not exit on error.
*/
get_params(argc, argv, &conns, &threads, &name);

/*
** Initialize server.
**
** Get rid of cs_ctx_alloc(), srv_version(), etc. Do not call srv_init().
** Certain properties previously set using srv_props() are now set
** in Jaguar Manager.
*/
if (conns > 0)
srv_props(context, CS_SET, SRV_S_NUMCONNECTIONS,
(CS_VOID *) &conns, sizeof(conns), (CS_INT *) NULL);
if (threads > 0)
srv_props(context, CS_SET, SRV_S_NUMTHREADS,
(CS_VOID *) &threads, sizeof(threads), (CS_INT *) NULL);

/*
** Register handlers. Files handler1.c and handler2.c contain handler
** functions.
**
** Register all the handlers using Jaguar Manager.
*/

/*
** Start server.
**
** Jaguar calls srv_run(); Do not call it yourself.
*/

/*
** Return rather than exit.
*/
return CS_SUCCEED;
}

Open Server properties

Do not attempt to set or reset the SRV_S_PREEMPT property.

Do not set the SRV_S_STACKSIZE property.

The properties:

are set from Jaguar Manager. Do not set these properties using srv_props() calls.

To set these properties from Jaguar Manager:

  1. Highlight the server whose properties you want to set.
  2. Select File | Server properties.
  3. Select the Resources tab and set the properties.

Refer to the Jaguar CTS System Administration Guide for more information.

Making your code thread-safe

Open Server uses it's own implementation of threads with non-preemptive scheduling. Jaguar uses native operating system threads with preemptive scheduling. Context switches were predictable in Open Server's non-preemptive environment. Jaguar does not support non-preemptive scheduling. As a result, context switches are unpredictable and managed by the operating system's thread management facility. You must modify your Open Server application so that when a context switch does occur, your resources (variables, data, and so on) are maintained and not overwritten by another thread.

Protecting data

In a multi-threaded program, it is possible for multiple "threads" of control to be active concurrently. These threads can overwrite each other's data, resulting in unpredictable behavior such as race conditions. Any data shared across threads is vulnerable to race conditions:

Consider the following code segment running in a traditional Open Server application:

static int x = 0; 
x += 100;

if (x > 1000)

x = 0;

A race condition cannot occur when this code runs because a context switch cannot occur with non-preemptive scheduling. However, when you move to Jaguar, the result of the execution of this code is unpredictable.

You can protect the previous code with an Open Server mutex:

static int x = 0; 
srv_lockmutex(mutex);
x += 100;

if (x > 1000)

x = 0;
srv_unlockmutex(mutex);

Identify sections of code that need explicit protection and protect them with any applicable synchronization mechanism such as Open Server mutexes, and test your software (for deadlocks, etc.).

Tools for protecting data

Solaris users may find tools in the SPARCworks/iMPact multithreaded development kit from SunSoft useful in analyzing your code to identify critical sections that need protection, and help test the software after protecting these sections. This kit contains tools such as LockLint, LoopTool, SPARCworks Debugger, and Thread Analyzer.

DLLs, shared objects, and makefiles

This section contains two sample makefiles. The first is an example of a traditional Open Server makefile for Solaris, which contains routines, such as main and srv_run, used to build an executable. The second makefile is used to build dynamic libraries for use with Jaguar. The main() logic has been moved to an init handler.

In addition to the sample makefiles there are instructions for building shared objects for Solaris and DLLs for NT.

Traditional Open Server makefile for Solaris

# A makefile that builds the Open Server executable, server
# server.exe on NT.## The main() function is in server.c.## The two files, handler1.c and handler2.c implement all the handlers, and
# compile into a static library, handler.a (handler.lib on NT).## All the remaining code is in files appl1.c and appl2.c, and compiles into a
# static library, appl.a (appl.lib on NT).#CFLAGS= -I$(SYBASE)/include -I.LIBS= -lsrv -lblk -lct -ltcl -lcs -lcomn -lintl -lm -lnsl -ldlLDFLAGS= -L$(SYBASE)/lib $(LIBS)server: server.o handler.a appl.a $(LINK.c) -o $@ server.o handler.a appl.ahandler.a: handler1.o handler2.o $(AR) $(ARFLAGS) $@ handler1.o handler2.oappl.a: appl.o $(AR) $(ARFLAGS) $@ appl.o.c.o: $(COMPILE.c) $(OUTPUT_OPTION) $<

Makefile for Jaguar

# Build a dynamic library, server.so (server.dll on NT), instead of an
# executable.
#There is no main() function anymore: the applicable main() logic is in 
# the init handler function. The file is still server.c.
#
# The two files, handler1.c and handler2.c, implement all the handlers and
# compile into a dynamic library, handler.so (handler.dll on NT).
# Register each handler in Jaguar Manager.
#
# All the remaining code is in files appl.c, and compiles into a
# static library, appl.a (appl.lib on NT). No change here.#No need to link with libraries.
CFLAGS=                    -I$(JAGUAR)/include -I.

server.so: server.o handler.so appl.a
$(LINK.c) -G -o $@ server.o handler.so appl.a

handler.so : handler1.o handler2.o
$(LINK.c) -G -o $@ handler1.o handler2.o

appl.a: appl.o
$(AR) $(ARFLAGS) $@ appl.o

.c.o:
$(COMPILE.c) $(OUTPUT_OPTION) $<

Building shared objects on Solaris

When building shared objects on Solaris compile all the modules with the compile switches -KPIC -mt

The following link line should be used when creating a shared object on Solaris. Note that Jaguar libraries have a "j" prefix in them, for example, libjsrv_r.so:

 ld -g -o <shared object name> <object files> -ljsrv_r -ljct_r -ljcs_r\
-ljtcl_r -ljcomn_r -ljintl_r -ljtml_r -Bdynamic -lnsl -ldl -lthread -lm

Building DLLs on NT

When building DLLs on NT use the compile flags:

CFLAGS = /W3 /MD /nologo /Z7 /Od /DWIN32 /Gz

You need to export all handler functions. Use a .def file for this purpose and specify this .def file /def link option. For example:

# Definition file dependencies
LIBRARY sample INITINSTANCE
DESCRIPTION 'Jaguar Server event Handler'
HEAPSIZE 22000
PROTMODE
CODE LOADONCALL EXECUTEREAD NONCONFORMING
DATA PRELOAD READWRITE MULTIPLE NONSHARED
EXPORTS
connect_handler

Note   To run a server using an event-handler DLL, the directory containing the DLL must be specified in the PATH environment variable.

 


Copyright © 2000 Sybase, Inc. All rights reserved.