Programming considerations when using the "out-of-process" model
The out-of-process ISAPI runner imposes different constraints from those involved when running ISAPI in-process. These are addressed below. See also the manual page for isapisnoop, a tracing tool for use with the out-of-process ISAPI runner. Contents:
- Multi-threaded vs Single-threaded
- Unsupported ISAPI functionality in the "out-of-process" model
- Extended Notification of filters and extensions.
Multi-threaded vs Single-threaded
One of the main differences between running an ISAPI filter or extension as "out-of-process" is that it will run in a multi-threaded environment, as opposed to running "in-process" where it will run in a single-threaded application.
When writing an ISAPI module to be run "out-of-process", you must take care so that your ISAPI application is thread safe.
For example, let's take one of the examples in the ISAPI Introduction:
/* Import the ISAPI constants and type definitions */ #include "httpext.h" BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer ) { pVer->dwExtensionVersion = HSE_VERSION; strncpy( pVer->lpszExtensionDesc, "A simple page counter", HSE_MAX_EXT_DLL_NAME_LEN ); return TRUE; } static unsigned int hits = 0; DWORD WINAPI HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK ecb ) { char *header = "Content-Type: text/html"; int headerlen = strlen( header ); char msg[256]; int msglen; /* use a server support function to write out a header with our additional header information */ ecb->ServerSupportFunction( ecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, 0, &headerlen, (DWORD *)header ); /* write out the number of accesses */ sprintf( msg, "This page has been accessed %d times", ++hits ); msglen = strlen( msg ); ecb->WriteClient( ecb->ConnID, msg, &msglen,0 ); /* return, indicating success */ return 0; }
as you can see in the code, this extension counts the number of times it was invoked using a global variable called hits. The problem with this architecture is that if it is running in a multi-threaded environment, the global variable hits isn't thread safe, because more than one thread can access it at the same time.
To make it thread safe, we need to wrap this variable with a mutex variable, so that no more than one thread can access it at the same time:
/* Import the ISAPI constants and type definitions */ #include "httpext.h" #include <pthread.h> /* Assuming you are using POSIX threads. */ static unsigned int hits = 0; static pthread_mutex_t hits_mutex; BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer ) { pVer->dwExtensionVersion = HSE_VERSION; strncpy( pVer->lpszExtensionDesc, "A simple page counter", HSE_MAX_EXT_DLL_NAME_LEN ); /* Initialize the Mutex */ pthread_mutex_init( &hits_mutex, NULL ); return TRUE; } DWORD WINAPI HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK ecb ) { char *header = "Content-Type: text/html"; int headerlen = strlen( header ); char msg[256]; int msglen; /* use a server support function to write out a header with our additional header information */ ecb->ServerSupportFunction( ecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, 0, &headerlen, (DWORD *)header ); /* write out the number of accesses */ /* try to lock the mutex */ pthread_mutex_lock( &hits_mutex ); sprintf( msg, "This page has been accessed %d times", ++hits ); /* now unlock the mutex */ pthread_mutex_unlock( &hits_mutex ); msglen = strlen( msg ); ecb->WriteClient( ecb->ConnID, msg, &msglen,0 ); /* return, indicating success */ return 0; }
We defined
sprintf( msg, "This page has been accessed %d times", ++hits );as a critical section of the code, by wrapping it with a mutex lock/unlock.
You must also take care with what system library calls you use in your application, as some parts of your systems libraries might not be thread safe. Please read the systems programming documentation of your operating system if in doubt.
Unsupported ISAPI functionality in the "out-of-process" model
Because of the difference in architecture of the "out-of-process" model, not all ISAPI functionality is supported. The following is a list of request codes (HSE_... for extensions, SF_... for filters) not supported by ServerSupportFunction() when running "out-of-process":
- HSE_REQ_GET_FILE_DESCRIPTOR
- HSE_REQ_TRANSMIT_FILE
- SF_REQ_GET_FILE_DESCRIPTOR
Extended Notification of filters and extensions.
When running "out-of-process", ISAPI handlers (filters and extensions) have available to them an additional notification mechanism besides the Get*Version() and Http*Proc() functions. If your handler defines the following function:
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved );it will be called when the handler is loaded and unloaded, and when a thread using it starts or exits. This function is provided to ease porting of ISAPI applications from Windows, which uses DllMain() to manage loading, etc. of dynamically-linked libraries. When ZWS calls DllMain(), it passes NULL (that is, 0) values for the hinstDLL and lpvReserved parameters. Aside from the initial call while loading a handler (see DLL_PROCESS_ATTACH below), ZWS ignores the return value from DllMain().
The fdwReason value passed to DllMain() indicates the reason for the call:
fdwReason Meaning DLL_PROCESS_ATTACH The module was just loaded: Get*Version() will be called next. If DllMain() returns FALSE, then loading this module will be aborted DLL_PROCESS_DETACH The module is about to be unloaded. DLL_THREAD_ATTACH A new thread was just created. DLL_THREAD_DETACH A thread is exiting.