//----------------------------------------------------------------------------- // // // File: asyncq.cpp // // Description: Non-template asyncq implementations // // Author: Mike Swafford (MikeSwa) // // History: // 2/23/99 - MikeSwa Created // // Copyright (C) 1999 Microsoft Corporation // //----------------------------------------------------------------------------- #include "aqprecmp.h" #include "asyncq.h" #include "asyncq.inl" DWORD CAsyncQueueBase::s_cAsyncQueueStaticInitRefCount = 0; DWORD CAsyncQueueBase::s_cMaxPerProcATQThreadAdjustment = 0; DWORD CAsyncQueueBase::s_cDefaultMaxAsyncThreads = 0; //---[ CAsyncQueueBase::ThreadPoolInitialize ]--------------------------------- // // // Description: // Performs static ATQ initialization. This call is ref-counted. If // it succeeds, the caller should call HrThreadPoolDeinitialze(); // Parameters: // - // Returns: // - // History: // 3/30/2000 - MikeSwa Created // //----------------------------------------------------------------------------- void CAsyncQueueBase::ThreadPoolInitialize() { TraceFunctEnterEx((LPARAM) this, "CAsyncQueueBase::HrThreadPoolInitialize"); DWORD cATQMaxAsyncThreads = 0; DWORD cATQMaxTotalAsyncThreads = 0; DWORD cOurMaxAsyncThreads = 0; SYSTEM_INFO sinf; // // On 0 -> 1 transition, adjust ATQ according to our config // if (!s_cAsyncQueueStaticInitRefCount) { // // Get max threads per proc // cATQMaxAsyncThreads = (DWORD)AtqGetInfo(AtqMaxPoolThreads); _ASSERT(cATQMaxAsyncThreads && "AtqGetInfo says there are no threads!"); if (!cATQMaxAsyncThreads) cATQMaxAsyncThreads = 1; cOurMaxAsyncThreads = cATQMaxAsyncThreads; // // Adjust value by our config value // cOurMaxAsyncThreads += g_cPerProcMaxThreadPoolModifier; // // Get # of procs (using GetSystemInfo) // GetSystemInfo(&sinf); cOurMaxAsyncThreads *= sinf.dwNumberOfProcessors; // // We will throttle our requests at g_cMaxATQPercent // the max number of ATQ threads // cOurMaxAsyncThreads = (g_cMaxATQPercent*cOurMaxAsyncThreads)/100; if (!cOurMaxAsyncThreads) cOurMaxAsyncThreads = 1; // // Set static so people later on can use this calculation // s_cDefaultMaxAsyncThreads = cOurMaxAsyncThreads; // // Now we need to adjust our threads // s_cMaxPerProcATQThreadAdjustment = g_cPerProcMaxThreadPoolModifier; // // Per proc thread limit // if (s_cMaxPerProcATQThreadAdjustment) { AtqSetInfo(AtqMaxPoolThreads, cATQMaxAsyncThreads + s_cMaxPerProcATQThreadAdjustment); DebugTrace((LPARAM) this, "Adjusting per proc ATQ thread limit by %d (orig %d)", s_cMaxPerProcATQThreadAdjustment, cATQMaxAsyncThreads); } _ASSERT(!(0xFF000000 & cOurMaxAsyncThreads)); //sanity check number } s_cAsyncQueueStaticInitRefCount++; m_cMaxAsyncThreads = s_cDefaultMaxAsyncThreads; DebugTrace((LPARAM) this, "Setting m_cMaxAsyncThreads to %d"); TraceFunctLeave(); } //---[ CAsyncQueueBase::ThreadPoolDeinitialize ]------------------------------- // // // Description: // Will re-adjust ATQ data if we changed them during initialization // Parameters: // - // Returns: // - // History: // 3/30/2000 - MikeSwa Created // //----------------------------------------------------------------------------- void CAsyncQueueBase::ThreadPoolDeinitialize() { TraceFunctEnterEx((LPARAM) this, "CAsyncQueueBase::HrThreadPoolDeinitialize"); DWORD cATQMaxAsyncThreads = 0; DWORD cATQMaxTotalAsyncThreads = 0; _ASSERT(s_cAsyncQueueStaticInitRefCount != 0); s_cAsyncQueueStaticInitRefCount--; // // If this is the last queue, adjust our configuration so back to // the way we found it. // if (!s_cAsyncQueueStaticInitRefCount) { cATQMaxAsyncThreads = (DWORD)AtqGetInfo(AtqMaxPoolThreads); cATQMaxTotalAsyncThreads = (DWORD) AtqGetInfo(AtqMaxThreadLimit); // // Reset per-proc threads if it makes sense // if (s_cMaxPerProcATQThreadAdjustment && (cATQMaxAsyncThreads > s_cMaxPerProcATQThreadAdjustment)) { AtqSetInfo(AtqMaxPoolThreads, cATQMaxAsyncThreads - s_cMaxPerProcATQThreadAdjustment); DebugTrace((LPARAM) this, "Resetting ATQ Max per proc threads to %d", cATQMaxAsyncThreads - s_cMaxPerProcATQThreadAdjustment); s_cMaxPerProcATQThreadAdjustment = 0; } } TraceFunctLeave(); } //---[ CAsyncRetryAdminMsgRefQueue::CAsyncRetryAdminMsgRefQueue ]-------------- // // // Description: // Constructor for CAsyncRetryAdminMsgRefQueue // Parameters: // szDomain Domain name for this queue // pguid GUID for this queue // dwID Shedule ID for this queue // Returns: // - // History: // 2/23/99 - MikeSwa Created // //----------------------------------------------------------------------------- CAsyncRetryAdminMsgRefQueue::CAsyncRetryAdminMsgRefQueue( LPCSTR szDomain, LPCSTR szLinkName, const GUID *pguid, DWORD dwID, CAQSvrInst *paqinst) { _ASSERT(szDomain); _ASSERT(pguid); m_cbDomain = 0; m_szDomain = NULL; m_cbLinkName = 0; m_szLinkName = NULL; m_paqinst = paqinst; if (szDomain) { m_cbDomain = lstrlen(szDomain); m_szDomain = (LPSTR) pvMalloc(m_cbDomain+1); if (m_szDomain) lstrcpy(m_szDomain, szDomain); } if (szLinkName) { m_cbLinkName = lstrlen(szLinkName); m_szLinkName = (LPSTR) pvMalloc(m_cbLinkName+1); } if (m_szLinkName) lstrcpy(m_szLinkName, szLinkName); if (pguid) memcpy(&m_guid, pguid, sizeof(GUID)); else ZeroMemory(&m_guid, sizeof(GUID)); m_dwID = dwID; } //---[ CAsyncRetryAdminMsgRefQueue::~CAsyncRetryAdminMsgRefQueue ]------------- // // // Description: // Destructor for CAsyncRetryAdminMsgRefQueue // Parameters: // - // Returns: // - // History: // 2/23/99 - MikeSwa Created // //----------------------------------------------------------------------------- CAsyncRetryAdminMsgRefQueue::~CAsyncRetryAdminMsgRefQueue() { if (m_szDomain) FreePv(m_szDomain); if (m_szLinkName) FreePv(m_szLinkName); } //---[ CAsyncRetryAdminMsgRefQueue::QueryInterface ]--------------------------- // // // Description: // QueryInterface for CAsyncRetryAdminMsgRefQueue that supports: // - IQueueAdminAction // - IUnknown // - IQueueAdminQueue // Parameters: // // Returns: // // History: // 2/23/99 - MikeSwa Created // //----------------------------------------------------------------------------- STDMETHODIMP CAsyncRetryAdminMsgRefQueue::QueryInterface( REFIID riid, LPVOID *ppvObj) { HRESULT hr = S_OK; if (!ppvObj) { hr = E_POINTER; goto Exit; } if (IID_IUnknown == riid) { *ppvObj = static_cast(this); } else if (IID_IQueueAdminAction == riid) { *ppvObj = static_cast(this); } else if (IID_IQueueAdminQueue == riid) { *ppvObj = static_cast(this); } else { *ppvObj = NULL; hr = E_NOINTERFACE; goto Exit; } static_cast(*ppvObj)->AddRef(); Exit: return hr; } //---[ CAsyncRetryAdminMsgRefQueue::HrApplyQueueAdminFunction ]---------------- // // // Description: // Will call the IQueueAdminMessageFilter::Process message for every // message in this queue. If the message passes the filter, then // HrApplyActionToMessage on this object will be called. // Parameters: // IN pIQueueAdminMessageFilter // Returns: // S_OK on success // History: // 2/23/99 - MikeSwa Created // //----------------------------------------------------------------------------- STDMETHODIMP CAsyncRetryAdminMsgRefQueue::HrApplyQueueAdminFunction( IQueueAdminMessageFilter *pIQueueAdminMessageFilter) { HRESULT hr = S_OK; _ASSERT(pIQueueAdminMessageFilter); hr = pIQueueAdminMessageFilter->HrSetQueueAdminAction( (IQueueAdminAction *) this); //This is an internal interface that should not fail _ASSERT(SUCCEEDED(hr) && "HrSetQueueAdminAction"); if (FAILED(hr)) goto Exit; hr = HrMapFn(QueueAdminApplyActionToMessages, pIQueueAdminMessageFilter); Exit: return hr; } //---[ CAsyncRetryAdminMsgRefQueue::HrApplyActionToMessage ]------------------- // // // Description: // Applies an action to this message for this queue. This will be called // by the IQueueAdminMessageFilter during a queue enumeration function. // Parameters: // IN *pIUnknownMsg ptr to message abstraction // IN ma Message action to perform // IN pvContext Context set on IQueueAdminFilter // OUT pfShouldDelete TRUE if the message should be deleted // Returns: // S_OK on success // History: // 2/23/99 - MikeSwa Created // 4/2/99 - MikeSwa Added context // //----------------------------------------------------------------------------- STDMETHODIMP CAsyncRetryAdminMsgRefQueue::HrApplyActionToMessage( IUnknown *pIUnknownMsg, MESSAGE_ACTION ma, PVOID pvContext, BOOL *pfShouldDelete) { TraceFunctEnterEx((LPARAM) this, "CAsyncRetryAdminMsgRefQueue::HrApplyActionToMessage"); HRESULT hr = S_OK; CMsgRef *pmsgref = NULL; CLinkMsgQueue *plmq = (CLinkMsgQueue *)pvContext; CAQStats aqstats; _ASSERT(pIUnknownMsg); _ASSERT(pfShouldDelete); *pfShouldDelete = FALSE; hr = pIUnknownMsg->QueryInterface(IID_CMsgRef, (void **) &pmsgref); _ASSERT(SUCCEEDED(hr) && "IUnknownMsg Must be a CMsgRef!!"); if (FAILED(hr)) goto Exit; switch (ma) { case MA_DELETE: hr = pmsgref->HrQueueAdminNDRMessage(NULL); *pfShouldDelete = TRUE; break; case MA_DELETE_SILENT: hr = pmsgref->HrRemoveMessageFromQueue(NULL); *pfShouldDelete = TRUE; break; case MA_FREEZE_GLOBAL: pmsgref->GlobalFreezeMessage(); break; case MA_THAW_GLOBAL: pmsgref->GlobalThawMessage(); break; case MA_COUNT: default: //do nothing for counting and default break; } // // If we are deleting the message, we need to tell the // link so we can have accurate stats for the link. // if (*pfShouldDelete && SUCCEEDED(hr)) { // // NTRAID#X5-138120-2000/10/04-mikeswa // We will not have a plmq, if this delete is coming // from the queue-level. This can cause stats to be // incorrect, but this action is not exposed via the UI. // if (plmq) { pmsgref->GetStatsForMsg(&aqstats); plmq->HrNotify(&aqstats, FALSE); } else { ErrorTrace((LPARAM) this, "Unable to update stats for queue-level ops"); } if (m_paqinst) m_paqinst->DecPendingLocal(); } Exit: if (pmsgref) pmsgref->Release(); TraceFunctLeave(); return hr; } //---[ CAsyncRetryAdminMsgRefQueue::HrGetQueueInfo ]--------------------------- // // // Description: // Gets the Queue Admin infor for this Queue // Parameters: // IN OUT pqiQueueInfo Ptr to Queue Info Stucture to fill // Returns: // S_OK on success // E_OUTOFMEMORY if unable to allocate memory for queue name. // History: // 2/23/99 - MikeSwa Created // //----------------------------------------------------------------------------- STDMETHODIMP CAsyncRetryAdminMsgRefQueue::HrGetQueueInfo( QUEUE_INFO *pqiQueueInfo) { HRESULT hr = S_OK; //Get # of messages pqiQueueInfo->cMessages = m_cItemsPending+m_cRetryItems; //Get Link name: Note that this class is used for special links like //local delivery queue... so there is no destination SMTP domain to //route to... therefore we need to return a special link name to admin. pqiQueueInfo->szLinkName = wszQueueAdminConvertToUnicode(m_szLinkName, m_cbLinkName); if (m_szDomain) { //Get Queue name pqiQueueInfo->szQueueName = wszQueueAdminConvertToUnicode(m_szDomain, m_cbDomain); if (!pqiQueueInfo->szQueueName) { hr = E_OUTOFMEMORY; goto Exit; } } //Currently setting this to zero since we do not calculate it pqiQueueInfo->cbQueueVolume.QuadPart = 0; pqiQueueInfo->dwMsgEnumFlagsSupported = AQUEUE_DEFAULT_SUPPORTED_ENUM_FILTERS; Exit: return hr; } //---[ CAsyncRetryAdminMsgRefQueue::HrGetQueueID ]----------------------------- // // // Description: // Gets the QueueID for this Queue. // Parameters: // IN OUT pQueueID QUEUELINK_ID struct to fill in // Returns: // S_OK on success // E_OUTOFMEMORY if unable to allocate memory for queue name. // History: // 2/23/99 - MikeSwa Created // //----------------------------------------------------------------------------- STDMETHODIMP CAsyncRetryAdminMsgRefQueue::HrGetQueueID( QUEUELINK_ID *pQueueID) { pQueueID->qltType = QLT_QUEUE; pQueueID->dwId = m_dwID; memcpy(&pQueueID->uuid, &m_guid, sizeof(GUID)); if (m_szDomain) { pQueueID->szName = wszQueueAdminConvertToUnicode(m_szDomain, m_cbDomain); if (!pQueueID->szName) return E_OUTOFMEMORY; } return S_OK; } //---[ CAsyncRetryAdminMsgRefQueue::fMatchesID ]------------------------------- // // // Description: // Used to determine if this link matches a given scheduleID/guid pair // Parameters: // IN QueueLinkID ID to match against // Returns: // TRUE if it matches // FALSE if it does not // History: // 2/23/99 - MikeSwa Created // //----------------------------------------------------------------------------- BOOL STDMETHODCALLTYPE CAsyncRetryAdminMsgRefQueue::fMatchesID( QUEUELINK_ID *pQueueLinkID) { _ASSERT(pQueueLinkID); if (!pQueueLinkID) return FALSE; if (0 != memcmp(&m_guid, &(pQueueLinkID->uuid), sizeof(GUID))) return FALSE; if (m_dwID != pQueueLinkID->dwId) return FALSE; //Don't need to check domain name since there is a special GUID to //identify the local async queue. return TRUE; } //---[ CAsyncMailMsgQueue::HrQueueRequest ]------------------------------------ // // // Description: // Function that will queue a request to the async queue and close // the handles associated with a message if we are above our simple // "throttle" limit. // Parameters: // pIMailMsgProperties The IMailMsgProperties interface to queue // fRetry TRUE - if this message is being retried // FALSE - otherwise // cMsgsInSystem The total number of messages in the system // Returns: // S_OK on success // Error code from async queue on failure. // History: // 10/7/1999 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncMailMsgQueue::HrQueueRequest(IMailMsgProperties *pIMailMsgProperties, BOOL fRetry, DWORD cMsgsInSystem) { TraceFunctEnterEx((LPARAM) this, "CAsyncMailMsgQueue::HrQueueRequest"); IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL; HRESULT hr = S_OK; if (g_cMaxIMsgHandlesThreshold < cMsgsInSystem) { DebugTrace((LPARAM) this, "INFO: Closing IMsg Content - %d messsages in queue", cMsgsInSystem); hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt, (void **) &pIMailMsgQueueMgmt); if (SUCCEEDED(hr)) { //bounce usage count off of zero pIMailMsgQueueMgmt->ReleaseUsage(); pIMailMsgQueueMgmt->AddUsage(); pIMailMsgQueueMgmt->Release(); } else { ErrorTrace((LPARAM) this, "Unable to QI for IMailMsgQueueMgmt - hr 0x%08X", hr); } } TraceFunctLeave(); return CAsyncMailMsgQueueBase::HrQueueRequest(pIMailMsgProperties, fRetry); } //---[ CAsyncMailMsgQueue::HrQueueRequest ]------------------------------------ // // // Description: // Since we inherit from AsyncQueue who implmenents this, we should assert // so that a dev adding a new call to this class later on, will use the // version that closes handles. // // In RTL this will force the handles closed and queue the request // Parameters: // pIMailMsgProperties The IMailMsgProperties interface to queue // fRetry TRUE - if this message is being retried // FALSE - otherwise // Returns: // returns return value from proper version of HrQueueRequest // History: // 10/7/1999 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncMailMsgQueue::HrQueueRequest(IMailMsgProperties *pIMailMsgProperties, BOOL fRetry) { _ASSERT(0 && "Should use HrQueueRequest with 3 parameters"); return HrQueueRequest(pIMailMsgProperties, fRetry, g_cMaxIMsgHandlesThreshold+1); }