Migration overview  Modified APIs and new event handlers

Appendix B: Migrating Open Server Applications to EAServer

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 EAServer automatically initiates, and remove properties and handlers that are configured through EAServer 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 EAServer Manager. EAServer
** 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 EAServer, using
** EAServer Manager.
*/CS_RETCODE CS_PUBLIC init_handler(CS_CONTEXT *context, 
		int argc, char *argv[])
{
		/*
		** Variables.
		**
		** EAServer 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 EAServer 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 EAServer Manager.
		*/
	
		/*
		** Start server.
		**
		** EAServer 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.

You must set the following properties using EAServer Manager:

Do not set these properties using srv_props() calls.

To set these properties from EAServer 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 Chapter 3, “Creating and Configuring Servers,” in the EAServer System Administration Guide for more information.


Making your code thread-safe

Open Server uses it’s own implementation of threads with non-preemptive scheduling. EAServer uses native operating system threads with preemptive scheduling. Context switches were predictable in Open Server’s non-preemptive environment. EAServer 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 EAServer, 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 EAServer. The main() logic has been moved to an init handler.

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

Traditional Open Server makefile for Solaris

# A makefile that builds the Open Server executable, server
# server.exe on Windows.## 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 Windows).## All the remaining code is in files appl1.c and appl2.c, and compiles into a
# static library, appl.a (appl.lib on Windows).#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 EAServer

# Build a dynamic library, server.so (server.dll on Windows), 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 Windows).
# Register each handler in EAServer Manager.
#
# All the remaining code is in files appl.c, and compiles into a
# static library, appl.a (appl.lib on Windows). 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 EAServer 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 Windows

When building DLLs on Windows 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  'EAServer event Handler'
HEAPSIZE      22000
PROTMODE
CODE LOADONCALL EXECUTEREAD NONCONFORMING
DATA PRELOAD READWRITE MULTIPLE NONSHARED
EXPORTS
connect_handler

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





Copyright © 2005. Sybase Inc. All rights reserved. Modified APIs and new event handlers