/* * @implemented */ LONG NTAPI KePulseEvent(IN PKEVENT Event, IN KPRIORITY Increment, IN BOOLEAN Wait) { KIRQL OldIrql; LONG PreviousState; PKTHREAD Thread; ASSERT_EVENT(Event); ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); /* Lock the Dispatcher Database */ OldIrql = KiAcquireDispatcherLock(); /* Save the Old State */ PreviousState = Event->Header.SignalState; /* Check if we are non-signaled and we have stuff in the Wait Queue */ if (!PreviousState && !IsListEmpty(&Event->Header.WaitListHead)) { /* Set the Event to Signaled */ Event->Header.SignalState = 1; /* Wake the Event */ KiWaitTest(&Event->Header, Increment); } /* Unsignal it */ Event->Header.SignalState = 0; /* Check what wait state was requested */ if (Wait == FALSE) { /* Wait not requested, release Dispatcher Database and return */ KiReleaseDispatcherLock(OldIrql); } else { /* Return Locked and with a Wait */ Thread = KeGetCurrentThread(); Thread->WaitNext = TRUE; Thread->WaitIrql = OldIrql; } /* Return the previous State */ return PreviousState; }
/* * @implemented */ VOID NTAPI KeInitializeMutant(IN PKMUTANT Mutant, IN BOOLEAN InitialOwner) { PKTHREAD CurrentThread; KIRQL OldIrql; /* Check if we have an initial owner */ if (InitialOwner) { /* We also need to associate a thread */ CurrentThread = KeGetCurrentThread(); Mutant->OwnerThread = CurrentThread; /* We're about to touch the Thread, so lock the Dispatcher */ OldIrql = KiAcquireDispatcherLock(); /* And insert it into its list */ InsertTailList(&CurrentThread->MutantListHead, &Mutant->MutantListEntry); /* Release Dispatcher Lock */ KiReleaseDispatcherLock(OldIrql); } else { /* In this case, we don't have an owner yet */ Mutant->OwnerThread = NULL; } /* Now we set up the Dispatcher Header */ Mutant->Header.Type = MutantObject; Mutant->Header.Size = sizeof(KMUTANT) / sizeof(ULONG); Mutant->Header.SignalState = InitialOwner ? 0 : 1; InitializeListHead(&(Mutant->Header.WaitListHead)); /* Initialize the default data */ Mutant->Abandoned = FALSE; Mutant->ApcDisable = 0; }
/* * @implemented */ LONG NTAPI KeResetEvent(IN PKEVENT Event) { KIRQL OldIrql; LONG PreviousState; ASSERT_EVENT(Event); ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); /* Lock the Dispatcher Database */ OldIrql = KiAcquireDispatcherLock(); /* Save the Previous State */ PreviousState = Event->Header.SignalState; /* Set it to zero */ Event->Header.SignalState = 0; /* Release Dispatcher Database and return previous state */ KiReleaseDispatcherLock(OldIrql); return PreviousState; }
/* * @implemented */ BOOLEAN NTAPI KeCancelTimer(IN OUT PKTIMER Timer) { KIRQL OldIrql; BOOLEAN Inserted; ASSERT_TIMER(Timer); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); DPRINT("KeCancelTimer(): Timer %p\n", Timer); /* Lock the Database and Raise IRQL */ OldIrql = KiAcquireDispatcherLock(); /* Check if it's inserted, and remove it if it is */ Inserted = Timer->Header.Inserted; if (Inserted) KxRemoveTreeTimer(Timer); /* Release Dispatcher Lock */ KiReleaseDispatcherLock(OldIrql); /* Return the old state */ return Inserted; }
/* * @implemented */ LONG NTAPI KeSetEvent(IN PKEVENT Event, IN KPRIORITY Increment, IN BOOLEAN Wait) { KIRQL OldIrql; LONG PreviousState; PKTHREAD Thread; ASSERT_EVENT(Event); ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); /* * Check if this is an signaled notification event without an upcoming wait. * In this case, we can immediately return TRUE, without locking. */ if ((Event->Header.Type == EventNotificationObject) && (Event->Header.SignalState == 1) && !(Wait)) { /* Return the signal state (TRUE/Signalled) */ return TRUE; } /* Lock the Dispathcer Database */ OldIrql = KiAcquireDispatcherLock(); /* Save the Previous State */ PreviousState = Event->Header.SignalState; /* Set the Event to Signaled */ Event->Header.SignalState = 1; /* Check if the event just became signaled now, and it has waiters */ if (!(PreviousState) && !(IsListEmpty(&Event->Header.WaitListHead))) { /* Check the type of event */ if (Event->Header.Type == EventNotificationObject) { /* Unwait the thread */ KxUnwaitThread(&Event->Header, Increment); } else { /* Otherwise unwait the thread and unsignal the event */ KxUnwaitThreadForEvent(Event, Increment); } } /* Check what wait state was requested */ if (!Wait) { /* Wait not requested, release Dispatcher Database and return */ KiReleaseDispatcherLock(OldIrql); } else { /* Return Locked and with a Wait */ Thread = KeGetCurrentThread(); Thread->WaitNext = TRUE; Thread->WaitIrql = OldIrql; } /* Return the previous State */ return PreviousState; }
/* * @implemented */ BOOLEAN NTAPI KeDisconnectInterrupt(IN PKINTERRUPT Interrupt) { KIRQL OldIrql, Irql; ULONG Vector; DISPATCH_INFO Dispatch; PKINTERRUPT NextInterrupt; BOOLEAN State; /* Set the affinity */ KeSetSystemAffinityThread(1 << Interrupt->Number); /* Lock the dispatcher */ OldIrql = KiAcquireDispatcherLock(); /* Check if it's actually connected */ State = Interrupt->Connected; if (State) { /* Get the vector and IRQL */ Irql = Interrupt->Irql; Vector = Interrupt->Vector; /* Get vector dispatch data */ KiGetVectorDispatch(Vector, &Dispatch); /* Check if it was chained */ if (Dispatch.Type == ChainConnect) { /* Check if the top-level interrupt is being removed */ ASSERT(Irql <= SYNCH_LEVEL); if (Interrupt == Dispatch.Interrupt) { /* Get the next one */ Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt-> InterruptListEntry.Flink, KINTERRUPT, InterruptListEntry); /* Reconnect it */ KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect); } /* Remove it */ RemoveEntryList(&Interrupt->InterruptListEntry); /* Get the next one */ NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt-> InterruptListEntry.Flink, KINTERRUPT, InterruptListEntry); /* Check if this is the only one left */ if (Dispatch.Interrupt == NextInterrupt) { /* Connect it in non-chained mode */ KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect); } } else { /* Only one left, disable and remove it */ HalDisableSystemInterrupt(Interrupt->Vector, Irql); KiConnectVectorToInterrupt(Interrupt, NoConnect); } /* Disconnect it */ Interrupt->Connected = FALSE; } /* Unlock the dispatcher and revert affinity */ KiReleaseDispatcherLock(OldIrql); KeRevertToUserAffinityThread(); /* Return to caller */ return State; }
/* * @implemented */ BOOLEAN NTAPI KeConnectInterrupt(IN PKINTERRUPT Interrupt) { BOOLEAN Connected, Error, Status; KIRQL Irql, OldIrql; UCHAR Number; ULONG Vector; DISPATCH_INFO Dispatch; /* Get data from interrupt */ Number = Interrupt->Number; Vector = Interrupt->Vector; Irql = Interrupt->Irql; /* Validate the settings */ if ((Irql > HIGH_LEVEL) || (Number >= KeNumberProcessors) || (Interrupt->SynchronizeIrql < Irql) || (Interrupt->FloatingSave)) { return FALSE; } /* Set defaults */ Connected = FALSE; Error = FALSE; /* Set the system affinity and acquire the dispatcher lock */ KeSetSystemAffinityThread(1 << Number); OldIrql = KiAcquireDispatcherLock(); /* Check if it's already been connected */ if (!Interrupt->Connected) { /* Get vector dispatching information */ KiGetVectorDispatch(Vector, &Dispatch); /* Check if the vector is already connected */ if (Dispatch.Type == NoConnect) { /* Do the connection */ Interrupt->Connected = Connected = TRUE; /* Initialize the list */ InitializeListHead(&Interrupt->InterruptListEntry); /* Connect and enable the interrupt */ KiConnectVectorToInterrupt(Interrupt, NormalConnect); Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode); if (!Status) Error = TRUE; } else if ((Dispatch.Type != UnknownConnect) && (Interrupt->ShareVector) && (Dispatch.Interrupt->ShareVector) && (Dispatch.Interrupt->Mode == Interrupt->Mode)) { /* The vector is shared and the interrupts are compatible */ Interrupt->Connected = Connected = TRUE; /* FIXME */ // ASSERT(Irql <= SYNCH_LEVEL); /* Check if this is the first chain */ if (Dispatch.Type != ChainConnect) { /* This is not supported */ ASSERT(Dispatch.Interrupt->Mode != Latched); /* Setup the chainned handler */ KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect); } /* Insert into the interrupt list */ InsertTailList(&Dispatch.Interrupt->InterruptListEntry, &Interrupt->InterruptListEntry); } } /* Unlock the dispatcher and revert affinity */ KiReleaseDispatcherLock(OldIrql); KeRevertToUserAffinityThread(); /* Check if we failed while trying to connect */ if ((Connected) && (Error)) { DPRINT1("HalEnableSystemInterrupt failed\n"); KeDisconnectInterrupt(Interrupt); Connected = FALSE; } /* Return to caller */ return Connected; }
VOID NTAPI KiTimerExpiration(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { ULARGE_INTEGER SystemTime, InterruptTime; LARGE_INTEGER Interval; LONG Limit, Index, i; ULONG Timers, ActiveTimers, DpcCalls; PLIST_ENTRY ListHead, NextEntry; KIRQL OldIrql; PKTIMER Timer; PKDPC TimerDpc; ULONG Period; DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; PKSPIN_LOCK_QUEUE LockQueue; #ifdef CONFIG_SMP PKPRCB Prcb = KeGetCurrentPrcb(); #endif /* Disable interrupts */ _disable(); /* Query system and interrupt time */ KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); InterruptTime.QuadPart = KeQueryInterruptTime(); Limit = KeTickCount.LowPart; /* Bring interrupts back */ _enable(); /* Get the index of the timer and normalize it */ Index = PtrToLong(SystemArgument1); if ((Limit - Index) >= TIMER_TABLE_SIZE) { /* Normalize it */ Limit = Index + TIMER_TABLE_SIZE - 1; } /* Setup index and actual limit */ Index--; Limit &= (TIMER_TABLE_SIZE - 1); /* Setup accounting data */ DpcCalls = 0; Timers = 24; ActiveTimers = 4; /* Lock the Database and Raise IRQL */ OldIrql = KiAcquireDispatcherLock(); /* Start expiration loop */ do { /* Get the current index */ Index = (Index + 1) & (TIMER_TABLE_SIZE - 1); /* Get list pointers and loop the list */ ListHead = &KiTimerTableListHead[Index].Entry; while (ListHead != ListHead->Flink) { /* Lock the timer and go to the next entry */ LockQueue = KiAcquireTimerLock(Index); NextEntry = ListHead->Flink; /* Get the current timer and check its due time */ Timers--; Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); if ((NextEntry != ListHead) && (Timer->DueTime.QuadPart <= InterruptTime.QuadPart)) { /* It's expired, remove it */ ActiveTimers--; KiRemoveEntryTimer(Timer); /* Make it non-inserted, unlock it, and signal it */ Timer->Header.Inserted = FALSE; KiReleaseTimerLock(LockQueue); Timer->Header.SignalState = 1; /* Get the DPC and period */ TimerDpc = Timer->Dpc; Period = Timer->Period; /* Check if there's any waiters */ if (!IsListEmpty(&Timer->Header.WaitListHead)) { /* Check the type of event */ if (Timer->Header.Type == TimerNotificationObject) { /* Unwait the thread */ KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); } else { /* Otherwise unwait the thread and signal the timer */ KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); } } /* Check if we have a period */ if (Period) { /* Calculate the interval and insert the timer */ Interval.QuadPart = Int32x32To64(Period, -10000); while (!KiInsertTreeTimer(Timer, Interval)); } /* Check if we have a DPC */ if (TimerDpc) { #ifdef CONFIG_SMP /* * If the DPC is targeted to another processor, * then insert it into that processor's DPC queue * instead of delivering it now. * If the DPC is a threaded DPC, and the current CPU * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs), * then also insert it into the DPC queue for threaded delivery, * instead of doing it here. */ if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) && ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) || ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))) { /* Queue it */ KeInsertQueueDpc(TimerDpc, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } else #endif { /* Setup the DPC Entry */ DpcEntry[DpcCalls].Dpc = TimerDpc; DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; DpcCalls++; ASSERT(DpcCalls < MAX_TIMER_DPCS); } } /* Check if we're done processing */ if (!(ActiveTimers) || !(Timers)) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Reset accounting */ Timers = 24; ActiveTimers = 4; /* Lock the dispatcher database */ KiAcquireDispatcherLock(); } } else { /* Check if the timer list is empty */ if (NextEntry != ListHead) { /* Sanity check */ ASSERT(KiTimerTableListHead[Index].Time.QuadPart <= Timer->DueTime.QuadPart); /* Update the time */ _disable(); KiTimerTableListHead[Index].Time.QuadPart = Timer->DueTime.QuadPart; _enable(); } /* Release the lock */ KiReleaseTimerLock(LockQueue); /* Check if we've scanned all the timers we could */ if (!Timers) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Reset accounting */ Timers = 24; ActiveTimers = 4; /* Lock the dispatcher database */ KiAcquireDispatcherLock(); } /* Done looping */ break; } } } while (Index != Limit); /* Verify the timer table, on debug builds */ if (KeNumberProcessors == 1) KiCheckTimerTable(InterruptTime); /* Check if we still have DPC entries */ if (DpcCalls) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Lower IRQL if we need to */ if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql); } else { /* Unlock the dispatcher */ KiReleaseDispatcherLock(OldIrql); } }
VOID FASTCALL KiTimerListExpire(IN PLIST_ENTRY ExpiredListHead, IN KIRQL OldIrql) { ULARGE_INTEGER SystemTime; LARGE_INTEGER Interval; LONG i; ULONG DpcCalls = 0; PKTIMER Timer; PKDPC TimerDpc; ULONG Period; DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; #ifdef CONFIG_SMP PKPRCB Prcb = KeGetCurrentPrcb(); #endif /* Query system */ KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); /* Loop expired list */ while (ExpiredListHead->Flink != ExpiredListHead) { /* Get the current timer */ Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry); /* Remove it */ RemoveEntryList(&Timer->TimerListEntry); /* Not inserted */ Timer->Header.Inserted = FALSE; /* Signal it */ Timer->Header.SignalState = 1; /* Get the DPC and period */ TimerDpc = Timer->Dpc; Period = Timer->Period; /* Check if there's any waiters */ if (!IsListEmpty(&Timer->Header.WaitListHead)) { /* Check the type of event */ if (Timer->Header.Type == TimerNotificationObject) { /* Unwait the thread */ KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); } else { /* Otherwise unwait the thread and signal the timer */ KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); } } /* Check if we have a period */ if (Period) { /* Calculate the interval and insert the timer */ Interval.QuadPart = Int32x32To64(Period, -10000); while (!KiInsertTreeTimer(Timer, Interval)); } /* Check if we have a DPC */ if (TimerDpc) { #ifdef CONFIG_SMP /* * If the DPC is targeted to another processor, * then insert it into that processor's DPC queue * instead of delivering it now. * If the DPC is a threaded DPC, and the current CPU * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs), * then also insert it into the DPC queue for threaded delivery, * instead of doing it here. */ if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) && ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) || ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))) { /* Queue it */ KeInsertQueueDpc(TimerDpc, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } else #endif { /* Setup the DPC Entry */ DpcEntry[DpcCalls].Dpc = TimerDpc; DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; DpcCalls++; ASSERT(DpcCalls < MAX_TIMER_DPCS); } } } /* Check if we still have DPC entries */ if (DpcCalls) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Lower IRQL */ KeLowerIrql(OldIrql); } else { /* Unlock the dispatcher */ KiReleaseDispatcherLock(OldIrql); } }
/* * @implemented */ LONG NTAPI KeReleaseMutant(IN PKMUTANT Mutant, IN KPRIORITY Increment, IN BOOLEAN Abandon, IN BOOLEAN Wait) { KIRQL OldIrql; LONG PreviousState; PKTHREAD CurrentThread = KeGetCurrentThread(); BOOLEAN EnableApc = FALSE; ASSERT_MUTANT(Mutant); ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); /* Lock the Dispatcher Database */ OldIrql = KiAcquireDispatcherLock(); /* Save the Previous State */ PreviousState = Mutant->Header.SignalState; /* Check if it is to be abandonned */ if (Abandon == FALSE) { /* Make sure that the Owner Thread is the current Thread */ if (Mutant->OwnerThread != CurrentThread) { /* Release the lock */ KiReleaseDispatcherLock(OldIrql); /* Raise an exception */ ExRaiseStatus(Mutant->Abandoned ? STATUS_ABANDONED : STATUS_MUTANT_NOT_OWNED); } /* If the thread owns it, then increase the signal state */ Mutant->Header.SignalState++; } else { /* It's going to be abandonned */ Mutant->Header.SignalState = 1; Mutant->Abandoned = TRUE; } /* Check if the signal state is only single */ if (Mutant->Header.SignalState == 1) { /* Check if it's below 0 now */ if (PreviousState <= 0) { /* Remove the mutant from the list */ RemoveEntryList(&Mutant->MutantListEntry); /* Save if we need to re-enable APCs */ EnableApc = Mutant->ApcDisable; } /* Remove the Owning Thread and wake it */ Mutant->OwnerThread = NULL; /* Check if the Wait List isn't empty */ if (!IsListEmpty(&Mutant->Header.WaitListHead)) { /* Wake the Mutant */ KiWaitTest(&Mutant->Header, Increment); } } /* Check if the caller wants to wait after this release */ if (Wait == FALSE) { /* Release the Lock */ KiReleaseDispatcherLock(OldIrql); } else { /* Set a wait */ CurrentThread->WaitNext = TRUE; CurrentThread->WaitIrql = OldIrql; } /* Check if we need to re-enable APCs */ if (EnableApc) KeLeaveCriticalRegion(); /* Return the previous state */ return PreviousState; }