// // asyncctx.h -- This file contains the class definitions for: // CSearchRequestBlock // CBatchLdapConnection // CBatchLdapConnectionCache // CStoreListResolveContext // CSingleSearchReinsertionRequest // // Created: // Feb 19, 1997 -- Milan Shah (milans) // // Changes: // jstamerj 1999/03/16 15:29:20: Heavily modified to batch requests // togethor across multiple message // categorizations // jstamerj 1999/03/22 12:44:01: Modified to support throttling via a // queue of CInsertionRequest objects // // dlongley 2001/10/22: Added CSingleSearchReinsertionRequest to force // abandoned and reissued searches to be throttled // via the CInsertionRequest object queue // #ifndef _ASYNCCTX_H_ #define _ASYNCCTX_H_ #include "ldapconn.h" #include "ccataddr.h" #include "simparray.h" #include "icatqueries.h" #include "icatasync.h" class CBatchLdapConnectionCache; class CCfgConnection; template class CEmailIDLdapStore; // // The MCIS3 LDAP server beta 1 does not correctly handle queries with more // than 4 legs in an OR clause. Because of this, we need to limit our search // query compression on a configurable basis. The global value, // nMaxSearchBlockSize constrains how many searches will be compressed into // a single search. The value is read from the registry key // szMaxSearchBlockSize. If it is not present, it defaults to // MAX_SEARCH_BLOCK_SIZE. // #define MAX_SEARCH_BLOCK_SIZE_KEY "System\\CurrentControlSet\\Services\\SMTPSVC\\Parameters" #define MAX_SEARCH_BLOCK_SIZE_VALUE "MaxSearchBlockSize" #define MAX_SEARCH_BLOCK_SIZE 20 #define MAX_PENDING_SEARCHES_VALUE "MaxPendingSearches" #define MAX_PENDING_SEARCHES 60 #define MAX_CONNECTION_RETRIES_VALUE "MaxConnectionRetries" #define MAX_CONNECTION_RETRIES 20 class CBatchLdapConnectionCache; class CBatchLdapConnection; class CStoreListResolveContext; typedef VOID (*LPFNLIST_COMPLETION)(VOID *lpContext); typedef VOID (*LPSEARCHCOMPLETION)( CCatAddr *pCCatAddr, CStoreListResolveContext *pslrc, CBatchLdapConnection *pConn); //------------------------------------------------------------ // // Class CSearchRequestBlock // //------------------------------------------------------------ CatDebugClass(CSearchRequestBlock) { private: typedef struct _SearchRequest { CCatAddr *pCCatAddr; LPSEARCHCOMPLETION fnSearchCompletion; CStoreListResolveContext *pslrc; LPSTR pszSearchFilter; LPSTR pszDistinguishingAttribute; LPSTR pszDistinguishingAttributeValue; } SEARCH_REQUEST, *PSEARCH_REQUEST; #define SIGNATURE_CSEARCHREQUESTBLOCK (DWORD)'lBRS' #define SIGNATURE_CSEARCHREQUESTBLOCK_INVALID (DWORD)'lBRX' public: void * operator new(size_t size, DWORD dwNumRequests); CSearchRequestBlock( CBatchLdapConnection *pConn); ~CSearchRequestBlock(); VOID InsertSearchRequest( ISMTPServer *pISMTPServer, ICategorizerParameters *pICatParams, CCatAddr *pCCatAddr, LPSEARCHCOMPLETION fnSearchCompletion, CStoreListResolveContext *pslrc, LPSTR pszSearchFilter, LPSTR pszDistinguishingAttribute, LPSTR pszDistinguishingAttributeValue); VOID DispatchBlock(); HRESULT ReserveSlot() { if( ((DWORD)InterlockedIncrement((PLONG)&m_cBlockRequestsReserved)) > m_cBlockSize) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); else return S_OK; } HRESULT AddResults( DWORD dwNumResults, ICategorizerItemAttributes **rgpItemAttributes) { HRESULT hr; hr = m_csaItemAttr.AddArray( dwNumResults, rgpItemAttributes); if(SUCCEEDED(hr)) { // // AddRef all the interfaces we hold // for(DWORD dwCount = 0; dwCount < dwNumResults; dwCount++) { rgpItemAttributes[dwCount]->AddRef(); } } return hr; } private: PSEARCH_REQUEST GetNextSearchRequest(DWORD *pdwIndex) { *pdwIndex = InterlockedIncrement((PLONG)&m_cBlockRequestsAllocated) - 1; _ASSERT(*pdwIndex < m_cBlockSize); return &(m_prgSearchRequests[*pdwIndex]); } VOID CompleteBlockWithError(HRESULT hr) { PutBlockHRESULT(hr); CallCompletions(); } static VOID LDAPCompletion( LPVOID ctx, DWORD dwNumResults, ICategorizerItemAttributes **rgpICatItemAttributes, HRESULT hrStatus, BOOL fFinalCompletion); HRESULT HrTriggerBuildQueries(); HRESULT HrTriggerSendQuery(); static HRESULT HrBuildQueriesDefault( HRESULT HrStatus, PVOID pContext); static HRESULT HrSendQueryDefault( HRESULT HrStatus, PVOID pContext); static HRESULT HrSendQueryCompletion( HRESULT HrStatus, PVOID pContext); VOID CompleteSearchBlock( HRESULT hrStatus); HRESULT HrTriggerSortQueryResult( HRESULT hrStatus); static HRESULT HrSortQueryResultDefault( HRESULT hrStatus, PVOID pContext); VOID PutBlockHRESULT( HRESULT hr); VOID CallCompletions(); VOID MatchItem( ICategorizerItem *pICatItem, ICategorizerItemAttributes *pICatItemAttr); DWORD DwNumBlockRequests() { return m_cBlockRequestsReadyForDispatch; } ISMTPServerEx * GetISMTPServerEx() { return m_pISMTPServerEx; } VOID LogAmbiguousEvent(ICategorizerItem *pItem); DWORD m_dwSignature; ISMTPServer *m_pISMTPServer; ISMTPServerEx *m_pISMTPServerEx; ICategorizerParameters *m_pICatParams; DWORD m_cBlockRequestsReserved; DWORD m_cBlockRequestsAllocated; DWORD m_cBlockRequestsReadyForDispatch; DWORD m_cBlockSize; LIST_ENTRY m_listentry; PSEARCH_REQUEST m_prgSearchRequests; ICategorizerItem **m_rgpICatItems; CBatchLdapConnection *m_pConn; LPSTR m_pszSearchFilter; CICategorizerQueriesIMP m_CICatQueries; CICategorizerAsyncContextIMP m_CICatAsyncContext; CSimpArray m_csaItemAttr; friend class CBatchLdapConnection; friend class CICategorizerAsyncContextIMP; }; //------------------------------------------------------------ // // Class CBatchLdapConnection // //------------------------------------------------------------ class CBatchLdapConnection : public CLdapConnectionCache::CCachedLdapConnection { private: #define SIGNATURE_CBATCHLDAPCONN (DWORD)'oCLB' #define SIGNATURE_CBATCHLDAPCONN_INVALID (DWORD)'oCLX' public: CBatchLdapConnection( LPSTR szHost, DWORD dwPort, LPSTR szNamingContext, LPSTR szAccount, LPSTR szPassword, LDAP_BIND_TYPE bt, CLdapConnectionCache *pCache) : CCachedLdapConnection( szHost, dwPort, szNamingContext, szAccount, szPassword, bt, pCache) { m_dwSignature = SIGNATURE_CBATCHLDAPCONN; m_pInsertionBlock = NULL; InitializeListHead(&m_listhead); InitializeSpinLock(&m_spinlock); m_cInsertionContext = 0; if(m_nMaxSearchBlockSize == 0) InitializeFromRegistry(); InitializeSpinLock(&m_spinlock_insertionrequests); m_dwcPendingSearches = 0; m_fDPS_Was_Here = FALSE; InitializeListHead(&m_listhead_insertionrequests); InitializeListHead(&m_listhead_cancelnotifies); } ~CBatchLdapConnection() { _ASSERT(m_dwSignature == SIGNATURE_CBATCHLDAPCONN); m_dwSignature = SIGNATURE_CBATCHLDAPCONN_INVALID; _ASSERT(m_dwcPendingSearches == 0); } CLdapConnection *GetConnection() { return( this ); } VOID GetInsertionContext() { AcquireSpinLock(&m_spinlock); m_cInsertionContext++; ReleaseSpinLock(&m_spinlock); } VOID ReleaseInsertionContext() { AcquireSpinLock(&m_spinlock); if((--m_cInsertionContext) == 0) { LIST_ENTRY listhead_dispatch; // // Remove all blocks from the insertion list and put them in the dispatch list // if(IsListEmpty(&m_listhead)) { // No blocks ReleaseSpinLock(&m_spinlock); InitializeListHead(&listhead_dispatch); } else { CopyMemory(&listhead_dispatch, &m_listhead, sizeof(LIST_ENTRY)); listhead_dispatch.Blink->Flink = &listhead_dispatch; listhead_dispatch.Flink->Blink = &listhead_dispatch; InitializeListHead(&m_listhead); ReleaseSpinLock(&m_spinlock); // // Dispatch all the blocks // DispatchBlocks(&listhead_dispatch); } } else { ReleaseSpinLock(&m_spinlock); } } HRESULT HrInsertSearchRequest( ISMTPServer *pISMTPServer, ICategorizerParameters *pICatParams, CCatAddr *pCCatAddr, LPSEARCHCOMPLETION fnSearchCompletion, CStoreListResolveContext *pslrc, LPSTR pszSearchFilter, LPSTR pszDistinguishingAttribute, LPSTR pszDistinguishingAttributeValue); static VOID InitializeFromRegistry(); VOID IncrementPendingSearches(DWORD dw = 1) { AcquireSpinLock(&m_spinlock_insertionrequests); m_dwcPendingSearches += dw; ReleaseSpinLock(&m_spinlock_insertionrequests); } VOID DecrementPendingSearches(DWORD dwcSearches = 1); HRESULT HrInsertInsertionRequest( CInsertionRequest *pCInsertionRequest); VOID CancelAllSearches( HRESULT hr = HRESULT_FROM_WIN32(ERROR_CANCELLED)); private: CSearchRequestBlock *GetSearchRequestBlock(); VOID RemoveSearchRequestBlockFromList( CSearchRequestBlock *pBlock) { AcquireSpinLock(&m_spinlock); RemoveEntryList(&(pBlock->m_listentry)); ReleaseSpinLock(&m_spinlock); } VOID DispatchBlocks(PLIST_ENTRY listhead); public: static DWORD m_nMaxConnectionRetries; private: static DWORD m_nMaxSearchBlockSize; static DWORD m_nMaxPendingSearches; DWORD m_dwSignature; LIST_ENTRY m_listhead; SPIN_LOCK m_spinlock; LONG m_cInsertionContext; CSearchRequestBlock *m_pInsertionBlock; SPIN_LOCK m_spinlock_insertionrequests; DWORD m_dwcPendingSearches; DWORD m_dwcReservedSearches; LIST_ENTRY m_listhead_insertionrequests; typedef struct _tagCancelNotify { LIST_ENTRY le; HRESULT hrCancel; } CANCELNOTIFY, *PCANCELNOTIFY; // This list is also protected by m_spinlock_insertionrequests LIST_ENTRY m_listhead_cancelnotifies; CExShareLock m_cancellock; BOOL m_fDPS_Was_Here; friend class CSearchRequestBlock; }; //------------------------------------------------------------ // // class CBatchLdapConnectionCache // //------------------------------------------------------------ class CBatchLdapConnectionCache : public CLdapConnectionCache { public: CBatchLdapConnectionCache( ISMTPServerEx *pISMTPServerEx) : CLdapConnectionCache(pISMTPServerEx) { } HRESULT GetConnection( CBatchLdapConnection **ppConn, LPSTR szHost, DWORD dwPort, LPSTR szNamingContext, LPSTR szAccount, LPSTR szPassword, LDAP_BIND_TYPE bt, PVOID pCreateContext = NULL) { return CLdapConnectionCache::GetConnection( (CLdapConnection **)ppConn, szHost, dwPort, szNamingContext, szAccount, szPassword, bt, pCreateContext); } CCachedLdapConnection *CreateCachedLdapConnection( LPSTR szHost, DWORD dwPort, LPSTR szNamingContext, LPSTR szAccount, LPSTR szPassword, LDAP_BIND_TYPE bt, PVOID pCreateContext) { CCachedLdapConnection *pret; pret = new CBatchLdapConnection( szHost, dwPort, szNamingContext, szAccount, szPassword, bt, this); if(pret) if(FAILED(pret->HrInitialize())) { pret->Release(); pret = NULL; } return pret; } }; //------------------------------------------------------------ // // class CStoreListResolveContext // //------------------------------------------------------------ CatDebugClass(CStoreListResolveContext) { public: CStoreListResolveContext(CEmailIDLdapStore *pStore); ~CStoreListResolveContext(); VOID AddRef() { InterlockedIncrement((PLONG) &m_cRefs); } VOID Release() { if( InterlockedDecrement((PLONG) &m_cRefs) == 0 ) delete this; } HRESULT HrInitialize( ISMTPServer *pISMTPServer, ICategorizerParameters *pICatParams); HRESULT HrLookupEntryAsync( CCatAddr *pCCatAddr); VOID Cancel(); CCfgConnection *GetConnection(); VOID GetInsertionContext(); VOID ReleaseInsertionContext(); HRESULT HrInsertInsertionRequest( CInsertionRequest *pCInsertionRequest); ISMTPServerEx * GetISMTPServerEx() { return m_pISMTPServerEx; } BOOL Canceled() { return m_fCanceled; } HRESULT HrInvalidateConnectionAndRetrieveNewConnection( CBatchLdapConnection *pConn, BOOL fIncrementRetryCount = TRUE); private: static VOID AsyncLookupCompletion( CCatAddr *pCCatAddr, CStoreListResolveContext *pslrc, CBatchLdapConnection *pConn); VOID LogSLRCFailover( IN DWORD dwcRetries, IN LPSTR pszOldHost, IN LPSTR pszNewHost); VOID LogSLRCFailure( IN DWORD dwcRetries, IN LPSTR pszHost); private: #define SIGNATURE_CSTORELISTRESOLVECONTEXT (DWORD)'CRLS' #define SIGNATURE_CSTORELISTRESOLVECONTEXT_INVALID (DWORD)'XRLS' DWORD m_cRefs; DWORD m_dwSignature; CCfgConnection *m_pConn; CRITICAL_SECTION m_cs; DWORD m_dwcInsertionContext; BOOL m_fCanceled; DWORD m_dwcRetries; DWORD m_dwcCompletedLookups; ISMTPServer *m_pISMTPServer; ISMTPServerEx *m_pISMTPServerEx; ICategorizerParameters *m_pICatParams; CEmailIDLdapStore *m_pStore; }; inline CSearchRequestBlock::CSearchRequestBlock( CBatchLdapConnection *pConn) : m_CICatQueries( &m_pszSearchFilter ) { _ASSERT(m_dwSignature == SIGNATURE_CSEARCHREQUESTBLOCK); m_pISMTPServer = NULL; m_pISMTPServerEx = NULL; m_pICatParams = NULL; m_pszSearchFilter = NULL; m_cBlockRequestsReserved = 0; m_cBlockRequestsAllocated = 0; m_cBlockRequestsReadyForDispatch = 0; m_pConn = pConn; m_pConn->AddRef(); } //------------------------------------------------------------ // // class CSingleSearchReinsertionRequest // //------------------------------------------------------------ CatDebugClass(CSingleSearchReinsertionRequest), public CInsertionRequest { public: HRESULT HrInsertSearches( DWORD dwcSearches); VOID NotifyDeQueue( HRESULT hr); #define SIGNATURE_CSingleSearchReinsertionRequest (DWORD)'qRIS' #define SIGNATURE_CSingleSearchReinsertionRequest_INVALID (DWORD)'XRIS' CSingleSearchReinsertionRequest( CStoreListResolveContext *pslrc, CCatAddr *pCCatAddr) { m_dwSignature = SIGNATURE_CSingleSearchReinsertionRequest; m_hr = S_OK; m_dwcSearches = 0; m_pslrc = pslrc; m_pCCatAddr = pCCatAddr; m_pCCatAddr->AddRef(); } ~CSingleSearchReinsertionRequest() { if(!m_dwcSearches) m_pCCatAddr->LookupCompletion(); m_pCCatAddr->Release(); _ASSERT(m_dwSignature == SIGNATURE_CSingleSearchReinsertionRequest); m_dwSignature = SIGNATURE_CSingleSearchReinsertionRequest_INVALID; } ISMTPServerEx *GetISMTPServerEx() { return m_pslrc->GetISMTPServerEx(); } private: DWORD m_dwSignature; HRESULT m_hr; DWORD m_dwcSearches; CStoreListResolveContext *m_pslrc; CCatAddr *m_pCCatAddr; }; #endif _ASYNCCTX_H_