windows-nt/Source/XPSP1/NT/base/ntos/ex/event.c
2020-09-26 16:20:57 +08:00

982 lines
24 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
event.c
Abstract:
This module implements the executive event object. Functions are provided
to create, open, set, reset, pulse, and query event objects.
Author:
David N. Cutler (davec) 8-May-1989
Environment:
Kernel mode only.
Revision History:
--*/
#include "exp.h"
//
// Temporary so boost is patchable
//
ULONG ExpEventBoost = EVENT_INCREMENT;
//
// Address of event object type descriptor.
//
POBJECT_TYPE ExEventObjectType;
//
// Structure that describes the mapping of generic access rights to object
// specific access rights for event objects.
//
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#endif
const GENERIC_MAPPING ExpEventMapping = {
STANDARD_RIGHTS_READ |
EVENT_QUERY_STATE,
STANDARD_RIGHTS_WRITE |
EVENT_MODIFY_STATE,
STANDARD_RIGHTS_EXECUTE |
SYNCHRONIZE,
EVENT_ALL_ACCESS
};
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, ExpEventInitialization)
#pragma alloc_text(PAGE, NtClearEvent)
#pragma alloc_text(PAGE, NtCreateEvent)
#pragma alloc_text(PAGE, NtOpenEvent)
#pragma alloc_text(PAGE, NtPulseEvent)
#pragma alloc_text(PAGE, NtQueryEvent)
#pragma alloc_text(PAGE, NtResetEvent)
#pragma alloc_text(PAGE, NtSetEvent)
#pragma alloc_text(PAGE, NtSetEventBoostPriority)
#endif
BOOLEAN
ExpEventInitialization (
)
/*++
Routine Description:
This function creates the event object type descriptor at system
initialization and stores the address of the object type descriptor
in global storage.
Arguments:
None.
Return Value:
A value of TRUE is returned if the event object type descriptor is
successfully initialized. Otherwise a value of FALSE is returned.
--*/
{
OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
NTSTATUS Status;
UNICODE_STRING TypeName;
//
// Initialize string descriptor.
//
RtlInitUnicodeString(&TypeName, L"Event");
//
// Create event object type descriptor.
//
RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
ObjectTypeInitializer.GenericMapping = ExpEventMapping;
ObjectTypeInitializer.PoolType = NonPagedPool;
ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KEVENT);
ObjectTypeInitializer.ValidAccessMask = EVENT_ALL_ACCESS;
Status = ObCreateObjectType(&TypeName,
&ObjectTypeInitializer,
(PSECURITY_DESCRIPTOR)NULL,
&ExEventObjectType);
//
// If the event object type descriptor was successfully created, then
// return a value of TRUE. Otherwise return a value of FALSE.
//
return (BOOLEAN)(NT_SUCCESS(Status));
}
NTSTATUS
NtClearEvent (
IN HANDLE EventHandle
)
/*++
Routine Description:
This function sets an event object to a Not-Signaled state.
Arguments:
EventHandle - Supplies a handle to an event object.
Return Value:
NTSTATUS.
--*/
{
PVOID Event;
NTSTATUS Status;
//
// Reference event object by handle.
//
Status = ObReferenceObjectByHandle(EventHandle,
EVENT_MODIFY_STATE,
ExEventObjectType,
KeGetPreviousMode(),
&Event,
NULL);
//
// If the reference was successful, then set the state of the event
// object to Not-Signaled and dereference event object.
//
if (NT_SUCCESS(Status)) {
PERFINFO_DECLARE_OBJECT(Event);
KeClearEvent((PKEVENT)Event);
ObDereferenceObject(Event);
}
//
// Return service status.
//
return Status;
}
NTSTATUS
NtCreateEvent (
OUT PHANDLE EventHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN EVENT_TYPE EventType,
IN BOOLEAN InitialState
)
/*++
Routine Description:
This function creates an event object, sets it initial state to the
specified value, and opens a handle to the object with the specified
desired access.
Arguments:
EventHandle - Supplies a pointer to a variable that will receive the
event object handle.
DesiredAccess - Supplies the desired types of access for the event object.
ObjectAttributes - Supplies a pointer to an object attributes structure.
EventType - Supplies the type of the event (autoclearing or notification).
InitialState - Supplies the initial state of the event object.
Return Value:
NTSTATUS.
--*/
{
PVOID Event;
HANDLE Handle;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
//
// Establish an exception handler, probe the output handle address, and
// attempt to create an event object. If the probe fails, then return the
// exception code as the service status. Otherwise return the status value
// returned by the object insertion routine.
//
PreviousMode = KeGetPreviousMode();
//
// Get previous processor mode and probe output handle address if
// necessary.
//
if (PreviousMode != KernelMode) {
try {
ProbeForWriteHandle(EventHandle);
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
}
//
// Check argument validity.
//
if ((EventType != NotificationEvent) && (EventType != SynchronizationEvent)) {
return STATUS_INVALID_PARAMETER;
}
//
// Allocate event object.
//
Status = ObCreateObject(PreviousMode,
ExEventObjectType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(KEVENT),
0,
0,
(PVOID *)&Event);
//
// If the event object was successfully allocated, then initialize the
// event object and attempt to insert the event object in the current
// process' handle table.
//
if (NT_SUCCESS(Status)) {
KeInitializeEvent((PKEVENT)Event, EventType, InitialState);
Status = ObInsertObject(Event,
NULL,
DesiredAccess,
0,
(PVOID *)NULL,
&Handle);
//
// If the event object was successfully inserted in the current
// process' handle table, then attempt to write the event object
// handle value. If the write attempt fails, then do not report
// an error. When the caller attempts to access the handle value,
// an access violation will occur.
//
if (NT_SUCCESS(Status)) {
if (PreviousMode != KernelMode) {
try {
*EventHandle = Handle;
} except(ExSystemExceptionFilter()) {
NOTHING;
}
}
else {
*EventHandle = Handle;
}
}
}
//
// Return service status.
//
return Status;
}
NTSTATUS
NtOpenEvent (
OUT PHANDLE EventHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
)
/*++
Routine Description:
This function opens a handle to an event object with the specified
desired access.
Arguments:
EventHandle - Supplies a pointer to a variable that will receive the
event object handle.
DesiredAccess - Supplies the desired types of access for the event object.
ObjectAttributes - Supplies a pointer to an object attributes structure.
Return Value:
NTSTATUS.
--*/
{
HANDLE Handle;
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
//
// Establish an exception handler, probe the output handle address, and
// attempt to open the event object. If the probe fails, then return the
// exception code as the service status. Otherwise return the status value
// returned by the object open routine.
//
//
// Get previous processor mode and probe output handle address
// if necessary.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
try {
ProbeForWriteHandle(EventHandle);
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
}
//
// Open handle to the event object with the specified desired access.
//
Status = ObOpenObjectByName(ObjectAttributes,
ExEventObjectType,
PreviousMode,
NULL,
DesiredAccess,
NULL,
&Handle);
//
// If the open was successful, then attempt to write the event object
// handle value. If the write attempt fails, then do not report an
// error. When the caller attempts to access the handle value, an
// access violation will occur.
//
if (NT_SUCCESS(Status)) {
if (PreviousMode != KernelMode) {
try {
*EventHandle = Handle;
} except(ExSystemExceptionFilter()) {
NOTHING;
}
}
else {
*EventHandle = Handle;
}
}
//
// Return service status.
//
return Status;
}
NTSTATUS
NtPulseEvent (
IN HANDLE EventHandle,
OUT PLONG PreviousState OPTIONAL
)
/*++
Routine Description:
This function sets an event object to a Signaled state, attempts to
satisfy as many waits as possible, and then resets the state of the
event object to Not-Signaled.
Arguments:
EventHandle - Supplies a handle to an event object.
PreviousState - Supplies an optional pointer to a variable that will
receive the previous state of the event object.
Return Value:
NTSTATUS.
--*/
{
PVOID Event;
KPROCESSOR_MODE PreviousMode;
LONG State;
NTSTATUS Status;
//
// Establish an exception handler, probe the previous state address if
// specified, reference the event object, and pulse the event object. If
// the probe fails, then return the exception code as the service status.
// Otherwise return the status value returned by the reference object by
// handle routine.
//
//
// Get previous processor mode and probe previous state address
// if necessary.
//
PreviousMode = KeGetPreviousMode();
if (ARGUMENT_PRESENT(PreviousState) && (PreviousMode != KernelMode)) {
try {
ProbeForWriteLong(PreviousState);
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
}
//
// Reference event object by handle.
//
Status = ObReferenceObjectByHandle(EventHandle,
EVENT_MODIFY_STATE,
ExEventObjectType,
PreviousMode,
&Event,
NULL);
//
// If the reference was successful, then pulse the event object,
// dereference event object, and write the previous state value if
// specified. If the write of the previous state fails, then do not
// report an error. When the caller attempts to access the previous
// state value, an access violation will occur.
//
if (NT_SUCCESS(Status)) {
PERFINFO_DECLARE_OBJECT(Event);
State = KePulseEvent((PKEVENT)Event, ExpEventBoost, FALSE);
ObDereferenceObject(Event);
if (ARGUMENT_PRESENT(PreviousState)) {
if (PreviousMode != KernelMode) {
try {
*PreviousState = State;
} except(ExSystemExceptionFilter()) {
NOTHING;
}
}
else {
*PreviousState = State;
}
}
}
//
// Return service status.
//
return Status;
}
NTSTATUS
NtQueryEvent (
IN HANDLE EventHandle,
IN EVENT_INFORMATION_CLASS EventInformationClass,
OUT PVOID EventInformation,
IN ULONG EventInformationLength,
OUT PULONG ReturnLength OPTIONAL
)
/*++
Routine Description:
This function queries the state of an event object and returns the
requested information in the specified record structure.
Arguments:
EventHandle - Supplies a handle to an event object.
EventInformationClass - Supplies the class of information being requested.
EventInformation - Supplies a pointer to a record that is to receive the
requested information.
EventInformationLength - Supplies the length of the record that is to
receive the requested information.
ReturnLength - Supplies an optional pointer to a variable that is to
receive the actual length of information that is returned.
Return Value:
NTSTATUS.
--*/
{
PKEVENT Event;
KPROCESSOR_MODE PreviousMode;
LONG State;
NTSTATUS Status;
EVENT_TYPE EventType;
//
// Check argument validity.
//
if (EventInformationClass != EventBasicInformation) {
return STATUS_INVALID_INFO_CLASS;
}
if (EventInformationLength != sizeof(EVENT_BASIC_INFORMATION)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
//
// Establish an exception handler, probe the output arguments, reference
// the event object, and return the specified information. If the probe
// fails, then return the exception code as the service status. Otherwise
// return the status value returned by the reference object by handle
// routine.
//
//
// Get previous processor mode and probe output arguments if necessary.
//
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
try {
ProbeForWrite(EventInformation,
sizeof(EVENT_BASIC_INFORMATION),
sizeof(ULONG));
if (ARGUMENT_PRESENT(ReturnLength)) {
ProbeForWriteUlong(ReturnLength);
}
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
}
//
// Reference event object by handle.
//
Status = ObReferenceObjectByHandle(EventHandle,
EVENT_QUERY_STATE,
ExEventObjectType,
PreviousMode,
(PVOID *)&Event,
NULL);
//
// If the reference was successful, then read the current state of
// the event object, deference event object, fill in the information
// structure, and return the length of the information structure if
// specified. If the write of the event information or the return
// length fails, then do not report an error. When the caller accesses
// the information structure or length an access violation will occur.
//
if (NT_SUCCESS(Status)) {
State = KeReadStateEvent(Event);
EventType = Event->Header.Type;
ObDereferenceObject(Event);
if (PreviousMode != KernelMode) {
try {
((PEVENT_BASIC_INFORMATION)EventInformation)->EventType = EventType;
((PEVENT_BASIC_INFORMATION)EventInformation)->EventState = State;
if (ARGUMENT_PRESENT(ReturnLength)) {
*ReturnLength = sizeof(EVENT_BASIC_INFORMATION);
}
} except(ExSystemExceptionFilter()) {
NOTHING;
}
}
else {
((PEVENT_BASIC_INFORMATION)EventInformation)->EventType = EventType;
((PEVENT_BASIC_INFORMATION)EventInformation)->EventState = State;
if (ARGUMENT_PRESENT(ReturnLength)) {
*ReturnLength = sizeof(EVENT_BASIC_INFORMATION);
}
}
}
//
// Return service status.
//
return Status;
}
NTSTATUS
NtResetEvent (
IN HANDLE EventHandle,
OUT PLONG PreviousState OPTIONAL
)
/*++
Routine Description:
This function sets an event object to a Not-Signaled state.
Arguments:
EventHandle - Supplies a handle to an event object.
PreviousState - Supplies an optional pointer to a variable that will
receive the previous state of the event object.
Return Value:
NTSTATUS.
--*/
{
PVOID Event;
KPROCESSOR_MODE PreviousMode;
LONG State;
NTSTATUS Status;
//
// Establish an exception handler, probe the previous state address if
// specified, reference the event object, and reset the event object. If
// the probe fails, then return the exception code as the service status.
// Otherwise return the status value returned by the reference object by
// handle routine.
//
//
// Get previous processor mode and probe previous state address
// if necessary.
//
PreviousMode = KeGetPreviousMode();
if ((ARGUMENT_PRESENT(PreviousState)) && (PreviousMode != KernelMode)) {
try {
ProbeForWriteLong(PreviousState);
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
}
//
// Reference event object by handle.
//
Status = ObReferenceObjectByHandle(EventHandle,
EVENT_MODIFY_STATE,
ExEventObjectType,
PreviousMode,
&Event,
NULL);
//
// If the reference was successful, then set the state of the event
// object to Not-Signaled, dereference event object, and write the
// previous state value if specified. If the write of the previous
// state fails, then do not report an error. When the caller attempts
// to access the previous state value, an access violation will occur.
//
if (NT_SUCCESS(Status)) {
PERFINFO_DECLARE_OBJECT(Event);
State = KeResetEvent((PKEVENT)Event);
ObDereferenceObject(Event);
if (ARGUMENT_PRESENT(PreviousState)) {
if (PreviousMode != KernelMode) {
try {
*PreviousState = State;
} except(ExSystemExceptionFilter()) {
NOTHING;
}
}
else {
*PreviousState = State;
}
}
}
//
// Return service status.
//
return Status;
}
NTSTATUS
NtSetEvent (
IN HANDLE EventHandle,
OUT PLONG PreviousState OPTIONAL
)
/*++
Routine Description:
This function sets an event object to a Signaled state and attempts to
satisfy as many waits as possible.
Arguments:
EventHandle - Supplies a handle to an event object.
PreviousState - Supplies an optional pointer to a variable that will
receive the previous state of the event object.
Return Value:
NTSTATUS.
--*/
{
PVOID Event;
KPROCESSOR_MODE PreviousMode;
LONG State;
NTSTATUS Status;
#if DBG
//
// Sneaky trick here to catch sleazy apps (csrss) that erroneously call
// NtSetEvent on an event that happens to be somebody else's
// critical section. Only allow setting a protected handle if the low
// bit of PreviousState is set.
//
OBJECT_HANDLE_INFORMATION HandleInfo;
#endif
//
// Establish an exception handler, probe the previous state address if
// specified, reference the event object, and set the event object. If
// the probe fails, then return the exception code as the service status.
// Otherwise return the status value returned by the reference object by
// handle routine.
//
//
// Get previous processor mode and probe previous state address
// if necessary.
//
PreviousMode = KeGetPreviousMode();
try {
#if DBG
if ((PreviousMode != KernelMode) &&
(ARGUMENT_PRESENT(PreviousState)) &&
(PreviousState != (PLONG)1)) {
ProbeForWriteLong(PreviousState);
}
#else
if ((PreviousMode != KernelMode) && (ARGUMENT_PRESENT(PreviousState))) {
ProbeForWriteLong(PreviousState);
}
#endif
//
// Reference event object by handle.
//
#if DBG
Status = ObReferenceObjectByHandle(EventHandle,
EVENT_MODIFY_STATE,
ExEventObjectType,
PreviousMode,
&Event,
&HandleInfo);
if (NT_SUCCESS(Status)) {
if ((HandleInfo.HandleAttributes & 1) &&
(PreviousState != (PLONG)1)) {
#if 0
//
// This is a protected handle. If the low bit of PreviousState is NOT set,
// break into the debugger
//
DbgPrint("NtSetEvent: Illegal call to NtSetEvent on a protected handle\n");
DbgBreakPoint();
PreviousState = NULL;
#endif
}
} else {
if ((KeGetPreviousMode() != KernelMode) &&
(EventHandle != NULL) &&
((NtGlobalFlag & FLG_ENABLE_CLOSE_EXCEPTIONS) ||
(PsGetCurrentProcess()->DebugPort != NULL))) {
return KeRaiseUserException(STATUS_INVALID_HANDLE);
}
}
#else
Status = ObReferenceObjectByHandle(EventHandle,
EVENT_MODIFY_STATE,
ExEventObjectType,
PreviousMode,
&Event,
NULL);
#endif
//
// If the reference was successful, then set the event object to the
// Signaled state, dereference event object, and write the previous
// state value if specified. If the write of the previous state fails,
// then do not report an error. When the caller attempts to access the
// previous state value, an access violation will occur.
//
if (NT_SUCCESS(Status)) {
PERFINFO_DECLARE_OBJECT(Event);
State = KeSetEvent((PKEVENT)Event, ExpEventBoost, FALSE);
ObDereferenceObject(Event);
if (ARGUMENT_PRESENT(PreviousState)) {
try {
*PreviousState = State;
} except(ExSystemExceptionFilter()) {
}
}
}
//
// If an exception occurs during the probe of the previous state, then
// always handle the exception and return the exception code as the status
// value.
//
} except(ExSystemExceptionFilter()) {
return GetExceptionCode();
}
//
// Return service status.
//
return Status;
}
NTSTATUS
NtSetEventBoostPriority (
IN HANDLE EventHandle
)
/*++
Routine Description:
This function sets an event object to a Signaled state and performs
a special priority boost operation.
N.B. This service can only be performed on synchronization events.
Arguments:
EventHandle - Supplies a handle to an event object.
PreviousState - Supplies an optional pointer to a variable that will
receive the previous state of the event object.
Return Value:
NTSTATUS.
--*/
{
PKEVENT Event;
NTSTATUS Status;
//
// Reference event object by handle.
//
Status = ObReferenceObjectByHandle(EventHandle,
EVENT_MODIFY_STATE,
ExEventObjectType,
KeGetPreviousMode(),
&Event,
NULL);
//
// If the reference was successful, then check the type of event object.
// If the event object is a notification event, then return an object
// type mismatch status. Otherwise, set the specified event and boost
// the unwaited thread priority as appropriate.
//
if (NT_SUCCESS(Status)) {
if (Event->Header.Type == NotificationEvent) {
Status = STATUS_OBJECT_TYPE_MISMATCH;
} else {
KeSetEventBoostPriority(Event, NULL);
}
ObDereferenceObject(Event);
}
//
// Return service status.
//
return Status;
}