Keep-Alive with Worker Thread

Overview

This example demonstrates how to implement keep-alive processing by using multiple threads organized into a thread pool. Though this sample is a relatively straightforward implementation, it is helpful if you are at least somewhat familiar with the basics of thread management and communication in Microsoft Windows. For more information, see the relevant portions of the Windows and C++ documentation.

Code Tour

The extension performs a series of initialization tasks when first loaded. A work queue is created, implemented as a simple linked list, each element of the array consisting of a pointer to the ECB associated with the requesting client browser's session, and a pointer to the next work queue entry. Threads will check this queue to determine if there is any work to be done.

The extension initializes the thread pool and creates a semaphore, set initially to its nonsignaled state, that will be used to release threads from the thread pool. A critical section object is created and initialized as well. This will be used, along with the EnterCriticalSection and LeaveCriticalSection Windows functions, to maintain the integrity of the work queue. Last, but certainly not least, the Windows function CreateThread is invoked to populate the thread pool.

At this point in the execution of this extension, several things begin to happen at once, and like most multi-threaded applications, things can get confusing in a hurry. The main IIS server thread that initially loaded the DLL, and called DllMain in the first place, now proceeds to call the standard extension entry-point functions. GetExtensionVersion, as usual, returns version and descriptive information. HttpExtensionProc is executed and attempts to add another job to the work queue. If the function AddWorkQueueEntry returns FALSE, indicating that the queue is full, then the extension informs the client browser that the server is too busy, and provides a button with which the user can re-access the page. If there is space in the queue, ReleaseSemaphore is called, incrementing the semaphore by 1, and HttpExtensionProc exits with the return code HSE_STATUS_PENDING. This return code tells the server that there is still work pending, and the extension will notify the server when all of it is complete.

Meanwhile, immediately after each thread was created with CreateThread, each of them have been executing their individual WorkerFunction thread function. However, the thread accomplishes very little before it quickly arrives at the WaitForSingleObject function. There the thread will wait until the single object, the semaphore in this case, changes to a signaled state. Depending on the speed of your server and the number of threads created, all the threads in the thread pool may be waiting on the semaphore before the server even calls HttpExtensionProc.

The ReleaseSemaphore function, called when a new job has been entered into the work queue, releases one thread from the thread pool. When the semaphore is incremented by one, one thread's WaitForSingleObject call immediately decrements the semaphore by one, and returns. The thread is now free to complete the WorkerFunction function. First, GetWorkQueueEntry is called to get the pointer to the ECB of the next connection to be processed. Extended processing is simulated with the Sleep function, and a message is sent identifying the thread and connection IDs. Once the task is completed, the thread goes back to WaitForSingleObject, waiting for its chance to do it all over again.

Remarks

Notice that the thread never exits under normal conditions, but built-in to the logic of WorkerFunction are provisions for two types of pathological cases that you would want to account for in your own applications. If WaitForSingleObject returns a value other than WAIT_OBJECT_0 (such as WAIT_FAILED), the function returns, which implicitly causes the thread to exit. The other case, in this sample, is if a worker thread is released by the semaphore, but finds no jobs in the work queue. Since only one thread should be released for each semaphore signal event, a thread finding no jobs would indicate a serious problem, possibly at the operating system level. The thread would be explicitly ended at that point with the ExitThread function.

Note   You might notice that this extension never really terminates the connection with the client browser. Even though HSE_STATUS_SUCCESS_AND_KEEP_CONN does not necessarily guarantee that the server will keep the connection open, there are possible performance issues here for your server if multiple client browsers were accessing this sample. In addition, this sample creates several system objects that are designed to remain in memory until the server is rebooted. For this reason, as well as to make the demonstration more clear, the number of queue entries and threads has been limited to 2 each. If you increase this number (in the threadpool.h file) too much, you can expect to see a persistent, potentially dramatic decrease in system performance until you restart your server.

Location

This project is available in the ...\isapi\extensions\KeepAliveWithThread subdirectory of the IIS samples directory.