/*++ Copyright (c) 1992 Microsoft Corporation Module Name: timeract.c Abstract: Provides the timer activity functions. Author: Sunita Shrivastava (sunitas) 10-Nov-1995 Revision History: --*/ #include "service.h" #include "lmp.h" //global data static LIST_ENTRY gActivityHead; static HANDLE ghTimerCtrlEvent=NULL; static HANDLE ghTimerCtrlDoneEvent = NULL; static HANDLE ghTimerThread=NULL; static CRITICAL_SECTION gActivityCritSec; static HANDLE grghWaitHandles[MAX_TIMER_ACTIVITIES]; static PTIMER_ACTIVITY grgpActivity[MAX_TIMER_ACTIVITIES]; static DWORD gdwNumHandles; static DWORD gdwTimerCtrl; //internal prototypes DWORD WINAPI ClTimerThread(PVOID pContext); void ReSyncTimerHandles(); /**** @doc EXTERNAL INTERFACES CLUSSVC LM ****/ /**** @func DWORD | TimerActInitialize| It initializes structures for log file management and creates a timer thread to process timer activities. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened. @comm This function is called when the cluster components are initialized. @xref ****/ DWORD TimerActInitialize() { DWORD dwError = ERROR_SUCCESS; DWORD dwThreadId; //we need to create a thread to general log management //later this may be used by other clussvc client components ClRtlLogPrint(LOG_NOISE, "[LM] TimerActInitialize Entry. \r\n"); InitializeCriticalSection(&gActivityCritSec); //initialize the activity structures //when a log file is created, an activity structure //will be added to this list InitializeListHead(&gActivityHead); //create an auto-reset event to signal changes to the timer list ghTimerCtrlEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (!ghTimerCtrlEvent) { dwError = GetLastError(); CL_LOGFAILURE(dwError); goto FnExit; } //create a manual reset event for the timer thread to signal //when it is done syncing the activitity list ghTimerCtrlDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!ghTimerCtrlDoneEvent) { dwError = GetLastError(); CL_LOGFAILURE(dwError); goto FnExit; } gdwNumHandles = 1; grghWaitHandles[0] = ghTimerCtrlEvent; //create a thread to do the periodic management ghTimerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ClTimerThread, NULL, 0, &dwThreadId); if (!ghTimerThread) { dwError = GetLastError(); CL_LOGFAILURE(dwError); } FnExit: if (dwError != ERROR_SUCCESS) { //free up resources if (ghTimerCtrlEvent) { CloseHandle(ghTimerCtrlEvent); ghTimerCtrlEvent = NULL; } //free up resources if (ghTimerCtrlDoneEvent) { CloseHandle(ghTimerCtrlDoneEvent); ghTimerCtrlDoneEvent = NULL; } DeleteCriticalSection(&gActivityCritSec); } return(dwError); } /**** @func DWORD | ClTimerThread | This thread does a wait on all the waitable timers registered within the cluster service. @parm PVOID | pContext | Supplies the identifier of the log. @comm When any of the timers is signaled, it calls the activity callback function corresponding to that timer. When the timer control event is signaled, it either resyncs its wait handles or shuts down. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened. @xref ****/ DWORD WINAPI ClTimerThread(PVOID pContext) { HANDLE hClTimer; DWORD dwReturn; while (TRUE) { dwReturn = WaitForMultipleObjects(gdwNumHandles, grghWaitHandles, FALSE, INFINITE); //walk the activity list if (dwReturn == WAIT_FAILED) { //run down the activity lists and call the functions ClRtlLogPrint(LOG_UNUSUAL, "[LM] ClTimerThread: WaitformultipleObjects failed 0x%1!08lx!\r\n", GetLastError()); } else if (dwReturn == 0) { //the first handle is the timer ctrl event if (gdwTimerCtrl == TIMER_ACTIVITY_SHUTDOWN) { ExitThread(0); } else if (gdwTimerCtrl == TIMER_ACTIVITY_CHANGE) { ReSyncTimerHandles(); } } else { // SS::we got rid of holding the critsec by using the manual // reset event. if (dwReturn < gdwNumHandles) { //if the activity has been set up for delete, we cant rely //on the context and callback being there! if (grgpActivity[dwReturn]->dwState == ACTIVITY_STATE_READY) { //call the corresponding activity fn (*((grgpActivity[dwReturn])->pfnTimerCb)) ((grgpActivity[dwReturn])->hWaitableTimer, (grgpActivity[dwReturn])->pContext); } } } } return(0); } /**** @func DWORD | ReSyncTimerHandles | resyncs the wait handles, when the activity list changes. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened. @comm This function is called by the timer thread to resync its wait handles according to the timer activities currently registered. @xref ****/ void ReSyncTimerHandles() { PLIST_ENTRY pListEntry; PTIMER_ACTIVITY pActivity; int i = 1; ClRtlLogPrint(LOG_NOISE, "[LM] ReSyncTimerHandles Entry. \r\n"); EnterCriticalSection(&gActivityCritSec); pListEntry = gActivityHead.Flink; gdwNumHandles = 1; //will resync the list of waitable timers and activities //depending on the activity list while ((pListEntry != &gActivityHead) && (i< MAX_TIMER_ACTIVITIES)) { pActivity = CONTAINING_RECORD(pListEntry, TIMER_ACTIVITY, ListEntry); //goto the next link pListEntry = pListEntry->Flink; if (pActivity->dwState == ACTIVITY_STATE_DELETE) { ClRtlLogPrint(LOG_NOISE, "[LM] ResyncTimerHandles: removed Timer 0x%1!08lx!\r\n", pActivity->hWaitableTimer); RemoveEntryList(&pActivity->ListEntry); //close the timer handle here CloseHandle(pActivity->hWaitableTimer); LocalFree(pActivity); continue; } //call the fn grghWaitHandles[i] = pActivity->hWaitableTimer; grgpActivity[i] = pActivity; gdwNumHandles++; i++; } LeaveCriticalSection(&gActivityCritSec); //now if timer activities were resynced, we need to //signal all threads that might be waiting on this SetEvent(ghTimerCtrlDoneEvent); ClRtlLogPrint(LOG_NOISE, "[LM] ReSyncTimerHandles Exit gdwNumHandles=%1!u!\r\n", gdwNumHandles); } /**** @func DWORD | AddTimerActivity | Adds a periodic Activity to the timer callback list. @parm HANDLE | hTimer | A handle to a waitaible timer object. @parm DWORD | dwInterval | The duration for this timer, in msecs. @parm LONG | lPeriod | If lPeriod is 0, the timer is signalled once if greater than 0, the timer is periodic. If less than zero, then error will be returned. @parm PFN_TIMER_CALLBACK | pfnTimerCb | A pointer to the callback function that will be called when this timer is signaled. @parm PVOID | pContext | A pointer to the callback data that will be passed to the callback function. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened. @comm SetWaitableTimer() for the corresponding timer is called by this function for the given duration. CreateWaitableTimer() must be used to create this timer handle. @xref ****/ DWORD AddTimerActivity(IN HANDLE hTimer, IN DWORD dwInterval, IN LONG lPeriod, IN PFN_TIMER_CALLBACK pfnTimerCb, IN PVOID pContext) { PTIMER_ACTIVITY pActivity = NULL; DWORD dwError = ERROR_SUCCESS; LARGE_INTEGER Interval; ClRtlLogPrint(LOG_NOISE, "[LM] AddTimerActivity: hTimer = 0x%1!08lx! pfnTimerCb=0x%2!08lx! dwInterval(in msec)=%3!u!\r\n", hTimer, pfnTimerCb, dwInterval); pActivity =(PTIMER_ACTIVITY) LocalAlloc(LMEM_FIXED,sizeof(TIMER_ACTIVITY)); if (!pActivity) { dwError = GetLastError(); CL_UNEXPECTED_ERROR(dwError); goto FnExit; } Interval.QuadPart = -10 * 1000 * (_int64)dwInterval; //time in 100 nano secs ClRtlLogPrint(LOG_NOISE, "[LM] AddTimerActivity: Interval(high)=0x%1!08lx! Interval(low)=0x%2!08lx!\r\n", Interval.HighPart, Interval.LowPart); pActivity->hWaitableTimer = hTimer; memcpy(&(pActivity->Interval), (LPBYTE)&Interval, sizeof(LARGE_INTEGER)); pActivity->pfnTimerCb = pfnTimerCb; pActivity->pContext = pContext; //set the timer if (lPeriod) { lPeriod = (LONG)dwInterval; } else { lPeriod = 0; } if (!SetWaitableTimer(hTimer, &Interval, lPeriod , NULL, NULL, FALSE)) { CL_LOGFAILURE((dwError = GetLastError())); goto FnExit; }; //add to the list of activities //and get the timer thread to resync EnterCriticalSection(&gActivityCritSec); pActivity->dwState = ACTIVITY_STATE_READY; InitializeListHead(&pActivity->ListEntry); InsertTailList(&gActivityHead, &pActivity->ListEntry); gdwTimerCtrl = TIMER_ACTIVITY_CHANGE; LeaveCriticalSection(&gActivityCritSec); SetEvent(ghTimerCtrlEvent); FnExit: if ( (dwError != ERROR_SUCCESS) && pActivity ) { LocalFree(pActivity); } ClRtlLogPrint(LOG_NOISE, "[LM] AddTimerActivity: returns 0x%1!08lx!\r\n", dwError); return(dwError); } /**** @func DWORD | RemoveTimerActivity | This functions removes the activity associated with a timer from the timer threads activity list. @parm HANDLE | hTimer | The handle to the timer whose related activity will be removed. The handle is closed. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened. @comm This function cancels the waitable timer and removes the activity corresponding to it. The calling component must not close the handle to the timer. It is closed by the timer activity manager once this function is called. @xref ****/ DWORD RemoveTimerActivity(HANDLE hTimer) { PLIST_ENTRY pListEntry; PTIMER_ACTIVITY pActivity; PTIMER_ACTIVITY pActivityToDel = NULL; DWORD dwError = ERROR_SUCCESS; ClRtlLogPrint(LOG_NOISE, "[LM] LmRemoveTimerActivity: Entry 0x%1!08lx!\r\n", hTimer); EnterCriticalSection(&gActivityCritSec); pListEntry = gActivityHead.Flink; while (pListEntry != &gActivityHead) { pActivity = CONTAINING_RECORD(pListEntry, TIMER_ACTIVITY, ListEntry); if (pActivity->hWaitableTimer == hTimer) { pActivityToDel = pActivity; break; } pListEntry = pListEntry->Flink; } if (!pActivityToDel) { ClRtlLogPrint(LOG_UNUSUAL, "[LM] LmRemoveTimerActivity: didnt find activity correspondint to 0x%1!08lx!\r\n", hTimer); } else { //will be deleted by resynctimerhandles CancelWaitableTimer(pActivityToDel->hWaitableTimer); pActivityToDel->dwState = ACTIVITY_STATE_DELETE; } //signal the timer thread to resync its array of wait handles //from this list SetEvent(ghTimerCtrlEvent); //do a manual reset on the done event so that we will wait on it //until the timer thread has done a resync of its array of //wait handles from the list after this thread leaves the critsec //note that we do this holding the critsec //now we are guaranteed that timer thread will wake us up ResetEvent(ghTimerCtrlDoneEvent); LeaveCriticalSection(&gActivityCritSec); //wait till the timer thread signals that done event WaitForSingleObject(ghTimerCtrlDoneEvent, INFINITE); ClRtlLogPrint(LOG_NOISE, "[LM] LmRemoveTimerActivity: Exit\r\n"); return(dwError); } /**** @func DWORD | PauseTimerActivity | This functions pauses the activity associated with a timer in the timer threads activity list. @parm HANDLE | hTimer | The handle to the timer whose related activity will be removed. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened. @comm This function sets the timer into a paused state so that the timer callbacks are not proccessed. @xref hWaitableTimer == hTimer) { pActivityToDel = pActivity; break; } pListEntry = pListEntry->Flink; } if (!pActivityToDel) { ClRtlLogPrint(LOG_UNUSUAL, "[LM] PauseTimerActivity: didnt find activity correspondint to 0x%1!08lx!\r\n", hTimer); } else { CL_ASSERT(pActivity->dwState == ACTIVITY_STATE_READY); //set the state to be paused pActivityToDel->dwState = ACTIVITY_STATE_PAUSED; } //signal the timer thread to resync its array of wait handles //from this list SetEvent(ghTimerCtrlEvent); //do a manual reset on the done event so that we will wait on it //until the timer thread has done a resync of its array of //wait handles from the list after this thread leaves the critsec //note that we do this holding the critsec //now we are guaranteed that timer thread will wake us up ResetEvent(ghTimerCtrlDoneEvent); LeaveCriticalSection(&gActivityCritSec); //wait till the timer thread signals that done event WaitForSingleObject(ghTimerCtrlDoneEvent, INFINITE); ClRtlLogPrint(LOG_NOISE, "[LM] PauseTimerActivity: Exit\r\n"); return(dwError); } /**** @func DWORD | UnpauseTimerActivity | This functions unpauses the activity associated with a timer in the timer threads activity list. @parm HANDLE | hTimer | The handle to the timer whose related activity will be removed. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened. @comm This function sets the activity into a ready state. @xref hWaitableTimer == hTimer) { pActivityToDel = pActivity; break; } pListEntry = pListEntry->Flink; } if (!pActivityToDel) { ClRtlLogPrint(LOG_UNUSUAL, "[LM] PauseTimerActivity: didnt find activity correspondint to 0x%1!08lx!\r\n", hTimer); } else { CL_ASSERT(pActivity->dwState == ACTIVITY_STATE_PAUSED); //set the state to be paused pActivityToDel->dwState = ACTIVITY_STATE_READY; } LeaveCriticalSection(&gActivityCritSec); ClRtlLogPrint(LOG_NOISE, "[LM] UnpauseTimerActivity: Exit\r\n"); return(dwError); } /**** @func DWORD | TimerActShutdown | Deinitializes the TimerActivity manager. @rdesc ERROR_SUCCESS if successful. Win32 error code if something horrible happened. @comm This function notifies the timer thread to shutdown down and closes all resources associated with timer activity management. @xref ****/ DWORD TimerActShutdown( ) { PLIST_ENTRY pListEntry; PTIMER_ACTIVITY pActivity; ClRtlLogPrint(LOG_NOISE, "[LM] TimerActShutDown : Entry \r\n"); //check if we were initialized before if (ghTimerThread && ghTimerCtrlEvent) { //signal the timer thread to kill itself gdwTimerCtrl = TIMER_ACTIVITY_SHUTDOWN; SetEvent(ghTimerCtrlEvent); //wait for the thread to exit WaitForSingleObject(ghTimerThread,INFINITE); //close the timer thread control event CloseHandle(ghTimerCtrlEvent); ghTimerCtrlEvent = NULL; //close the timer thread control done event CloseHandle(ghTimerCtrlDoneEvent); ghTimerCtrlDoneEvent = NULL; CloseHandle(ghTimerThread); ghTimerThread = NULL; //clean up the activity structures, if there any left pListEntry = gActivityHead.Flink; while (pListEntry != &gActivityHead) { pActivity = CONTAINING_RECORD(pListEntry, TIMER_ACTIVITY, ListEntry); CloseHandle(pActivity->hWaitableTimer); LocalFree(pActivity); pListEntry = pListEntry->Flink; } //reset the activity head structure InitializeListHead(&gActivityHead); DeleteCriticalSection(&gActivityCritSec); } ClRtlLogPrint(LOG_NOISE, "[LM] TimerActShutDown : Exit\r\n"); return(ERROR_SUCCESS); }