/***************************************************************************** * service.cpp - service group object implementation ***************************************************************************** * Copyright (c) 1997-2000 Microsoft Corporation. All Rights Reserved. */ #include "private.h" /***************************************************************************** * CServiceGroup ***************************************************************************** * Service group implementation. */ class CServiceGroup : public IServiceGroup , public CUnknown { private: KDPC m_kDpc; KSPIN_LOCK m_kSpinLock; LIST_ENTRY m_listEntry; KTIMER m_kTimer; BOOLEAN m_bDelayedService; static VOID NTAPI ServiceDpc ( IN PKDPC pKDpc, IN PVOID pvDeferredContext, IN PVOID pvSystemArgument1, IN PVOID pvSystemArgument2 ); public: DECLARE_STD_UNKNOWN(); CServiceGroup(PUNKNOWN pUnknownOuter); ~CServiceGroup(); IMP_IServiceGroup; friend PKSPIN_LOCK GetServiceGroupSpinLock ( PSERVICEGROUP pServiceGroup ); }; PKSPIN_LOCK GetServiceGroupSpinLock ( PSERVICEGROUP pServiceGroup ) { CServiceGroup *ServiceGroup = (CServiceGroup *) pServiceGroup; return &ServiceGroup->m_kSpinLock; } /***************************************************************************** * SERVICEGROUPMEMBER ***************************************************************************** * A structure representing a service group member. */ struct SERVICEGROUPMEMBER { LIST_ENTRY listEntry; PSERVICESINK pServiceSink; }; typedef SERVICEGROUPMEMBER *PSERVICEGROUPMEMBER; /***************************************************************************** * Factory. */ #pragma code_seg("PAGE") /***************************************************************************** * CreateServiceGroup() ***************************************************************************** * Creates a service group object. */ NTSTATUS CreateServiceGroup ( OUT PUNKNOWN * ppUnknown, IN REFCLSID, IN PUNKNOWN pUnknownOuter OPTIONAL, IN POOL_TYPE poolType ) { PAGED_CODE(); ASSERT(ppUnknown); _DbgPrintF(DEBUGLVL_LIFETIME,("Creating SERVICEGROUP")); STD_CREATE_BODY ( CServiceGroup, ppUnknown, pUnknownOuter, poolType ); } /***************************************************************************** * PcNewServiceGroup() ***************************************************************************** * Creates and initializes a service group. */ PORTCLASSAPI NTSTATUS NTAPI PcNewServiceGroup ( OUT PSERVICEGROUP * ppServiceGroup, IN PUNKNOWN pUnknownOuter OPTIONAL ) { PAGED_CODE(); ASSERT(ppServiceGroup); // // Validate Parameters. // if (NULL == ppServiceGroup) { _DbgPrintF(DEBUGLVL_TERSE, ("PcNewServiceGroup : Invalid Parameter")); return STATUS_INVALID_PARAMETER; } PUNKNOWN pUnknown; NTSTATUS ntStatus = CreateServiceGroup ( &pUnknown, GUID_NULL, pUnknownOuter, NonPagedPool ); if (NT_SUCCESS(ntStatus)) { PSERVICEGROUP pServiceGroup; ntStatus = pUnknown->QueryInterface ( IID_IServiceGroup, (PVOID *) &pServiceGroup ); if (NT_SUCCESS(ntStatus)) { *ppServiceGroup = pServiceGroup; } else { pServiceGroup->Release(); } pUnknown->Release(); } return ntStatus; } /***************************************************************************** * Member functions. */ /***************************************************************************** * CServiceGroup::CServiceGroup() ***************************************************************************** * Constructor. */ CServiceGroup:: CServiceGroup ( IN PUNKNOWN pUnknownOuter ) : CUnknown(pUnknownOuter) { PAGED_CODE(); _DbgPrintF(DEBUGLVL_LIFETIME,("Initializing SERVICEGROUP (0x%08x)",this)); KeInitializeDpc(&m_kDpc,ServiceDpc,PVOID(this)); KeInitializeSpinLock(&m_kSpinLock); InitializeListHead(&m_listEntry); } #pragma code_seg() /***************************************************************************** * CServiceGroup::~CServiceGroup() ***************************************************************************** * Destructor. */ CServiceGroup:: ~CServiceGroup ( void ) { _DbgPrintF(DEBUGLVL_LIFETIME,("Destroying SERVICEGROUP (0x%08x)",this)); // // Make sure that the timer is shut down if using deferred service // if( m_bDelayedService ) { KeCancelTimer( &m_kTimer ); } // // Make sure the DPC is not queued. // KeRemoveQueueDpc(&m_kDpc); // // Acquire the spin lock in order to wait for a running DPC to wind down. // TODO: Is there a window here where we can have a DPC running on // another processor about to take the spinlock, but we get it // first? That would mean it would wait for us to release the // spinlock and then run as we destruct the service group. // KIRQL kIrqlOld; KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld); // // Get rid of any remaining members. // while (! IsListEmpty(&m_listEntry)) { PLIST_ENTRY pListEntry = RemoveHeadList(&m_listEntry); PSERVICEGROUPMEMBER pServiceGroupMember = PSERVICEGROUPMEMBER(pListEntry); pServiceGroupMember->pServiceSink->Release(); delete pServiceGroupMember; } KeReleaseSpinLock(&m_kSpinLock,kIrqlOld); } #pragma code_seg("PAGE") /***************************************************************************** * CServiceGroup::NonDelegatingQueryInterface() ***************************************************************************** * Obtains an interface. */ STDMETHODIMP_(NTSTATUS) CServiceGroup:: NonDelegatingQueryInterface ( IN REFIID refIid, OUT PVOID * ppvObject ) { PAGED_CODE(); ASSERT(ppvObject); if ( (IsEqualGUIDAligned(refIid,IID_IUnknown)) || (IsEqualGUIDAligned(refIid,IID_IServiceSink)) || (IsEqualGUIDAligned(refIid,IID_IServiceGroup)) ) { *ppvObject = PVOID(PSERVICEGROUP(this)); } else { *ppvObject = NULL; } if (*ppvObject) { PUNKNOWN(*ppvObject)->AddRef(); return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } #pragma code_seg() /***************************************************************************** * ServiceDpc() ***************************************************************************** * Deferred procedure to be executed as a result of service request. */ VOID NTAPI CServiceGroup:: ServiceDpc ( IN PKDPC pKDpc, IN PVOID pvDeferredContext, IN PVOID pvSystemArgument1, IN PVOID pvSystemArgument2 ) { _DbgPrintF(DEBUGLVL_BLAB,("CServiceGroup::ServiceDpc start")); ASSERT(pvDeferredContext); if( pvDeferredContext ) { // // The deferred context is the service group object. // CServiceGroup *pServiceGroup = (CServiceGroup *) pvDeferredContext; KeAcquireSpinLockAtDpcLevel(&pServiceGroup->m_kSpinLock); // // Request service on all members. // for ( PLIST_ENTRY pListEntry = pServiceGroup->m_listEntry.Flink; pListEntry != &pServiceGroup->m_listEntry; pListEntry = pListEntry->Flink ) { PSERVICEGROUPMEMBER(pListEntry)->pServiceSink->RequestService(); } KeReleaseSpinLockFromDpcLevel(&pServiceGroup->m_kSpinLock); } _DbgPrintF(DEBUGLVL_BLAB,("CServiceGroup::ServiceDpc stop")); } /***************************************************************************** * CServiceGroup::RequestService() ***************************************************************************** * Service group function to indicate that service is requested for the group. */ STDMETHODIMP_(void) CServiceGroup:: RequestService ( void ) { _DbgPrintF(DEBUGLVL_BLAB,("CServiceGroup::RequestService start")); if (m_bDelayedService) { LARGE_INTEGER largeInteger; largeInteger.QuadPart = 0; KeSetTimer(&m_kTimer,largeInteger,&m_kDpc); } else if (KeGetCurrentIrql() < DISPATCH_LEVEL) { KIRQL kIrqlOld; KeRaiseIrql(DISPATCH_LEVEL,&kIrqlOld); KeInsertQueueDpc ( &m_kDpc, NULL, NULL ); KeLowerIrql(kIrqlOld); } else { KeInsertQueueDpc ( &m_kDpc, NULL, NULL ); } _DbgPrintF(DEBUGLVL_BLAB,("CServiceGroup::RequestService end")); } /***************************************************************************** * CServiceGroup::AddMember() ***************************************************************************** * Service group function to add a member. */ STDMETHODIMP_(NTSTATUS) CServiceGroup:: AddMember ( IN PSERVICESINK pServiceSink ) { // // Create a new member. // PSERVICEGROUPMEMBER pServiceGroupMember = new(NonPagedPool,'mScP') SERVICEGROUPMEMBER; NTSTATUS ntStatus = STATUS_SUCCESS; if (pServiceGroupMember) { // // Member structure holds a reference to the sink. // pServiceGroupMember->pServiceSink = pServiceSink; pServiceSink->AddRef(); // // Add the member to the list. // KIRQL kIrqlOld; KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld); InsertTailList ( &m_listEntry, &pServiceGroupMember->listEntry ); KeReleaseSpinLock(&m_kSpinLock,kIrqlOld); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } return ntStatus; } /***************************************************************************** * CServiceGroup::RemoveMember() ***************************************************************************** * Service group function to remove a member. */ STDMETHODIMP_(void) CServiceGroup:: RemoveMember ( IN PSERVICESINK pServiceSink ) { // // Remove the member structure from the list. // KIRQL kIrqlOld; KeAcquireSpinLock(&m_kSpinLock,&kIrqlOld); for( PLIST_ENTRY pListEntry = m_listEntry.Flink; pListEntry != &m_listEntry; pListEntry = pListEntry->Flink ) { PSERVICEGROUPMEMBER pServiceGroupMember = PSERVICEGROUPMEMBER(pListEntry); if (pServiceGroupMember->pServiceSink == pServiceSink) { RemoveEntryList(pListEntry); pServiceGroupMember->pServiceSink->Release(); delete pServiceGroupMember; break; } } KeReleaseSpinLock(&m_kSpinLock,kIrqlOld); } /***************************************************************************** * CServiceGroup::SupportDelayedService() ***************************************************************************** * Indicate service group should support delayed service. */ STDMETHODIMP_(void) CServiceGroup:: SupportDelayedService ( void ) { m_bDelayedService = TRUE; KeInitializeTimer(&m_kTimer); } /***************************************************************************** * CServiceGroup::RequestDelayedService() ***************************************************************************** * Request service after a delay. */ STDMETHODIMP_(void) CServiceGroup:: RequestDelayedService ( IN ULONGLONG ullDelay ) { LARGE_INTEGER largeInteger; largeInteger.QuadPart = ullDelay; KeSetTimer(&m_kTimer,largeInteger,&m_kDpc); } /***************************************************************************** * CServiceGroup::CancelDelayedService() ***************************************************************************** * Cancel delayed service. */ STDMETHODIMP_(void) CServiceGroup:: CancelDelayedService ( void ) { KeCancelTimer(&m_kTimer); }