/*++ Copyright (c) 1997 Microsoft Corporation Module Name: frsdelcs.c Abstract: This command server delays a command packet Author: Billy J. Fuller 01-Jun-1997 Environment User mode winnt --*/ #include #pragma hdrstop #define DEBSUB "FRSDELCS:" #include // // Struct for the Delayed Command Server // Contains info about the queues and the threads // #define DELCS_MAXTHREADS (1) // MUST BE 1; there are no locks on globals. COMMAND_SERVER DelCs; HANDLE DelCsEvent; // // List of delayed commands // LIST_ENTRY TimeoutList; VOID FrsDelCsInsertCmd( IN PCOMMAND_PACKET Cmd ) /*++ Routine Description: Insert the new command packet into the sorted, timeout list Arguments: Cmd Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "FrsDelCsInsertCmd:" PLIST_ENTRY Entry; PCOMMAND_PACKET OldCmd; if (Cmd == NULL) { return; } // // Insert into empty list // if (IsListEmpty(&TimeoutList)) { InsertHeadList(&TimeoutList, &Cmd->ListEntry); return; } // // Insert at tail // Entry = GetListTail(&TimeoutList); OldCmd = CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry); if (DsTimeout(OldCmd) <= DsTimeout(Cmd)) { InsertTailList(&TimeoutList, &Cmd->ListEntry); return; } // // Insert into list // for (Entry = GetListHead(&TimeoutList); Entry != &TimeoutList; Entry = GetListNext(Entry)) { OldCmd = CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry); if (DsTimeout(Cmd) <= DsTimeout(OldCmd)) { InsertTailList(Entry, &Cmd->ListEntry); return; } } FRS_ASSERT(!"FrsDelCsInsertCmd failed."); } VOID ProcessCmd( IN PCOMMAND_PACKET Cmd ) /*++ Routine Description: Process the expired command packet Arguments: Cmd Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "ProcessCmd:" ULONG WStatus = ERROR_SUCCESS; switch (Cmd->Command) { // // Submit a command to a command server // case CMD_DELAYED_SUBMIT: DPRINT3(5, "Del: submit Cmd %08x DsCmd %08x DsCs %08x\n", Cmd, DsCmd(Cmd), DsCs(Cmd)); FrsSubmitCommandServer(DsCs(Cmd), DsCmd(Cmd)); DsCmd(Cmd) = NULL; break; // // Submit a command to an FRS queue. // case CMD_DELAYED_QUEUE_SUBMIT: DPRINT2(5, "DelQueue: submit Cmd %08x DsCmd %08x\n", Cmd, DsCmd(Cmd)); FrsSubmitCommand(DsCmd(Cmd), FALSE); DsCmd(Cmd) = NULL; break; // // UnIdle a queue and kick its command server // case CMD_DELAYED_UNIDLED: DPRINT2(5, "Del: unidle Cmd %08x DsQueue %08x\n", Cmd, DsQueue(Cmd)); FrsRtlUnIdledQueue(DsQueue(Cmd)); FrsKickCommandServer(DsCs(Cmd)); DsQueue(Cmd) = NULL; break; // // Kick a command server // case CMD_DELAYED_KICK: DPRINT2(5, "Del: kick Cmd %08x DsCs %08x\n", Cmd, DsCs(Cmd)); FrsKickCommandServer(DsCs(Cmd)); break; // // Complete the command (work is done in the completion routine) // Command may be resubmitted to this delayed command server. // case CMD_DELAYED_COMPLETE: DPRINT2(5, "Del: Complete Cmd %08x DsCmd %08x\n", Cmd, DsCmd(Cmd)); FrsCompleteCommand(DsCmd(Cmd), ERROR_SUCCESS); DsCmd(Cmd) = NULL; break; // // Unknown command // default: DPRINT1(0, "Delayed: unknown command 0x%x\n", Cmd->Command); WStatus = ERROR_INVALID_FUNCTION; break; } // // All done // FrsCompleteCommand(Cmd, WStatus); } VOID ExpelCmds( IN ULONGLONG CurrentTime ) /*++ Routine Description: Expel the commands whose timeouts have passed. Arguments: CurrentTime Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "ExpelCmds:" PLIST_ENTRY Entry; PCOMMAND_PACKET Cmd; // // Expel expired commands // while (!IsListEmpty(&TimeoutList)) { Entry = GetListHead(&TimeoutList); Cmd = CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry); // // Hasn't expired; stop // if (DsTimeout(Cmd) > CurrentTime) { break; } FrsRemoveEntryList(Entry); ProcessCmd(Cmd); } } VOID RunDownCmds( VOID ) /*++ Routine Description: Error off the commands in the timeout list Arguments: None. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "RunDownCmds:" PLIST_ENTRY Entry; PCOMMAND_PACKET Cmd; // // Expel expired commands // while (!IsListEmpty(&TimeoutList)) { Entry = RemoveHeadList(&TimeoutList); Cmd = CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry); FrsCompleteCommand(Cmd, ERROR_ACCESS_DENIED); } } DWORD MainDelCs( PVOID Arg ) /*++ Routine Description: Entry point for a thread serving the Delayed command Command Server. Arguments: Arg - thread Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "MainDelCs:" ULONGLONG CurrentTime; ULONG WStatus = ERROR_SUCCESS; BOOL IsRunDown; PCOMMAND_PACKET Cmd; PLIST_ENTRY Entry; ULONG Timeout = INFINITE; PFRS_THREAD FrsThread = (PFRS_THREAD)Arg; // // Thread is pointing at the correct command server // FRS_ASSERT(FrsThread->Data == &DelCs); DPRINT(0, "Delayed command server has started.\n"); // // Try-Finally // try { // // Capture exception. // try { while(TRUE) { // // Pull entries off the "delayed" queue and put them on the timeout list // Cmd = FrsGetCommandServerTimeout(&DelCs, Timeout, &IsRunDown); // // Nothing to do; exit // if (Cmd == NULL && !IsRunDown && IsListEmpty(&TimeoutList)) { DPRINT(0, "Delayed command server is exiting.\n"); FrsExitCommandServer(&DelCs, FrsThread); } // // Rundown the timeout list and exit the thread // if (IsRunDown) { RunDownCmds(); DPRINT(0, "Delayed command server is exiting.\n"); FrsExitCommandServer(&DelCs, FrsThread); } // // Insert the new command, if any // FrsDelCsInsertCmd(Cmd); // // Expel expired commands // GetSystemTimeAsFileTime((PFILETIME)&CurrentTime); CurrentTime /= (ULONGLONG)(10 * 1000); ExpelCmds(CurrentTime); // // Reset our timeout // if (IsListEmpty(&TimeoutList)) { Timeout = INFINITE; } else { Entry = GetListHead(&TimeoutList); Cmd = CONTAINING_RECORD(Entry, COMMAND_PACKET, ListEntry); Timeout = (ULONG)(DsTimeout(Cmd) - CurrentTime); } } // // Get exception status. // } except (EXCEPTION_EXECUTE_HANDLER) { GET_EXCEPTION_CODE(WStatus); } } finally { if (WIN_SUCCESS(WStatus)) { if (AbnormalTermination()) { WStatus = ERROR_OPERATION_ABORTED; } } DPRINT_WS(0, "DelCs finally.", WStatus); // // Trigger FRS shutdown if we terminated abnormally. // if (!WIN_SUCCESS(WStatus) && (WStatus != ERROR_PROCESS_ABORTED)) { DPRINT(0, "DelCs terminated abnormally, forcing service shutdown.\n"); FrsIsShuttingDown = TRUE; SetEvent(ShutDownEvent); } else { WStatus = ERROR_SUCCESS; } } return WStatus; } VOID FrsDelCsInitialize( VOID ) /*++ Routine Description: Initialize the delayed command server subsystem. Arguments: None. Return Value: ERROR_SUCCESS --*/ { #undef DEBSUB #define DEBSUB "FrsDelCsInitialize:" // // Must be 1 because there are no locks on delayed-cmd lists // Also, there is no benefit to having more than 1. // FRS_ASSERT(DELCS_MAXTHREADS == 1); InitializeListHead(&TimeoutList); FrsInitializeCommandServer(&DelCs, DELCS_MAXTHREADS, L"DelCs", MainDelCs); DelCsEvent = FrsCreateEvent(FALSE, FALSE); //DelCsEvent = CreateEvent(NULL, FALSE, FALSE, NULL); } VOID DelCsCompletionRoutine( IN PCOMMAND_PACKET Cmd, IN PVOID Arg ) /*++ Routine Description: Completion routine for delayed command server Arguments: Cmd Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "DelCsCompletionRoutine:" DPRINT1(5, "DelRs: completion 0x%x\n", Cmd); if (DsCmd(Cmd)) { FrsCompleteCommand(DsCmd(Cmd), DsCmd(Cmd)->ErrorStatus); DsCmd(Cmd) = NULL; } FrsSetCompletionRoutine(Cmd, FrsFreeCommand, NULL); FrsCompleteCommand(Cmd, Cmd->ErrorStatus); } ULONGLONG ComputeTimeout( IN ULONG TimeoutInMilliSeconds ) /*++ Routine Description: Compute the absolute timeout in milliseconds. Arguments: TimeoutInMilliSeconds Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "ComputeTimeout:" ULONGLONG CurrentTime; // // 100-nanoseconds to milliseconds // GetSystemTimeAsFileTime((PFILETIME)&CurrentTime); CurrentTime /= (ULONGLONG)(10 * 1000); CurrentTime += (ULONGLONG)(TimeoutInMilliSeconds); return CurrentTime; } VOID FrsDelCsSubmitSubmit( IN PCOMMAND_SERVER Cs, IN PCOMMAND_PACKET DelCmd, IN ULONG Timeout ) /*++ Routine Description: Submit a delayed command to a command server Arguments: Cs DelCmd Timeout - in milliseconds Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "FrsDelCsSubmitSubmit:" PCOMMAND_PACKET Cmd; Cmd = FrsAllocCommand(&DelCs.Queue, CMD_DELAYED_SUBMIT); FrsSetCompletionRoutine(Cmd, DelCsCompletionRoutine, NULL); DsCs(Cmd) = Cs; DsCmd(Cmd) = DelCmd; DsTimeout(Cmd) = ComputeTimeout(Timeout); DPRINT3(5, "Del: submit Cmd %x DelCmd %x Cs %x\n", Cmd, DsCmd(Cmd), DsCs(Cmd)); FrsSubmitCommandServer(&DelCs, Cmd); } VOID FrsDelQueueSubmit( IN PCOMMAND_PACKET DelCmd, IN ULONG Timeout ) /*++ Routine Description: Submit a delayed command to an Frs Queue. Arguments: DelCmd Timeout - in milliseconds Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "FrsDelQueueSubmit:" PCOMMAND_PACKET Cmd; Cmd = FrsAllocCommand(&DelCs.Queue, CMD_DELAYED_QUEUE_SUBMIT); FrsSetCompletionRoutine(Cmd, DelCsCompletionRoutine, NULL); DsCs(Cmd) = NULL; DsCmd(Cmd) = DelCmd; DsTimeout(Cmd) = ComputeTimeout(Timeout); DPRINT2(5, "DelQueue: submit Cmd %x DelCmd %x\n", Cmd, DsCmd(Cmd)); FrsSubmitCommandServer(&DelCs, Cmd); } VOID FrsDelCsCompleteSubmit( IN PCOMMAND_PACKET DelCmd, IN ULONG Timeout ) /*++ Routine Description: Submit a delayed complete-command to the DelCs Arguments: DelCmd Timeout - in milliseconds Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "FrsDelCsCompleteSubmit:" PCOMMAND_PACKET Cmd; Cmd = FrsAllocCommand(&DelCs.Queue, CMD_DELAYED_COMPLETE); FrsSetCompletionRoutine(Cmd, DelCsCompletionRoutine, NULL); DsCs(Cmd) = NULL; DsCmd(Cmd) = DelCmd; DsTimeout(Cmd) = ComputeTimeout(Timeout); COMMAND_TRACE(4, Cmd, "Del Complete"); COMMAND_TRACE(4, DelCmd, "Del Complete Cmd"); FrsSubmitCommandServer(&DelCs, Cmd); } VOID FrsDelCsSubmitUnIdled( IN PCOMMAND_SERVER Cs, IN PFRS_QUEUE IdledQueue, IN ULONG Timeout ) /*++ Routine Description: Submit a delayed "FrsRtlUnIdledQueue" command. Arguments: Cs IdledQueue Timeout - In milliSeconds Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "FrsDelCsSubmitUnIdled:" PCOMMAND_PACKET Cmd; Cmd = FrsAllocCommand(&DelCs.Queue, CMD_DELAYED_UNIDLED); FrsSetCompletionRoutine(Cmd, DelCsCompletionRoutine, NULL); DsCs(Cmd) = Cs; DsQueue(Cmd) = IdledQueue; DsTimeout(Cmd) = ComputeTimeout(Timeout); DPRINT2(5, "Del: submit Cmd 0x%x IdledQueue 0x%x\n", Cmd, DsQueue(Cmd)); FrsSubmitCommandServer(&DelCs, Cmd); } VOID FrsDelCsSubmitKick( IN PCOMMAND_SERVER Cs, IN PFRS_QUEUE Queue, IN ULONG Timeout ) /*++ Routine Description: Submit a delayed "FrsKickCommandServer" command. Arguments: Cs Queue Timeout - In milliSeconds Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "FrsDelCsSubmitKick:" PCOMMAND_PACKET Cmd; Cmd = FrsAllocCommand(&DelCs.Queue, CMD_DELAYED_KICK); FrsSetCompletionRoutine(Cmd, DelCsCompletionRoutine, NULL); DsCs(Cmd) = Cs; DsQueue(Cmd) = Queue; DsTimeout(Cmd) = ComputeTimeout(Timeout); DPRINT2(5, "Del: submit Cmd 0x%x kick 0x%x\n", Cmd, DsCs(Cmd)); FrsSubmitCommandServer(&DelCs, Cmd); } VOID ShutDownDelCs( VOID ) /*++ Routine Description: Shutdown the send command server Arguments: None. Return Value: None. --*/ { #undef DEBSUB #define DEBSUB "ShutDownDelCs:" INT i; FrsRunDownCommandServer(&DelCs, &DelCs.Queue); for (i = 0; i < DELCS_MAXTHREADS; ++i) { SetEvent(DelCsEvent); } }