Question:
In a thread "Critical Section shared between ISAPI", David Wang promised a "blog entry about how ISAPI/CGI/ASP.Net binaries load in various IIS versions and configurations".
A serious background about process/thread issues in ISAPI and extension DLL loading/unloading sequence would be very much welcome, because 1) the topic was never covered thoroughly in official docs, 2) things changed in IIS6.
Here are few questions that come to my mind.
* Knowing if my extension will be loaded into one or more process spaces is crucial, because DLL's data will either be shared by instances, or exist as separate copies and this, of course, leads to different synchronization strategy. According to David, I can enforce IIS to keep my extension within one w3wp.exe process, provided I do not use Web Garden. Is this 100% guaranteed? I suspect it is not true on error or overload circumstances.
Let's assume 1st instance of worker process is not responding to ping. Will IIS try politely to terminate 1st instance BEFORE launching the 2nd one? From ISS point of view, 1st instance is inactive and useless, but actually it may still work and use shared resource.
* Is there any occasion for IIS to SUSPEND an extension's thread? (I mean - a thread of one instance, without suspending a thread of another instance). I hope not, because this would play havoc with any concurrency efforts!
* As Ian suggested in the same thread, mutex may solve the problem in case we are afraid of cross-process interaction. Fine, but sometimes we must have a singleton for other reasons than synchronization. My extension, for example, may load about 100-300MB in response to single request. I do not want this stuff to be loaded into memory twice. I need a single process instance - can I be sure?
* IIS5 docs claim that GetExtensionVersion and TerminateExtension functions get called once per DLL's lifetime in IIS space. Was it true? Is this still true in IIS6?
* Is it reasonable and save to spawn a new thread from within an extension? Is it save to leave "something" running between subsequent HTTPExtensionProc calls? (what I did is monitoring/logging thread that performs every 5 seconds or so) How about a timer?
* What is the relation of IIS6 "Application Pool" to Windows API "Thread Pool"?
Answer:
Excellent questions... Sorry I have not yet gotten around to writing that blog entry yet... I have many, many others yet to write. They are all in various stages of construction. Maybe during the upcoming holiday downtime I will find some time to wrap some of them up. But in the meantime, let me attend to your direct questions...
According to David, I can enforce IIS to keep my extension within one w3wp.exe process, provided I do not use Web Garden. Is this 100% guaranteed?
That conclusion is not 100% correct because the devil's in the details. I did not mention the details earlier because the simpler explanation of Web Garden suffices for most people. And... people's eyes usually glaze over when I start on the details, so I try to withhold them unless someone asks for them. ;-)
Now, IIS does NOT have a feature to enforce a "singleton w3wp.exe process loads a given ISAPI Extension DLL" no matter what. However, IIS does have features which when properly configured CAN result in a "singleton w3wp.exe process loading a given ISAPI Extension DLL".
Thus, if you can control IIS Application Pool and ISAPI Execution configuration for the entire server, you can create an environment of a "singleton w3wp.exe process loading a given ISAPI Extension DLL". If you cannot control those configurations for the entire IIS server, then you MUST rely on other mechanisms to ensure your desired behavior.
The following configuration options must be controlled on the entire server to create your desired behavior:
- DisallowOverlappingRotation - Ensure the replacement w3wp.exe process comes up AFTER the old w3wp.exe terminates.
- MaxProcesses - Maximum number of w3wp.exe to concurrently service a given Application Pool. Web Garden has a value greater than 1.
- AppPoolId - The Application Pool which services a given URL namespace. Since Application Pools partition the URL namespace and ISAPI Extensions load and execute inside some partition of the URL namespace serviced by IIS, controlling the Application Pool directly affects the number of times an ISAPI Extension DLL image loads.
- [Optional]ScriptMaps - Application Mappings determine which ISAPI Extension DLL or CGI EXE execute for any resource types. This configuration is at a per-URL level.
- The URL itself to invoke the ISAPI Extension DLL.
What you must ensure is that the URL used to invoke your ISAPI Extension DLL, and [optionally] any ScriptMap settings that map that ISAPI Extension DLL, all execute within one specific AppPoolId whose MaxProcesses value is 1 and DisallowOverlappingRotation is TRUE.
Now, I think I shall pause for a moment for the full meaning of the prior sentence to sink in... :-) Yes, there are many possible solution configurations, but they all fit the above criteria.
Of course, if you CANNOT control those IIS configuration values for the entire server, then you CANNOT ensure "singleton w3wp.exe process loads a given ISAPI Extension DLL" and must use other mechanisms. The only alternative I can think of is to load your code into a separate Singleton process on the server and inter-process communicate between it and w3wp.exe.
Will IIS try politely to terminate 1st instance BEFORE launching the 2nd one? From ISS point of view, 1st instance is inactive and useless, but actually it may still work and use shared resource.
IIS behavior depends on the configuration of DisallowOverlappingRotation. If FALSE (default), then IIS will launch the 2nd one before terminating the 1st one. If TRUE, then IIS will launch the 2nd one after the 1st one terminates and stops using shared resources.
* Is there any occasion for IIS to SUSPEND an extension's thread?
No, IIS does not suspend an ISAPI Extension's threads during execution.
* IIS5 docs claim that GetExtensionVersion and TerminateExtension functions get called once per DLL's lifetime in IIS space. Was it true? Is this still true in IIS6?
Yes and Yes. IIS always calls GetExtensionVersion() right after it loads the ISAPI DLL into a given process's memory address space and TerminateExtension() before unloading the ISAPI DLL from memory. In addition, various IIS features affect when an ISAPI DLL is loaded and unloaded.
Unfortunately, most people do not understand when an ISAPI DLL loads/unloads and incorrectly assume IIS guarantees random behavior/properties such as the number of times or timing of when those entry points are invoked... thus leading to confusion between IIS versions. In reality, you just need to understand when and what causes IIS to load/unload an ISAPI DLL.
IIS loads ISAPI Extension DLLs on demand to handle any given request. IIS also caches (i.e. leaves the DLL loaded in memory) the ISAPI Extension DLLs between requests to improve performance. This blog entry details how IIS selects the handler for a given request, so you know how to trigger your ISAPI Extension DLL. And the "Cache ISAPI application" checkbox in the "App Mappings" tab controls whether IIS loads/unloads the ISAPI Extension DLL between every single request, or just load on the first request and unload when the process terminates.
* Is it reasonable and save to spawn a new thread from within an extension? Is it save to leave "something" running between subsequent HTTPExtensionProc calls? (what I did is monitoring/logging thread that performs every 5 seconds or so) How about a timer?
Whether the action is "Reasonable" depends more on the function and design of the ISAPI Extension DLL. IIS places no requirements one way or another.
As for safety - it is safe to spawn your own threads from within an ISAPI Extension - for example, ISAPI Extension using a Thread Pool to queue/process requests asynchronously does this, by design. What you need to ensure is that if those threads touch the ECB (or any other memory, for that matter), that the ECB is valid. For synchronous request, the ECB will not be valid in between HttpExtensionProc calls (its lifetime is tied to the HttpExtensionProc lifetime for that particular request). For asynchronous requests, ECB will stay valid until HSE_REQ_DONE_WITH_SESSION is called on that ECB.
* What is the relation of IIS6 "Application Pool" to Windows API "Thread Pool"?
No direct relation. IIS6 "Application Pool" merely groups process-oriented configuration to partitions of URL namespace. "Thread Pool" is a method of scheduling code execution amongst threads vying for CPU time-slices
//David