When the VMM calls an event callback function, it enters the function with the following registers set:
Register | Contents |
EBX | Current VM handle |
EDX | Programmer-defined reference data specified when the event service routine was installed |
EDI | Current thread handle. |
EBP | Client register structure |
Interrupts | Enabled |
Direction | Clear (up) |
Note
Some types of events can pass additional information in other registers, and other types of events do not always pass all the information listed above. Exceptions are noted.
The event callback procedure can modify EAX, EBX, ECX, EDX, ESI, and EDI. It must return with interrupts enabled and the direction flag cleared.
Since events are not synchronized with the virtual machine, the contents of the client registers are unpredictable. Altering the contents of the client registers can cause the virtual machine to get very confused. You must save the client state before modifying the client registers, then restore them before returning from the event.
Since event callbacks can be called while the current thread is blocked on a semaphore or other synchronization object, events should be extremely careful not to create deadlocks by attempting to claim a resource that may already be owned by the current thread. For example, consider a thread which takes a resource, then blocks waiting for some other operation to complete, with the intention of releasing the resource after the other operation has completed. While waiting for the semaphore to be signalled, that thread is used to perform an event callback is which attempts to take the same resource. The system is now deadlocked, because the event will wait indefinitely for the resource, which cannot be released until the event returns.
There are many important examples of this deadlock risk which are common sources of problems, so they will be given explicit attention.
Event callbacks may not touch pageable data or otherwise cause paging unless it can be ensured that the thread processing the event callback is not in the middle of its own swapping operation. One way to ensure this is to schedule the event as PEF_Wait_Not_Crit, because the critical section is held during paging. Another way to is to check explicitly whether paging is allowed now (e.g., PAGESWAP_Test_IO_Valid). Note that allocating memory from the VMM page manager or heap manager may result in paging, so services like _PageAllocate and _HeapFree are also forbidden unless it has already been determined that the thread is not paging. List management functions like List_Allocate are safe to call, provided the list was not created with the LF_HEAP or LF_SWAP bits.
Event callbacks may not access the registry unless it can be ensured that the thread processing the event callback is itself not active in the registry. This can be done by using the PEF_Wait_Not_Nested_Exec restriction on the event.
Even if you have ensured that blocking on a semaphore or other synchronization object will not cause a deadlock, bear in mind that blocking at event time seriously impacts the system's ability to multitask smoothly. The thread that got selected to service an event might own resources at ring 3, such as the Win16Mutex, for which other threads are waiting. (Indeed, the fact that the thread is running at all makes it much more likely that it owns such a resource.) While the event is in progress, those resources remain held by the thread even though the thread isn't doing anything with them.
Thus, as a general rule, you should not block at event time.