/* ---------------------------------------------------------------------- Module: ULS.DLL (Service Provider) File: sppqueue.cpp Content: This file contains the pending item/queue objects. History: 10/15/96 Chu, Lon-Chan [lonchanc] Created. Copyright (c) Microsoft Corporation 1996-1997 ---------------------------------------------------------------------- */ #include "ulsp.h" #include "spinc.h" // #define MEASURE_ENUM_USER_INFO 1 ULONG g_uResponseTimeout = ILS_DEF_RESP_TIMEOUT; ULONG g_uResponsePollPeriod = ILS_DEF_RESP_POLL_PERIOD; SP_CResponseQueue *g_pRespQueue = NULL; SP_CRequestQueue *g_pReqQueue = NULL; SP_CRefreshScheduler *g_pRefreshScheduler = NULL; typedef BOOL (RESPONSE_HANDLER) ( HRESULT, SP_CResponse * ); typedef LPARAM (REQUEST_HANDLER) ( MARSHAL_REQ * ); extern RESPONSE_HANDLER *GetResponseHandler ( ULONG uNotifyMsg ); extern REQUEST_HANDLER *GetRequestHandler ( ULONG uNotifyMsg ); /* ---------- REQUEST QUEUE ----------- */ MARSHAL_REQ * MarshalReq_Alloc ( ULONG uNotifyMsg, ULONG cbSize, ULONG cParams ) { // Align the chunk of data for each parameter on 4-byte boundary // cbSize += cParams * sizeof (DWORD); // Calculate the total size of marshal buffer // ULONG cbTotalSize = sizeof (MARSHAL_REQ) + cParams * sizeof (DWORD) + cbSize; // Allocate the marshal buffer // MARSHAL_REQ *p = (MARSHAL_REQ *) MemAlloc (cbTotalSize); if (p != NULL) { // p->next = NULL; p->cbTotalSize = cbTotalSize; p->pb = (BYTE *) ((ULONG_PTR) p + (cbTotalSize - cbSize)); p->uRespID = GetUniqueNotifyID (); p->uNotifyMsg = uNotifyMsg; p->cParams = cParams; } return p; } HRESULT MarshalReq_SetParam ( MARSHAL_REQ *p, ULONG nIndex, DWORD_PTR dwParam, ULONG cbParamSize ) { if (p != NULL && nIndex < p->cParams) { MyAssert (p->aParams[nIndex] == 0); // not used before // If cbParamSize > 0, then // this means uParam is a pointer to a structure or // a pointer to a string // if (cbParamSize > 0) { // The pointer is now the one pointing to the new location // p->aParams[nIndex] = (DWORD_PTR) p->pb; // Copy the data chunk // CopyMemory (p->pb, (VOID *) dwParam, cbParamSize); // Make sure the data chunk is aligned on 4-byte boundary // if (cbParamSize & 0x3) { // Round it up // cbParamSize = (cbParamSize & (~0x3)) + 4; } // Adjust the running pointer // p->pb += cbParamSize; } else { // uParam can be an signed/unsigned integer, // p->aParams[nIndex] = dwParam; } } else { MyAssert (FALSE); } return S_OK; } DWORD_PTR MarshalReq_GetParam ( MARSHAL_REQ *p, ULONG nIndex ) { DWORD_PTR dwParam = 0; if (p != NULL && nIndex < p->cParams) { dwParam = p->aParams[nIndex]; } else { MyAssert (FALSE); } return dwParam; } HRESULT MarshalReq_SetParamServer ( MARSHAL_REQ *p, ULONG nIndex, SERVER_INFO *pServer, ULONG cbServer ) { if (p != NULL && nIndex < p->cParams) { MyAssert (p->aParams[nIndex] == 0); // not used before MyAssert (cbServer > sizeof (SERVER_INFO)); // The pointer is now the one pointing to the new location // p->aParams[nIndex] = (DWORD_PTR) p->pb; // Linearize the server info // IlsLinearizeServerInfo (p->pb, pServer); // Make sure the data chunk is aligned on 4-byte boundary // if (cbServer & 0x3) { // Round it up // cbServer = (cbServer & (~0x3)) + 4; } // Adjust the running pointer // p->pb += cbServer; } return S_OK; } SP_CRequestQueue:: SP_CRequestQueue ( VOID ) : m_ItemList (NULL), m_uCurrOpRespID (INVALID_RESP_ID) { // Create critical sections for thread safe access // ::MyInitializeCriticalSection (&m_csReqQ); ::MyInitializeCriticalSection (&m_csCurrOp); } SP_CRequestQueue:: ~SP_CRequestQueue ( VOID ) { // when this is called, the hidden window thread exited already. // this is assured in UlsLdap_Deinitialize(). // WriteLock (); // Free all the items in this list // MARSHAL_REQ *p, *next; for (p = m_ItemList; p != NULL; p = next) { next = p->next; MemFree (p); } m_ItemList = NULL; WriteUnlock (); // Delete critical sections // ::MyDeleteCriticalSection (&m_csReqQ); ::MyDeleteCriticalSection (&m_csCurrOp); } HRESULT SP_CRequestQueue:: Enter ( MARSHAL_REQ *p ) { // Make sure we have valid pointers // if (p == NULL) { MyAssert (FALSE); return ILS_E_POINTER; } MyAssert (! MyIsBadWritePtr (p, p->cbTotalSize)); MyAssert (p->next == NULL); MyAssert (p->uRespID != 0); WriteLock (); // Append the new request // p->next = NULL; if (m_ItemList == NULL) { m_ItemList = p; } else { for ( MARSHAL_REQ *prev = m_ItemList; prev->next != NULL; prev = prev->next) ; MyAssert (prev != NULL); prev->next = p; } WriteUnlock (); // Signal the internal request thread to pick up this request // SetEvent (g_hevNewRequest); return S_OK; } VOID SP_CRequestQueue:: Schedule ( VOID ) { MARSHAL_REQ *p; while (IsAnyReqInQueue () && ! g_fExitNow) { // Reset to null, we will use this as an indicator // to see if we need to process the request // p = NULL; // Lock request queue // WriteLock (); // Get a request to process // if (IsAnyReqInQueue ()) { p = m_ItemList; m_ItemList = m_ItemList->next; } // We want to lock both request queue and CurrOp at the same time // because we cannot have a temporal window that either one can change. // Set CurrOp // if (p != NULL) { // Lock CurrOp // LockCurrOp (); // Set CurrOp // m_uCurrOpRespID = p->uRespID; // Unlock CurrOp // UnlockCurrOp (); } // Unlock request queue // WriteUnlock (); // Make sure we have something to process // if (p == NULL) { // Nothing to do any more // MyAssert (FALSE); break; } // Let's process the request // Dispatch (p); MemFree(p); } } HRESULT SP_CRequestQueue:: Cancel ( ULONG uRespID ) { HRESULT hr; MARSHAL_REQ *p, *next, *prev; // The locking order is always in // Lock(PendingOpQueue), Lock(RequestQueue), Lock (CurrOp) // WriteLock (); LockCurrOp (); if (m_uCurrOpRespID == uRespID) { // Invalidate the curr op. // When the curr op is done, then the request thread will remove it // from the pending op queue. // m_uCurrOpRespID = INVALID_RESP_ID; hr = S_OK; } else { // Look for the item with a matching response id // for (prev = NULL, p = m_ItemList; p != NULL; prev = p, p = next) { // Cache the next pointer // next = p->next; // See if the response id matches // if (p->uRespID == uRespID) { // It is a match // MyDebugMsg ((ZONE_REQ, "ULS: cancelled request(0x%lX) in ReqQ\r\n", p->uNotifyMsg)); // Let's destroy this item // if (p == m_ItemList) { m_ItemList = next; } else { MyAssert (prev != NULL); prev->next = next; } // Free this structure // MemFree (p); // Get out of the loop // break; } } // for hr = (p == NULL) ? ILS_E_NOTIFY_ID : S_OK; } // else UnlockCurrOp (); WriteUnlock (); return hr; } VOID SP_CRequestQueue:: Dispatch ( MARSHAL_REQ *p ) { // Make sure we have a valid pointer // if (p == NULL) { MyAssert (FALSE); return; } // If it is keep alive, then do it // HRESULT hr; if (p->uNotifyMsg == WM_ILS_REFRESH) { // Keep alive handler // if (g_pRefreshScheduler != NULL) { ULONG uTTL = (ULONG) MarshalReq_GetParam (p, 0); hr = g_pRefreshScheduler->SendRefreshMessages (uTTL); } else { MyAssert (FALSE); } return; } // Locate the appropriate handler // REQUEST_HANDLER *pfn = ::GetRequestHandler (p->uNotifyMsg); if (pfn == NULL) { MyAssert (FALSE); return; } // Send the request to the server // MyDebugMsg ((ZONE_REQ, "ULS: sending request(0x%lX)\r\n", p->uNotifyMsg)); ULONG uRespID = p->uRespID; LPARAM lParam = (*pfn) (p); MyDebugMsg ((ZONE_REQ, "ULS: sent request(0x%lX), lParam=0x%lX\r\n", p->uNotifyMsg, lParam)); if (lParam != 0) { ::PostMessage (g_hWndNotify, p->uNotifyMsg, p->uRespID, lParam); return; } // BUGBUG: this is a workaround for a server bug which results in lost requests if several // are sent very quickly. Remove this Sleep() as soon as the bug is fixed!!! // Sleep(100); // Lock CurrOp again // LockCurrOp (); // Is this request cancelled // BOOL fCancelled = (m_uCurrOpRespID == INVALID_RESP_ID) ? TRUE : FALSE; // Clean up CurrOp // m_uCurrOpRespID = INVALID_RESP_ID; // Unlock CurrOp // UnlockCurrOp (); // If this request was cancelled, then remove it from the pending op queue // if (fCancelled) { // Redirect the call to the pending op queue object // if (g_pRespQueue != NULL) { g_pRespQueue->Cancel (uRespID); } else { MyAssert (FALSE); } } } /* ---------- RESPONSE ITEM ----------- */ /* ---------- public methods ----------- */ SP_CResponse:: SP_CResponse ( VOID ) : m_pSession (NULL), // Clean up session pointer m_pLdapMsg (NULL), // Clean up ldap msg pointer m_next (NULL) // Clean up the pointer to the next pending item { // Clean up pending info structure // ::ZeroMemory (&m_ri, sizeof (m_ri)); // Fill in creation time // UpdateLastModifiedTime (); m_tcTimeout = g_uResponseTimeout; } SP_CResponse:: ~SP_CResponse ( VOID ) { // Release the session if needed // if (m_pSession != NULL) m_pSession->Disconnect (); // Free the ldap msg if needed // if (m_pLdapMsg != NULL) ::ldap_msgfree (m_pLdapMsg); // Free extended attribute name list // ::MemFree (m_ri.pszAnyAttrNameList); // Free protocol names to resolve // ::MemFree (m_ri.pszProtNameToResolve); } /* ---------- protected methods ----------- */ VOID SP_CResponse:: EnterResult ( LDAPMessage *pLdapMsg ) { // Free the old ldap msg if needed // if (m_pLdapMsg != NULL) ::ldap_msgfree (m_pLdapMsg); // Keep the new ldap msg // m_pLdapMsg = pLdapMsg; } /* ---------- private methods ----------- */ /* ---------- RESPONSE QUEUE ----------- */ /* ---------- public methods ----------- */ SP_CResponseQueue:: SP_CResponseQueue ( VOID ) : m_ItemList (NULL) // Clean up the item list { // Create a critical section for thread safe access // ::MyInitializeCriticalSection (&m_csRespQ); } SP_CResponseQueue:: ~SP_CResponseQueue ( VOID ) { // when this is called, the hidden window thread exited already. // this is assured in UlsLdap_Deinitialize(). // WriteLock (); // Free all the items in this list // SP_CResponse *pItem, *next; for (pItem = m_ItemList; pItem != NULL; pItem = next) { next = pItem->GetNext (); delete pItem; } m_ItemList = NULL; WriteUnlock (); // Delete the critical section // ::MyDeleteCriticalSection (&m_csRespQ); } HRESULT SP_CResponseQueue:: EnterRequest ( SP_CSession *pSession, RESP_INFO *pInfo ) { // Make sure we have valid pointers // if (pSession == NULL || pInfo == NULL) { MyAssert (FALSE); return ILS_E_POINTER; } // Sanity checks // MyAssert (! MyIsBadWritePtr (pInfo, sizeof (*pInfo))); MyAssert (! MyIsBadWritePtr (pSession, sizeof (*pSession))); MyAssert (pInfo->ld != NULL && pInfo->uMsgID[0] != INVALID_MSG_ID); MyAssert (pInfo->uRespID != 0); // Create a new pending item // SP_CResponse *pItem = new SP_CResponse; if (pItem == NULL) return ILS_E_MEMORY; // Remember the contents of pending info // pItem->EnterRequest (pSession, pInfo); WriteLock (); // If this is the first item on the list, then // let's start the timer // if (m_ItemList == NULL) ::SetTimer (g_hWndHidden, ID_TIMER_POLL_RESULT, g_uResponsePollPeriod, NULL); // Append the new pending op // pItem->SetNext (NULL); if (m_ItemList == NULL) { m_ItemList = pItem; } else { for ( SP_CResponse *prev = m_ItemList; prev->GetNext () != NULL; prev = prev->GetNext ()) ; MyAssert (prev != NULL); prev->SetNext (pItem); } WriteUnlock (); return S_OK; } HRESULT SP_CResponseQueue:: PollLdapResults ( LDAP_TIMEVAL *pTimeout ) { MyAssert (pTimeout != NULL); SP_CResponse *pItem, *next, *prev; INT RetCode; RESP_INFO *pInfo; LDAPMessage *pLdapMsg; HRESULT hr; RESPONSE_HANDLER *pfn; ULONG uResultSetType; ::KillTimer (g_hWndHidden, ID_TIMER_POLL_RESULT); // avoid overrun WriteLock (); // Enumerate all the items to get available results for them // for (prev = NULL, pItem = m_ItemList; pItem != NULL; pItem = next) { // Cache the next pointer // next = pItem->GetNext (); // Get the pinding info structure // pInfo = pItem->GetRespInfo (); // Clean up ldap msg pointer // pLdapMsg = NULL; // Make sure ew have valid ld and msg id // MyAssert (pInfo->ld != NULL); MyAssert (pInfo->uMsgID[0] != INVALID_MSG_ID || pInfo->uMsgID[1] != INVALID_MSG_ID); // Check integrity in pending info // MyAssert (pInfo->uRespID != 0); // Set the result set type // switch (pInfo->uNotifyMsg) { case WM_ILS_ENUM_CLIENTS: case WM_ILS_ENUM_CLIENTINFOS: #ifdef ENABLE_MEETING_PLACE case WM_ILS_ENUM_MEETINGS: case WM_ILS_ENUM_MEETINGINFOS: #endif uResultSetType = LDAP_MSG_RECEIVED; // partial result set break; default: uResultSetType = LDAP_MSG_ALL; // complete result set break; } #ifdef _DEBUG if (MyIsBadWritePtr (pInfo->ld, sizeof (*(pInfo->ld)))) { MyDebugMsg ((ZONE_CONN, "ILS:: poll result, bad ld=0x%p\r\n", pInfo->ld)); MyAssert (FALSE); } if (pInfo->ld != pItem->GetSession()->GetLd()) { MyDebugMsg ((ZONE_CONN, "ILS:: poll result, inconsistent pInfo->ld=0x%p, pItem->pSession->ld=0x%p\r\n", pInfo->ld, pItem->GetSession()->GetLd())); MyAssert (FALSE); } #endif // _DEBUG // If primary msg id is valid // if (pInfo->uMsgID[0] != INVALID_MSG_ID) RetCode = ::ldap_result (pInfo->ld, pInfo->uMsgID[0], uResultSetType, pTimeout, &pLdapMsg); else // If secondary msg id is valid // if (pInfo->uMsgID[1] != INVALID_MSG_ID) RetCode = ::ldap_result (pInfo->ld, pInfo->uMsgID[1], uResultSetType, pTimeout, &pLdapMsg); // If timeout, ignore this item // if (RetCode == 0) { // Let's see if this item is expired // if (! pItem->IsExpired ()) { // Not timed out, next please! // prev = pItem; continue; } // Timed out // hr = ILS_E_TIMEOUT; } // If error, delete this request item // if (RetCode == -1) { // Convert the error // hr = ::LdapError2Hresult (pInfo->ld->ld_errno); } else // If not timed out // if (RetCode != 0) { // It appears to be successful! // MyAssert (pLdapMsg != NULL); // Cache the ldap msg pointer pItem->EnterResult (pLdapMsg); // Get the ldap error code // hr = (pLdapMsg != NULL) ? ::LdapError2Hresult (pLdapMsg->lm_returncode) : S_OK; } // Get the result handler based on uNotifyMsg // pfn = ::GetResponseHandler (pInfo->uNotifyMsg); if (pfn == NULL) { prev = pItem; continue; } // Check integrity in pending info // MyAssert (pInfo->uRespID != 0); // Deal with the result or error // MyDebugMsg ((ZONE_RESP, "ULS: response(0x%lX), hr=0x%lX\r\n", pInfo->uNotifyMsg, hr)); if ((*pfn) (hr, pItem)) { // Let's destroy this item // if (pItem == m_ItemList) { m_ItemList = next; } else { MyAssert (prev != NULL); prev->SetNext (next); } delete pItem; // SP_CSession::Disconnect() and ldap_msgfree() will be called in destructor } else { // Let's keep this item around. // There are pending results coming in. // pItem->UpdateLastModifiedTime (); // Update the pointer // prev = pItem; } } // for // If there is no more items on the list, then stop the timer // if (m_ItemList != NULL) ::SetTimer (g_hWndHidden, ID_TIMER_POLL_RESULT, g_uResponsePollPeriod, NULL); WriteUnlock (); return S_OK; } HRESULT SP_CResponseQueue:: Cancel ( ULONG uRespID ) { SP_CResponse *pItem, *next, *prev; RESP_INFO *pInfo; BOOL fNeedCleanup = FALSE; WriteLock (); // Look for the item with a matching response id // for (prev = NULL, pItem = m_ItemList; pItem != NULL; prev = pItem, pItem = next) { // Cache the next pointer // next = pItem->GetNext (); // Get the pinding info structure // pInfo = pItem->GetRespInfo (); MyAssert (pInfo != NULL); // See if the response id matches // if (pInfo->uRespID == uRespID) { // It is a match // SP_CSession *pSession = pItem->GetSession (); MyAssert (pSession != NULL); // Make sure we have a valid ldap session // MyAssert (pInfo->ld != NULL); // If we are NOT in the request thread, then we need to marshal it // to the request thread!!! Exit and report success!!! // if (GetCurrentThreadId () != g_dwReqThreadID) { MyDebugMsg ((ZONE_RESP, "ULS: marshalling request(0x%lX) in RespQ\r\n", pInfo->uNotifyMsg)); MARSHAL_REQ *pReq = MarshalReq_Alloc (WM_ILS_CANCEL, 0, 1); if (pReq != NULL) { MarshalReq_SetParam (pReq, 0, (DWORD) uRespID, 0); if (g_pReqQueue != NULL) { // This means that the locking order is // Lock(PendingOpQueue), Lock(RequestQueue) // g_pReqQueue->Enter (pReq); } else { MyAssert (FALSE); } } // Exit this loop // break; } // Indicate that we need to clean up item. Why? // because we should not have any network operation inside critical section. // this is to avoid any possible network blocking. // fNeedCleanup = TRUE; // Let's destroy this item // if (pItem == m_ItemList) { m_ItemList = next; } else { MyAssert (prev != NULL); prev->SetNext (next); } // Get out of the loop // break; } // if matched } // for // If there is no more items on the list, then stop the timer // if (m_ItemList == NULL) ::KillTimer (g_hWndHidden, ID_TIMER_POLL_RESULT); WriteUnlock (); if (fNeedCleanup && pItem != NULL) { MyDebugMsg ((ZONE_RESP, "ULS: cancelled request(0x%lX) in RespQ\r\n", pInfo->uNotifyMsg)); // Get resp info pointer // pInfo = pItem->GetRespInfo (); MyAssert (pInfo != NULL); // Abandon the primary response if needed // if (pInfo->uMsgID[1] != INVALID_MSG_ID) ::ldap_abandon (pInfo->ld, pInfo->uMsgID[1]); // Abandon the secondary response if needed // if (pInfo->uMsgID[0] != INVALID_MSG_ID) ::ldap_abandon (pInfo->ld, pInfo->uMsgID[0]); // SP_CSession::Disconnect() and ldap_msgfree() will be called in destructor // delete pItem; } return ((pItem == NULL) ? ILS_E_NOTIFY_ID : S_OK); } /* ---------- protected methods ----------- */ /* ---------- private methods ----------- */ /* ==================== utilities ====================== */ VOID FillDefRespInfo ( RESP_INFO *pInfo, ULONG uRespID, LDAP *ld, ULONG uMsgID, ULONG u2ndMsgID ) { // Clean up // ZeroMemory (pInfo, sizeof (*pInfo)); // Cache the ldap session // pInfo->ld = ld; // Generate a unique notify id // pInfo->uRespID = uRespID; // Store the primary and seconary msg ids // pInfo->uMsgID[0] = uMsgID; pInfo->uMsgID[1] = u2ndMsgID; } /* ---------- REFRESH SCHEDULER ----------- */ /* ---------- public methods ----------- */ SP_CRefreshScheduler:: SP_CRefreshScheduler ( VOID ) : m_ListHead (NULL) // Initialize the item list { // Create a critical section for thread safe access // ::MyInitializeCriticalSection (&m_csRefreshScheduler); } SP_CRefreshScheduler:: ~SP_CRefreshScheduler ( VOID ) { WriteLock (); // Clean up the item list // REFRESH_ITEM *p, *next; for (p = m_ListHead; p != NULL; p = next) { next = p->next; MemFree (p); } m_ListHead = NULL; WriteUnlock (); // Delete the critical section // ::MyDeleteCriticalSection (&m_csRefreshScheduler); } HRESULT SP_CRefreshScheduler:: SendRefreshMessages ( UINT uTimerID ) { SP_CClient *pClient; #ifdef ENABLE_MEETING_PLACE SP_CMeeting *pMtg; #endif REFRESH_ITEM *prev, *curr; INT nIndex; // Lock the lists // ReadLock (); // Locate this object in the list // nIndex = TimerID2Index (uTimerID); for (prev = NULL, curr = m_ListHead; curr != NULL; curr = (prev = curr)->next) { if (curr->nIndex == nIndex) { // Find it. Let's send a refresh message for this object // switch (curr->ObjectType) { case CLIENT_OBJ: pClient = (SP_CClient *) curr->pObject; // Make sure this object is not deleted already // if (! MyIsBadWritePtr (pClient, sizeof (*pClient)) && pClient->IsValidObject ()) { // Make sure this object is valid and registered // if (pClient->IsRegistered ()) { MyDebugMsg ((ZONE_KA, "KA: send refresh msg for client\r\n")); // Let's send a refresh message for this client object // and update the new ttl value // pClient->AddRef (); pClient->SendRefreshMsg (); curr->uTTL = pClient->GetTTL (); pClient->Release (); } } else { MyAssert (FALSE); } break; #ifdef ENABLE_MEETING_PLACE case MTG_OBJ: pMtg = (SP_CMeeting *) curr->pObject; // Make sure this object is not deleted already // if (! MyIsBadWritePtr (pMtg, sizeof (*pMtg)) && pMtg->IsValidObject ()) { // Make sure this object is valid and registered // if (pMtg->IsRegistered ()) { MyDebugMsg ((ZONE_KA, "KA: send refresh msg for mtg\r\n")); // Let's send a refresh message for this user object // and update the new ttl value // pMtg->AddRef (); pMtg->SendRefreshMsg (); curr->uTTL = pMtg->GetTTL (); pMtg->Release (); } } else { MyAssert (FALSE); } break; #endif default: MyAssert (FALSE); break; } // Start the timer again and exit // Note that curr->uTTL is the new TTL value from the server // Also note that uTTL is in unit of minute // MyDebugMsg ((ZONE_KA, "KA: new ttl=%lu\r\n", curr->uTTL)); ::SetTimer (g_hWndHidden, uTimerID, Minute2TickCount (curr->uTTL), NULL); break; } // if } // for ReadUnlock (); return S_OK; } HRESULT SP_CRefreshScheduler:: EnterClientObject ( SP_CClient *pClient ) { if (pClient == NULL) return ILS_E_POINTER; return EnterObject (CLIENT_OBJ, (VOID *) pClient, pClient->GetTTL ()); } #ifdef ENABLE_MEETING_PLACE HRESULT SP_CRefreshScheduler:: EnterMtgObject ( SP_CMeeting *pMtg ) { if (pMtg == NULL) return ILS_E_POINTER; return EnterObject (MTG_OBJ, (VOID *) pMtg, pMtg->GetTTL ()); } #endif VOID *SP_CRefreshScheduler:: AllocItem ( BOOL fNeedLock ) { REFRESH_ITEM *p, *curr, *prev; INT nIndex, nLargestIndex; BOOL fGotTheNewIndex; // Allocate the structure // p = (REFRESH_ITEM *) MemAlloc (sizeof (REFRESH_ITEM)); if (p != NULL) { if (fNeedLock) WriteLock (); // Find out what should be the index for the new item // nLargestIndex = -1; // Yes, it is -1 for the case m_ListHead==NULL fGotTheNewIndex = FALSE; for (nIndex = 0, prev = NULL, curr = m_ListHead; curr != NULL; nIndex++, curr = (prev = curr)->next) { if (curr->nIndex > nIndex) { p->nIndex = nIndex; fGotTheNewIndex = TRUE; break; } nLargestIndex = curr->nIndex; } // Put the new item in the list in its appropriate position // if (fGotTheNewIndex) { if (prev == NULL) { // The new one must be the first one // MyAssert (p->nIndex == 0); p->next = m_ListHead; m_ListHead = p; } else { // The new one in the middle of the list // MyAssert (prev->nIndex < p->nIndex && p->nIndex < curr->nIndex); MyAssert (prev->next == curr); (prev->next = p)->next = curr; } } else { MyAssert (m_ListHead == NULL || prev != NULL); if (m_ListHead == NULL) { // The new one will be the only one in the list // p->nIndex = 0; (m_ListHead = p)->next = NULL; } else { // The new one is at the end of the list // MyAssert (prev != NULL && prev->next == NULL && curr == NULL); p->nIndex = nLargestIndex + 1; (prev->next = p)->next = curr; } } if (fNeedLock) WriteUnlock (); } // if (p != NULL) return p; } HRESULT SP_CRefreshScheduler:: EnterObject ( PrivateObjType ObjectType, VOID *pObject, ULONG uInitialTTL ) { HRESULT hr = S_OK; WriteLock (); // Enter this object to the list // REFRESH_ITEM *p = (REFRESH_ITEM *) AllocItem (FALSE); if (p == NULL) { hr = ILS_E_MEMORY; goto MyExit; } // Fill in fields // p->ObjectType = ObjectType; p->pObject = pObject; p->uTTL = uInitialTTL; // Turn on the timer // Note that uTTL is in unit of minutes... // ::SetTimer (g_hWndHidden, Index2TimerID (p->nIndex), Minute2TickCount (p->uTTL), NULL); MyExit: WriteUnlock (); return hr; } HRESULT SP_CRefreshScheduler:: RemoveObject ( VOID *pObject ) { REFRESH_ITEM *prev, *curr; WriteLock (); // Locate this object in the list // for (prev = NULL, curr = m_ListHead; curr != NULL; curr = (prev = curr)->next) { if (curr->pObject == pObject) { // Find it, let's kill the timer first // KillTimer (g_hWndHidden, Index2TimerID (curr->nIndex)); // Remove it from the list // if (prev == NULL) { // This one is the first one on the list // MyAssert (m_ListHead == curr); m_ListHead = curr->next; } else { // This one is in the middle of the list // MyAssert (prev->next == curr); prev->next = curr->next; } ::MemFree(curr); // Exit the loop // break; } } WriteUnlock (); return (curr != NULL ? S_OK : S_FALSE); } extern BOOL NotifyGeneric ( HRESULT, SP_CResponse * ); extern BOOL NotifyRegister ( HRESULT, SP_CResponse * ); extern BOOL NotifyResolveClient ( HRESULT, SP_CResponse * ); extern BOOL NotifyEnumClients ( HRESULT, SP_CResponse * ); extern BOOL NotifyEnumClientInfos ( HRESULT, SP_CResponse * ); extern BOOL NotifyResolveProt ( HRESULT, SP_CResponse * ); extern BOOL NotifyEnumProts ( HRESULT, SP_CResponse * ); extern BOOL NotifyResolveMtg ( HRESULT, SP_CResponse * ); extern BOOL NotifyEnumMtgInfos ( HRESULT, SP_CResponse * ); extern BOOL NotifyEnumMtgs ( HRESULT, SP_CResponse * ); extern BOOL NotifyEnumAttendees ( HRESULT, SP_CResponse * ); extern LPARAM AsynReq_RegisterClient ( MARSHAL_REQ * ); extern LPARAM AsynReq_RegisterProtocol ( MARSHAL_REQ * ); extern LPARAM AsynReq_RegisterMeeting ( MARSHAL_REQ * ); extern LPARAM AsynReq_UnRegisterClient ( MARSHAL_REQ * ); extern LPARAM AsynReq_UnRegisterProt ( MARSHAL_REQ * ); extern LPARAM AsynReq_UnRegisterMeeting ( MARSHAL_REQ * ); extern LPARAM AsynReq_SetClientInfo ( MARSHAL_REQ * ); extern LPARAM AsynReq_SetProtocolInfo ( MARSHAL_REQ * ); extern LPARAM AsynReq_SetMeetingInfo ( MARSHAL_REQ * ); extern LPARAM AsynReq_EnumClientsEx ( MARSHAL_REQ * ); extern LPARAM AsynReq_EnumProtocols ( MARSHAL_REQ * ); extern LPARAM AsynReq_EnumMtgsEx ( MARSHAL_REQ * ); extern LPARAM AsynReq_EnumAttendees ( MARSHAL_REQ * ); extern LPARAM AsynReq_ResolveClient ( MARSHAL_REQ * ); extern LPARAM AsynReq_ResolveProtocol ( MARSHAL_REQ * ); extern LPARAM AsynReq_ResolveMeeting ( MARSHAL_REQ * ); extern LPARAM AsynReq_UpdateAttendees ( MARSHAL_REQ * ); extern LPARAM AsynReq_Cancel ( MARSHAL_REQ * ); typedef struct { #ifdef DEBUG LONG nMsg; #endif RESPONSE_HANDLER *pfnRespHdl; REQUEST_HANDLER *pfnReqHdl; } RES_HDL_TBL; RES_HDL_TBL g_ResHdlTbl[] = { { #ifdef DEBUG WM_ILS_REGISTER_CLIENT, #endif NotifyRegister, AsynReq_RegisterClient }, { #ifdef DEBUG WM_ILS_UNREGISTER_CLIENT, #endif NotifyGeneric, AsynReq_UnRegisterClient }, { #ifdef DEBUG WM_ILS_SET_CLIENT_INFO, #endif NotifyGeneric, AsynReq_SetClientInfo }, { #ifdef DEBUG WM_ILS_RESOLVE_CLIENT, #endif NotifyResolveClient, AsynReq_ResolveClient }, { #ifdef DEBUG WM_ILS_ENUM_CLIENTS, #endif NotifyEnumClients, AsynReq_EnumClientsEx }, { #ifdef DEBUG WM_ILS_ENUM_CLIENTINFOS, #endif NotifyEnumClientInfos, AsynReq_EnumClientsEx }, { #ifdef DEBUG WM_ILS_REGISTER_PROTOCOL, #endif NotifyRegister, AsynReq_RegisterProtocol }, { #ifdef DEBUG WM_ILS_UNREGISTER_PROTOCOL, #endif NotifyGeneric, AsynReq_UnRegisterProt }, { #ifdef DEBUG WM_ILS_SET_PROTOCOL_INFO, #endif NotifyGeneric, AsynReq_SetProtocolInfo }, { #ifdef DEBUG WM_ILS_RESOLVE_PROTOCOL, #endif NotifyResolveProt, AsynReq_ResolveProtocol }, { #ifdef DEBUG WM_ILS_ENUM_PROTOCOLS, #endif NotifyEnumProts, AsynReq_EnumProtocols }, #ifdef ENABLE_MEETING_PLACE { #ifdef DEBUG WM_ILS_REGISTER_MEETING, #endif NotifyRegister, AsynReq_RegisterMeeting }, { #ifdef DEBUG WM_ILS_UNREGISTER_MEETING, #endif NotifyGeneric, AsynReq_UnRegisterMeeting }, { #ifdef DEBUG WM_ILS_SET_MEETING_INFO, #endif NotifyGeneric, AsynReq_SetMeetingInfo }, { #ifdef DEBUG WM_ILS_RESOLVE_MEETING, #endif NotifyResolveMtg, AsynReq_ResolveMeeting }, { #ifdef DEBUG WM_ILS_ENUM_MEETINGINFOS, #endif NotifyEnumMtgInfos, AsynReq_EnumMtgsEx }, { #ifdef DEBUG WM_ILS_ENUM_MEETINGS, #endif NotifyEnumMtgs, AsynReq_EnumMtgsEx }, { #ifdef DEBUG WM_ILS_ADD_ATTENDEE, #endif NotifyGeneric, AsynReq_UpdateAttendees }, { #ifdef DEBUG WM_ILS_REMOVE_ATTENDEE, #endif NotifyGeneric, AsynReq_UpdateAttendees }, { #ifdef DEBUG WM_ILS_ENUM_ATTENDEES, #endif NotifyEnumAttendees, AsynReq_EnumAttendees }, #endif // ENABLE_MEETING_PLACE { #ifdef DEBUG WM_ILS_CANCEL, #endif NULL, AsynReq_Cancel } }; #ifdef DEBUG VOID DbgValidateHandlerTable ( VOID ) { MyAssert (ARRAY_ELEMENTS (g_ResHdlTbl) == WM_ILS_LAST_ONE - WM_ILS_ASYNC_RES + 1); for (LONG i = 0; i < ARRAY_ELEMENTS (g_ResHdlTbl); i++) { if (g_ResHdlTbl[i].nMsg - WM_ILS_ASYNC_RES != i) { MyAssert (FALSE); break; } } } #endif RES_HDL_TBL * GetHandlerTableEntry ( ULONG uNotifyMsg ) { ULONG nIndex = uNotifyMsg - WM_ILS_ASYNC_RES; if (nIndex > WM_ILS_LAST_ONE) { MyAssert (FALSE); return NULL; } return &g_ResHdlTbl[nIndex]; } RESPONSE_HANDLER * GetResponseHandler ( ULONG uNotifyMsg ) { RES_HDL_TBL *p = GetHandlerTableEntry (uNotifyMsg); return ((p != NULL) ? p->pfnRespHdl : NULL); } REQUEST_HANDLER * GetRequestHandler ( ULONG uNotifyMsg ) { RES_HDL_TBL *p = GetHandlerTableEntry (uNotifyMsg); return ((p != NULL) ? p->pfnReqHdl : NULL); }