// Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved /*--------------------------------------------------------- Filename: timer.cpp Written By: B.Rajeev ----------------------------------------------------------*/ #include "precomp.h" #include "common.h" #include "sync.h" #include "timer.h" #include "message.h" #include "dummy.h" #include "flow.h" #include "frame.h" #include "ssent.h" #include "idmap.h" #include "opreg.h" #include "session.h" SnmpClThreadObject *Timer :: g_timerThread = NULL ; UINT Timer :: g_SnmpWmTimer = SNMP_WM_TIMER ; // static CriticalSection and CMap CriticalSection Timer::timer_CriticalSection; TimerMapping Timer::timer_mapping; TimerEventId Timer :: next_timer_event_id = ILLEGAL_TIMER_EVENT_ID+1 ; Window *SnmpTimerObject :: window = NULL ; CMap SnmpTimerObject :: timerMap ; SnmpClThreadObject :: SnmpClThreadObject () : SnmpThreadObject ( "SnmpCl" ) { } void SnmpClThreadObject :: Initialise () { } void SnmpClThreadObject :: Uninitialise () { delete SnmpTimerObject :: window ; SnmpTimerObject :: window = NULL ; delete this ; } SnmpClTrapThreadObject :: SnmpClTrapThreadObject () : SnmpThreadObject ( "SnmpClTrapThread" ) { } void SnmpClTrapThreadObject :: Initialise () { DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"SnmpClTrapThreadObject::Initialise: Initialised!!\n" ) ; ) } void SnmpClTrapThreadObject :: Uninitialise () { DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"SnmpClTrapThreadObject::Uninitialise: About to destroy trap thread\n" ) ; ) delete this ; DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"SnmpClTrapThreadObject::Uninitialise: Trap thread destroyed!!\n" ) ; ) } Timer::Timer(SnmpImpSession &session) { Timer::session = &session; } BOOL Timer::CreateCriticalSection() { return TRUE; } void Timer::DestroyCriticalSection() { } BOOL Timer::InitializeStaticComponents() { return CreateCriticalSection(); } void Timer::DestroyStaticComponents() { DestroyCriticalSection(); } // generates and returns a new event id // associates the pair (event_id, waiting_message) // creates the timer event TimerEventId Timer::SetTimerEvent(UINT timeout_value) { TimerEventId suggested_event_id = next_timer_event_id++; if ( suggested_event_id == ILLEGAL_TIMER_EVENT_ID ) suggested_event_id = next_timer_event_id++; // let the dummy session receive the window messages for timer events TimerEventId event_id = SnmpSetTimer( session->m_SessionWindow.GetWindowHandle(), suggested_event_id, timeout_value, NULL ); if ( (event_id == ILLEGAL_TIMER_EVENT_ID) || (event_id != suggested_event_id) ) throw GeneralException(Snmp_Error, Snmp_Local_Error,__FILE__,__LINE__); return event_id; } // generates and returns a new event id // associates the pair (event_id, waiting_message) // creates the timer event void Timer::SetMessageTimerEvent(WaitingMessage &waiting_message) { CriticalSectionLock session_lock(session->session_CriticalSection); if ( !session_lock.GetLock(INFINITE) ) return; // no use throwing exception TimerEventId event_id = session->timer_event_id; // register the timer event in both the instance CMap and the global CMap waiting_message_mapping.AddTail ( &waiting_message ) ; session_lock.UnLock(); CriticalSectionLock timer_lock(Timer::timer_CriticalSection); if ( !timer_lock.GetLock(INFINITE) ) throw GeneralException ( Snmp_Error , Snmp_Local_Error,__FILE__,__LINE__ ) ; timer_mapping[event_id] = this; timer_lock.UnLock(); } // Removes the association (event_id, waiting_message) // and also kills the registered timer event void Timer::CancelMessageTimer(WaitingMessage &waiting_message,TimerEventId event_id) { CriticalSectionLock session_lock(session->session_CriticalSection); if ( !session_lock.GetLock(INFINITE) ) return; // no use throwing exception // remove the timer event from the instance CMap POSITION t_Position = waiting_message_mapping.GetHeadPosition () ; while ( t_Position ) { POSITION t_OldPosition = t_Position ; WaitingMessage *t_Message = waiting_message_mapping.GetNext ( t_Position ) ; if ( t_Message == & waiting_message ) { waiting_message_mapping.RemoveAt(t_OldPosition); break ; } } session_lock.UnLock(); DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"Cancelled Message TimerEvent %d\n", event_id ) ; ) } // Removes the association (event_id, waiting_message) // and also kills the registered timer event void Timer::CancelTimer(TimerEventId event_id) { CriticalSectionLock timer_lock(Timer::timer_CriticalSection); if ( !timer_lock.GetLock(INFINITE) ) throw GeneralException ( Snmp_Error , Snmp_Local_Error,__FILE__,__LINE__ ) ; // remove the timer event from the global CMap timer_mapping.RemoveKey(event_id); timer_lock.UnLock(); SnmpKillTimer(NULL, event_id); DebugMacro4( SnmpDebugLog :: s_SnmpDebugLog->WriteFileAndLine ( __FILE__,__LINE__, L"Cancelled TimerEvent %d\n", event_id ) ; ) } // it determines the corresponding Timer and calls // its TimerEventNotification with the appropriate parameters void CALLBACK Timer::HandleGlobalEvent(HWND hWnd ,UINT message, UINT_PTR idEvent, DWORD dwTime) { CriticalSectionLock timer_lock(Timer::timer_CriticalSection); if ( !timer_lock.GetLock(INFINITE) ) throw GeneralException ( Snmp_Error , Snmp_Local_Error,__FILE__,__LINE__ ) ; Timer *timer; TimerEventId event_id = idEvent; BOOL found = timer_mapping.Lookup(event_id, timer); timer_lock.UnLock(); // if no such timer event, return if ( !found ) return; // let the timer handle the event timer->TimerEventNotification(event_id); return; } // used by the event handler to notify the timer event. // it must notify the corresponding waiting message void Timer::TimerEventNotification(TimerEventId event_id) { CriticalSectionLock session_lock(session->session_CriticalSection); if ( !session_lock.GetLock(INFINITE) ) return; // no use throwing exception WaitingMessage *waiting_message; // identify the waiting message corresponding to // the event_id. if no such event, ignore it POSITION t_Position = waiting_message_mapping.GetHeadPosition () ; while ( t_Position ) { waiting_message = waiting_message_mapping.GetNext ( t_Position ) ; // notify the waiting message of the event waiting_message->TimerNotification(); } // session_lock.UnLock(); The lock may be released at this point } // remove all the (timer_event_id, timer) associations // from the static mapping data structure Timer::~Timer(void) { WaitingMessage *waiting_message; POSITION current = waiting_message_mapping.GetHeadPosition(); while ( current != NULL ) { waiting_message = waiting_message_mapping.GetNext(current); TimerEventId event_id = waiting_message->GetTimerEventId () ; CriticalSectionLock timer_lock(Timer::timer_CriticalSection); if ( !timer_lock.GetLock(INFINITE) ) throw GeneralException ( Snmp_Error , Snmp_Local_Error,__FILE__,__LINE__ ) ; timer_mapping.RemoveKey ( event_id ); timer_lock.UnLock(); SnmpKillTimer(NULL, event_id ); } waiting_message_mapping.RemoveAll(); } SnmpTimerObject :: SnmpTimerObject ( HWND hWndArg, // handle of window for timer messages UINT_PTR timerIdArg, // timer identifier UINT elapsedArg, // time-out value TIMERPROC lpTimerFuncArg // address of timer procedure ) : hWnd ( hWndArg ) , timerId ( timerIdArg ) , lpTimerFunc ( lpTimerFuncArg ) { if ( ! window ) window = new Window ; timerId = SetTimer ( window->GetWindowHandle(), timerId , elapsedArg , lpTimerFunc ) ; CriticalSectionLock session_lock(Timer::timer_CriticalSection); if ( !session_lock.GetLock(INFINITE) ) throw GeneralException ( Snmp_Error , Snmp_Local_Error,__FILE__,__LINE__ ) ; if ( timerId ) { timerMap [ timerId ] = this ; } else throw GeneralException ( Snmp_Error , Snmp_Local_Error,__FILE__,__LINE__ ) ; } SnmpTimerObject :: ~SnmpTimerObject () { CriticalSectionLock session_lock(Timer::timer_CriticalSection); if ( !session_lock.GetLock(INFINITE) ) throw GeneralException ( Snmp_Error , Snmp_Local_Error,__FILE__,__LINE__ ) ; timerMap.RemoveKey ( timerId ); if (window) { KillTimer ( window->GetWindowHandle () , timerId ) ; } } void SnmpTimerObject :: TimerNotification ( HWND hWnd , UINT timerId ) { :: WaitPostMessage ( hWnd , Timer :: g_SnmpWmTimer , timerId , 0 ) ; } SnmpSetTimerObject :: SnmpSetTimerObject ( HWND hWndArg, // handle of window for timer messages UINT_PTR nIDEventArg, // timer identifier UINT uElapseArg, // time-out value TIMERPROC lpTimerFuncArg // address of timer procedure ) : hWnd ( hWndArg ) , timerId ( nIDEventArg ) , elapsedTime ( uElapseArg ) , lpTimerFunc ( lpTimerFuncArg ) { } SnmpSetTimerObject :: ~SnmpSetTimerObject () { } void SnmpSetTimerObject :: Process () { SnmpTimerObject *object = new SnmpTimerObject ( hWnd , timerId , elapsedTime , lpTimerFunc ) ; Complete () ; } SnmpKillTimerObject :: SnmpKillTimerObject ( HWND hWndArg , // handle of window that installed timer UINT_PTR uIDEventArg // timer identifier ) : hWnd ( hWndArg ) , timerId ( uIDEventArg ) , status ( TRUE ) { } void SnmpKillTimerObject :: Process () { CriticalSectionLock session_lock(Timer::timer_CriticalSection); if ( !session_lock.GetLock(INFINITE) ) throw GeneralException ( Snmp_Error , Snmp_Local_Error,__FILE__,__LINE__ ) ; SnmpTimerObject *object ; if ( SnmpTimerObject :: timerMap.Lookup ( timerId , object ) ) { delete object ; } else { status = FALSE ; } Complete () ; } UINT_PTR SnmpSetTimer ( HWND hWnd, // handle of window for timer messages UINT_PTR nIDEvent, // timer identifier UINT uElapse, // time-out value, TIMERPROC lpTimerFunc // address of timer procedure ) { SnmpSetTimerObject object ( hWnd , nIDEvent , uElapse , lpTimerFunc ) ; Timer :: g_timerThread->ScheduleTask ( object ) ; object.Exec () ; if ( object.Wait () ) { Timer :: g_timerThread->ReapTask ( object ) ; return object.GetTimerId () ; } else { Timer :: g_timerThread->ReapTask ( object ) ; return FALSE ; } } BOOL SnmpKillTimer ( HWND hWnd, // handle of window that installed timer UINT_PTR uIDEvent // timer identifier ) { SnmpKillTimerObject object ( hWnd , uIDEvent ) ; Timer :: g_timerThread->ScheduleTask ( object ) ; object.Exec () ; if ( object.Wait () ) { Timer :: g_timerThread->ReapTask ( object ) ; return object.GetStatus () ; } else { Timer :: g_timerThread->ReapTask ( object ) ; return FALSE ; } }