|
|
/*******************************************************************************
* * (C) COPYRIGHT MICROSOFT CORP., 1999 * * TITLE: WiaLock.Cpp * * VERSION: 1.0 * * AUTHOR: ByronC * * DATE: 15 November, 1999 * * DESCRIPTION: * Implementation of Lock Manager. * *******************************************************************************/ #include "precomp.h"
#include "stiexe.h"
//
// Include Headers
//
#include <time.h>
#include "device.h"
#include "wiamonk.h"
#include "wiapriv.h"
#include "stiusd.h"
#define DECLARE_LOCKMGR
#include "lockmgr.h"
/**************************************************************************\
* StiLockMgr * * Constructor for the lock manager. * * Arguments: * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/
StiLockMgr::StiLockMgr() { DBG_FN(StiLockMgr::StiLockMgr); m_cRef = 0; m_dwCookie = 0; m_bSched = FALSE; m_lSchedWaitTime = 0; }
/**************************************************************************\
* Initialize * * Initializes the lock manager and registers this instance in the ROT. * * Arguments: * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT StiLockMgr::Initialize() { DBG_FN(StiLockMgr::Initialize); HRESULT hr = S_OK;
//
// Artificially set ref count too high to prevent destruction from inside Release()
//
m_cRef = 2;
#ifdef USE_ROT
CWiaInstMonk *pInstMonk = new CWiaInstMonk(); CHAR szCookieName[MAX_PATH]; IUnknown *pUnk; IMoniker *pIMoniker; IBindCtx *pCtx; IRunningObjectTable *pTable;
USES_CONVERSION;
//
// Make up a cookie name. This will be stored in the registry. This
// name uniquely identifies our lock manager on the system.
//
srand( (unsigned)time( NULL ) ); sprintf(szCookieName, "%d_LockMgr_%d", rand(), rand());
//
// Get our IUnknown interface
//
hr = QueryInterface(IID_IUnknown, (VOID**) &pUnk); if (SUCCEEDED(hr)) {
//
// We need to register this object in the ROT, so that any STI clients
// will connect to this Lock Manager. This way, we maintain consistent
// device lock information amongst multiple STI and WIA clients.
//
// First create an instance Moniker. Then get a pointer to the ROT.
// Register this named moniker with our Lock Manager. Store the
// cookie so we can unregister upon destruction of the Lock Manager.
//
if (pInstMonk) { hr = pInstMonk->Initialize(A2W(szCookieName)); if (SUCCEEDED(hr)) { hr = pInstMonk->QueryInterface(IID_IMoniker, (VOID**) &pIMoniker); } } else { hr = E_OUTOFMEMORY; } }
if (SUCCEEDED(hr)) {
hr = CreateBindCtx(0, &pCtx); if (SUCCEEDED(hr)) {
hr = pCtx->GetRunningObjectTable(&pTable); if (SUCCEEDED(hr)) {
//
// Register ourselves in the ROT
//
hr = pTable->Register(ROTFLAGS_ALLOWANYCLIENT, pUnk, pIMoniker, &m_dwCookie);
ASSERT(hr == S_OK);
//
// Write the cookie name to the registry, so clients will know
// the name of our lock manager.
//
if (hr == S_OK) {
hr = WriteCookieNameToRegistry(szCookieName); } else { DBG_ERR(("StiLockMgr::Initialize, could not register Moniker")); hr = (SUCCEEDED(hr)) ? E_FAIL : hr; }
pTable->Release(); } else { DBG_ERR(("StiLockMgr::Initialize, could not get Running Object Table")); }
pCtx->Release(); } else { DBG_ERR(("StiLockMgr::Initialize, could not create bind context")); } } else { DBG_ERR(("StiLockMgr::Initialize, problem creating Moniker")); }
if (pInstMonk) { pInstMonk->Release(); } #endif
return hr; }
/**************************************************************************\
* ~StiLockMgr * * Destructor - removes instance from the ROT that was resgistered in * Initialize. * * Arguments: * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/
StiLockMgr::~StiLockMgr() { DBG_FN(StiLockMgr::~StiLockMgr); m_cRef = 0;
#ifdef USE_ROT
if (m_dwCookie) { HRESULT hr; IBindCtx *pCtx; IRunningObjectTable *pTable;
hr = CreateBindCtx(0, &pCtx); if (SUCCEEDED(hr)) { hr = pCtx->GetRunningObjectTable(&pTable); if (SUCCEEDED(hr)) { hr = pTable->Revoke(m_dwCookie); } } DeleteCookieFromRegistry();
if (FAILED(hr)) {
DBG_ERR(("StiLockMgr::~StiLockMgr, could not Unregister Moniker")); }
m_dwCookie = 0; } #endif
m_bSched = FALSE; m_lSchedWaitTime = 0; }
/**************************************************************************\
* IUnknown methods * * QueryInterface * AddRef * Release * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT _stdcall StiLockMgr::QueryInterface( const IID& iid, void** ppv) { if (iid == IID_IUnknown) { *ppv = (IUnknown*) this; } else if (iid == IID_IStiLockMgr) { *ppv = (IStiLockMgr*) this; } else { return E_NOINTERFACE; }
AddRef(); return S_OK; }
ULONG _stdcall StiLockMgr::AddRef(void) { InterlockedIncrement(&m_cRef); return m_cRef; }
ULONG _stdcall StiLockMgr::Release(void) { LONG ref;
InterlockedDecrement(&m_cRef); ref = m_cRef;
if (ref == 0) { delete this; }
return ref; }
/**************************************************************************\
* RequestLock * * Attempt to acquire a device lock. NOTE: Do not attempt this call from * inside an ACTIVE_DEVICE - it may lead to dealock. Use * RequestLock(ACTIVE_DEVICE, ...) instead. * * Arguments: * * pszDeviceName - The STI internal name of the device (same as WIA * device ID) * ulTimeout - The max. amount of time to wait for a lock * bInServerProcess- Indicates whether we're being called from within the * server's process. * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT _stdcall StiLockMgr::RequestLock(BSTR pszDeviceName, ULONG ulTimeout, BOOL bInServerProcess, DWORD dwClientThreadId) { DBG_FN(StiLockMgr::RequestLock); HRESULT hr = E_FAIL; ACTIVE_DEVICE *pDevice = NULL;
USES_CONVERSION;
//
// Get the device specified by pszDeviceName
//
pDevice = g_pDevMan->IsInList(DEV_MAN_IN_LIST_DEV_ID, pszDeviceName); if(pDevice) { hr = RequestLockHelper(pDevice, ulTimeout, bInServerProcess, dwClientThreadId);
//
// Release the device due to the AddRef made by the call to
// IsInList
//
pDevice->Release(); } else {
//
// Device not found, log error
//
DBG_ERR(("StiLockMgr::RequestLock, device name was not found")); hr = STIERR_INVALID_DEVICE_NAME; }
return hr; }
/**************************************************************************\
* RequestLock * * Attempt to acquire a device lock. This method is always called from * the server. * * Arguments: * * pDevice - The STI ACTIVE_DEVICE object * ulTimeout - The max. amount of time to wait for a lock * * Return Value: * * Status * * History: * * 12/06/1999 Original Version * \**************************************************************************/
HRESULT _stdcall StiLockMgr::RequestLock(ACTIVE_DEVICE *pDevice, ULONG ulTimeout, BOOL bOpenPort) { DBG_FN(StiLockMgr::RequestLock);
return RequestLockHelper(pDevice, ulTimeout, bOpenPort, GetCurrentThreadId()); }
/**************************************************************************\
* RequestLockHelper * * Helper used to acquire a device lock. It fills out the appropriate * lock information stored with the device. * * Arguments: * * pDevice - A pointer to the STI device * ulTimeout - The max. amount of time to wait for a lock * bInServerProcess- Indicates whether we're in the server process * * Return Value: * * Status * * History: * * 12/06/1999 Original Version * \**************************************************************************/
HRESULT StiLockMgr::RequestLockHelper(ACTIVE_DEVICE *pDevice, ULONG ulTimeout, BOOL bInServerProcess, DWORD dwClientThreadId) { DBG_FN(StiLockMgr::RequestLockHelper); HRESULT hr = S_OK; DWORD dwWait = 0; LockInfo *pLockInfo = NULL; DWORD dwCurThread = 0;
hr = CheckDeviceInfo(pDevice); if (FAILED(hr)) { return hr; }
pLockInfo = (LockInfo*) pDevice->m_pLockInfo;
//
// Check whether this is the same thread re-acquiring an active lock.
// If not, we must wait for device to become free.
//
dwCurThread = dwClientThreadId; if (InterlockedCompareExchange((LONG*)&pLockInfo->dwThreadId, dwCurThread, dwCurThread) == (LONG) dwCurThread) {
pLockInfo->lInUse++; pLockInfo->lTimeLeft = pLockInfo->lHoldingTime; } else {
dwWait = WaitForSingleObject(pLockInfo->hDeviceIsFree, ulTimeout); if (dwWait == WAIT_OBJECT_0) { //
// Check whether the driver is still loaded.
//
if (pDevice->m_DrvWrapper.IsDriverLoaded()) { //
// Update lock information
//
InterlockedExchange((LONG*) &pLockInfo->dwThreadId, dwCurThread); pLockInfo->lTimeLeft = pLockInfo->lHoldingTime; pLockInfo->lInUse++;
//
// Only ask USD to open port if we're in the server process.
//
if (bInServerProcess) { hr = LockDevice(pDevice); } else { pLockInfo->bDeviceIsLocked = TRUE; } } else { //
// Driver not loaded, so clear the lock info. This is the
// case where an application was sitting on a request to
// lock the device, but the service's control thread
// was busy unloading it. We want to stop it here, so
// we don't make the bogus call down to the driver
// which we know is not loaded.
//
ClearLockInfo(pLockInfo); hr = WIA_ERROR_OFFLINE; }
} else { DBG_ERR(("StiLockMgr::RequestLockHelper, device is busy"));
hr = WIA_ERROR_BUSY; } } return hr; }
/**************************************************************************\
* RequestUnlock * * Attempt to unlock a device. NOTE: Do not attempt this call from * inside an ACTIVE_DEVICE - it may lead to dealock. Use * RequestUnlock(ACTIVE_DEVICE, ...) instead. * * Arguments: * * pszDeviceName - The STI internal name of the device (same as WIA * device ID) * bInServerProcess- Indicates whether we're in the server process * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT _stdcall StiLockMgr::RequestUnlock(BSTR bstrDeviceName, BOOL bInServerProcess, DWORD dwClientThreadId) { DBG_FN(StiLockMgr::RequestUnlock); HRESULT hr = E_FAIL; ACTIVE_DEVICE *pDevice = NULL;
USES_CONVERSION;
//
// Get the device specified by pszDeviceName
//
pDevice = g_pDevMan->IsInList(DEV_MAN_IN_LIST_DEV_ID, bstrDeviceName); if(pDevice) {
hr = RequestUnlockHelper(pDevice, bInServerProcess, dwClientThreadId);
//
// Release the device due to the AddRef made by the call to
// IsInList
//
pDevice->Release(); } else {
//
// Device not found, log error
//
DBG_ERR(("StiLockMgr::RequestUnlock, device name was not found")); hr = STIERR_INVALID_DEVICE_NAME; }
return hr; }
/**************************************************************************\
* RequestUnlock * * Attempt to unlock a device. This method is always called from within * the server. * * Arguments: * * pDevice - The STI ACTIVE_DEVICE object * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT _stdcall StiLockMgr::RequestUnlock(ACTIVE_DEVICE *pDevice, BOOL bClosePort) { DBG_FN(StiLockMgr::RequestUnlock); return RequestUnlockHelper(pDevice, bClosePort, GetCurrentThreadId());
}
/**************************************************************************\
* RequestUnlockHelper * * Helper used to unlock a device lock. It clears the appropriate * lock information stored with the device. * * Arguments: * * pDevice - A pointer to the STI device * ulTimeout - The max. amount of time to wait for a lock * bInServerProcess- Indicates whether we're in the server process * * Return Value: * * Status * * History: * * 12/06/1999 Original Version * \**************************************************************************/
HRESULT StiLockMgr::RequestUnlockHelper(ACTIVE_DEVICE *pDevice, BOOL bInServerProcess, DWORD dwClientThreadId) { DBG_FN(StiLockMgr::RequestUnlockHelper); HRESULT hr = S_OK; LockInfo *pLockInfo = NULL; BOOL bDidNotUnlock = TRUE;
hr = CheckDeviceInfo(pDevice); if (FAILED(hr)) { DBG_ERR(("StiLockMgr::RequestUnlockHelper, CheclDeviceInfo() failed with hr=%x", hr)); return hr; }
pLockInfo = (LockInfo*) pDevice->m_pLockInfo;
//
// Special case exists if device has been marked for removal. In this
// case, we want to unlock now (definitely not schedule for later).
//
if (pDevice->QueryFlags() & STIMON_AD_FLAG_REMOVING) {
if (pLockInfo) { if (pLockInfo->bDeviceIsLocked) { UnlockDevice(pDevice); }
hr = ClearLockInfo(pLockInfo); if (FAILED(hr)) { DBG_ERR(("StiLockMgr::RequestUnlockHelper, could not clear lock information")); } } return hr; }
//
// Decrement the usage count. If usage count == 0, then reset the
// lock information, but don't actually unlock. (To improve (burst)
// performance, we will hold onto the lock for a maximum idle period
// specified by pLockInfo->lHoldingTime). Only if lHoldingTime is 0,
// do we unlock straightaway.
//
if (pLockInfo->lInUse > 0) { pLockInfo->lInUse--; }
if (pLockInfo->lInUse <= 0) {
//
// Only unlock if holding is 0 and we're in the server process.
//
if ((pLockInfo->lHoldingTime == 0) && bInServerProcess) { UnlockDevice(pDevice); bDidNotUnlock = FALSE; }
hr = ClearLockInfo(pLockInfo); if (FAILED(hr)) { DBG_ERR(("StiLockMgr::RequestUnlockHelper, failed to clear lock information")); }
//
// If we're not in the server process, nothing left to do,
// so return.
//
if (!bInServerProcess) { pLockInfo->bDeviceIsLocked = FALSE; return hr; }
}
//
// If we did not unlock the device, then schedule the unlock
// callback to unlock it later.
//
if (bDidNotUnlock) { m_lSchedWaitTime = pLockInfo->lHoldingTime;
if (!m_bSched) {
if (ScheduleWorkItem((PFN_SCHED_CALLBACK) UnlockTimedCallback, this, m_lSchedWaitTime, NULL)) { m_bSched = TRUE; } } } return hr; }
/**************************************************************************\
* CreateLockInfo * * Allocates and initializes a new LockInfo struct. * * Arguments: * * pDevice - The STI ACTIVE_DEVICE object. * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT StiLockMgr::CreateLockInfo(ACTIVE_DEVICE *pDevice) { DBG_FN(StiLockMgr::CreateLockInfo); HRESULT hr = S_OK; LockInfo *pLockInfo = NULL;
USES_CONVERSION;
//
// Allocate memory for the structure
//
pLockInfo = (LockInfo*) LocalAlloc(LPTR, sizeof(LockInfo)); if (pLockInfo) { memset(pLockInfo, 0, sizeof(LockInfo));
pLockInfo->hDeviceIsFree = CreateEvent(NULL, FALSE, TRUE, NULL); if (pLockInfo->hDeviceIsFree) {
//
// Get any relevant lock information
//
pLockInfo->lHoldingTime = pDevice->m_DrvWrapper.getLockHoldingTime(); DBG_TRC(("StiLockMgr::CreateLockInfo, Lock holding time set to %d for device %S", pLockInfo->lHoldingTime, pDevice->GetDeviceID()));
pLockInfo->lTimeLeft = pLockInfo->lHoldingTime;
//
// Everything's OK, so set the device's lock information
//
pDevice->m_pLockInfo = pLockInfo;
} else { DBG_ERR(("StiLockMgr::CreateLockInfo, could not create event")); LocalFree(pLockInfo); hr = E_FAIL; } } else { DBG_ERR(("StiLockMgr::CreateLockInfo, out of memory")); hr = E_OUTOFMEMORY; }
return hr; }
/**************************************************************************\
* ClearLockInfo * * Clears information stored in a lockinfo struct. Also signals that the * device is free. Note: it does not unlock the device. * * Arguments: * * pLockInfo - a pointer to the device's LockInfo struct. * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT StiLockMgr::ClearLockInfo(LockInfo *pLockInfo) { DBG_FN(StiLockMgr::ClearLockInfo);
//
// Note: As much lock information is reset as can be done without
// unlocking the device. This method is only called when lInuse
// is 0, indicating that the device is no longer being actively used.
//
InterlockedExchange((LONG*)&pLockInfo->dwThreadId, 0); pLockInfo->lInUse = 0;
//
// Signal the device is free.
//
if (SetEvent(pLockInfo->hDeviceIsFree)) {
return S_OK; } else { DBG_ERR(("StiLockMgr::ClearLockInfo, could not signal event")); return E_FAIL; } }
/**************************************************************************\
* LockDevice * * Calls the USD to lock itsself and updates the relevant Lock information. * * Arguments: * * pDevice - pointer to the ACTIVE_DEVICE node * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT StiLockMgr::LockDevice(ACTIVE_DEVICE *pDevice) { DBG_FN(StiLockMgr::LockDevice);
HRESULT hr = S_OK; LockInfo *pLockInfo = (LockInfo*)pDevice->m_pLockInfo; IStiUSD *pIStiUSD = NULL;
__try {
//
// Check whether device is currently locked. We know that the device
// is not busy, so it is safe to simply keep the lock open. This is how
// we improve "burst" performance.
//
if (!pLockInfo->bDeviceIsLocked) {
hr = pDevice->m_DrvWrapper.STI_LockDevice(); } else { DBG_TRC(("StiLockMgr::LockDevice, Device is already locked.")); }
if (hr == S_OK) { pLockInfo->bDeviceIsLocked = TRUE; } else { HRESULT hres = E_FAIL;
pLockInfo->bDeviceIsLocked = FALSE; hres = ClearLockInfo(pLockInfo);
DBG_ERR(("StiLockMgr::LockDevice, USD error locking the device (0x%X)", hr)); } } __except (EXCEPTION_EXECUTE_HANDLER) { DBG_ERR(("StiLockMgr::LockDevice, exception")); hr = HRESULT_FROM_WIN32(GetExceptionCode()); }
return hr; }
/**************************************************************************\
* UnlockDevice * * Calls the USD to unlock itsself and updates the relevant Lock * information. * * Arguments: * * pDevice - pointer to the ACTIVE_DEVICE node * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT StiLockMgr::UnlockDevice(ACTIVE_DEVICE *pDevice) { DBG_FN(StiLockMgr::UnlockDevice); HRESULT hr = S_OK; LockInfo *pLockInfo = (LockInfo*)pDevice->m_pLockInfo; IStiUSD *pIStiUSD = NULL;
__try {
//
// Unlock the device and mark that device has been unlocked.
//
hr = pDevice->m_DrvWrapper.STI_UnLockDevice(); if (SUCCEEDED(hr)) { pLockInfo->bDeviceIsLocked = FALSE; }
if (hr != S_OK) { pLockInfo->bDeviceIsLocked = TRUE; DBG_ERR(("StiLockMgr::UnlockDevice, USD error unlocking the device")); } } __except (EXCEPTION_EXECUTE_HANDLER) { DBG_ERR(("StiLockMgr::UnlockDevice, exception")); hr = HRESULT_FROM_WIN32(GetExceptionCode()); }
return hr; }
HRESULT StiLockMgr::CheckDeviceInfo(ACTIVE_DEVICE *pDevice) { HRESULT hr = E_FAIL;
TAKE_ACTIVE_DEVICE _tad(pDevice);
if (!pDevice) { DBG_ERR(("StiLockMgr::CheckDeviceInfo, pDevice is NULL!")); return E_POINTER; }
//
// Check whether the device is valid and is not being removed
//
if (pDevice->IsValid() && !(pDevice->QueryFlags() & STIMON_AD_FLAG_REMOVING)) {
//
// Check whether lock information for this device exists yet. If
// not, create a new LockInfo struct for this device.
//
if (pDevice->m_pLockInfo) { hr = S_OK; } else { hr = CreateLockInfo(pDevice); } } else { DBG_ERR(("StiLockMgr::CheckDeviceInfo, ACTIVE_DEVICE is not valid!")); hr = E_FAIL; }
return hr; }
#ifdef USE_ROT
/**************************************************************************\
* WriteCookieNameToRegistry * * Writes the specified name to the registry. This is so clients know what * name to bind to when trying to get this instance of the Lock Manager * from the ROT (see Initialize). * * Arguments: * * szCookieName - string containing the cookie name * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
HRESULT StiLockMgr::WriteCookieNameToRegistry(CHAR *szCookieName) { HRESULT hr; HKEY hKey; LONG lErr; DWORD dwType = REG_SZ; DWORD dwSize = strlen(szCookieName) + 1;
//
// Write Lock Manager instance name to the registry.
//
lErr = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_STICONTROL, 0, TEXT(""), REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); if (lErr == ERROR_SUCCESS) {
lErr = ::RegSetValueExA(hKey, REGSTR_VAL_LOCK_MGR_COOKIE_A, 0, dwType, (BYTE*) szCookieName, dwSize); if (lErr != ERROR_SUCCESS) { DBG_ERR(("StiLockMgr::WriteCookieNameToRegistry, could not write to registry")); } else {
return S_OK; }
RegCloseKey(hKey); } return E_FAIL; }
/**************************************************************************\
* DeleteCookieFromRegistry * * Delete the cookie name from the registry. It is only needed while this * instance of the lock manager is running. * * Arguments: * * Return Value: * * Status * * History: * * 15/1/1999 Original Version * \**************************************************************************/
VOID StiLockMgr::DeleteCookieFromRegistry() { HRESULT hr; HKEY hKey; LONG lErr;
//
// Remove Lock Manager instance name from registry.
//
lErr = ::RegOpenKeyEx(HKEY_DYN_DATA, REGSTR_PATH_STICONTROL, 0, KEY_WRITE, &hKey); if (lErr == ERROR_SUCCESS) {
lErr = ::RegDeleteValue(hKey, REGSTR_VAL_LOCK_MGR_COOKIE); RegCloseKey(hKey); } }
#endif
/**************************************************************************\
* AutoUnlock * * Scans the device list to check whether any device's idle time has * expired and needs to be unlocked. * * Arguments: * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/
VOID StiLockMgr::AutoUnlock() { EnumContext Ctx;
m_bSched = FALSE;
//
// Enumerate through device list. At each locked device, update it's
// lTimeLeft. If lTimeLeft has expired, unlock the device.
// If not, mark that unlock callback needs to be scheduled.
//
// This logic is done in the UpdateLockInfoStatus method called by the
// EnumDeviceCallback function on each device.
//
Ctx.This = this; Ctx.lShortestWaitTime = LONG_MAX; Ctx.bMustSchedule = FALSE; g_pDevMan->EnumerateActiveDevicesWithCallback(EnumDeviceCallback, &Ctx);
//
// Schedule next callback, if needed
//
if (Ctx.bMustSchedule && !m_bSched) {
m_bSched = TRUE; m_lSchedWaitTime = Ctx.lShortestWaitTime; if (ScheduleWorkItem((PFN_SCHED_CALLBACK) UnlockTimedCallback, this, m_lSchedWaitTime, NULL)) { return; } else { DBG_ERR(("StiLockMgr::AutoUnlock, failed to schedule UnlockTimedCallback")); } } }
/**************************************************************************\
* UpdateLockInfoStatus * * Updates a device's lock information. If the device's idle time has * expired, it is unlocked. If it is still busy, it's amount of idle time * left is updated. * * Arguments: * * pDevice - A pointer to the ACTIVE_DEVICE node. * pWaitTime - This is a pointer to the shortest wait time left. * This is used as the time the AutoUnlock() method * needs to re-schedule itsself. * pbMustSchedule - A pointer to a BOOL indicating whether the * AutoUnlock() needs to be re-scheduled. * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/
VOID StiLockMgr::UpdateLockInfoStatus(ACTIVE_DEVICE *pDevice, LONG *pWaitTime, BOOL *pbMustSchedule) { DBG_FN(UpdateLockInfoStatus); LockInfo *pLockInfo = NULL; DWORD dwWait; HRESULT hr = S_OK;;
//
// NOTE: Do not modify pWaitTime or pbMustSchedule unless a new wait
// time is scheduled (see where pLockInfo->lTimeLeft < *pWaitTime)
//
//
// Get a pointer to the lock information
//
if (pDevice) {
pLockInfo = (LockInfo*) pDevice->m_pLockInfo; }
if (!pLockInfo) { return; }
//
// Check whether device is free. If the device is busy, don't bother
// scheduling a callback, since it will be rescheduled if needed when
// the call to RequestUnlock is made.
//
dwWait = WaitForSingleObject(pLockInfo->hDeviceIsFree, 0); if (dwWait == WAIT_OBJECT_0) {
//
// Check whether device is locked (we're only interested in devices
// that are locked).
//
if (pLockInfo->bDeviceIsLocked) {
//
// Decrease the amount of time left. If lTimeLeft <= 0, then no
// idle time remains and device should be unlocked.
//
// If time does remain, check whether it is smaller than
// the current wait time (pWaitTime). If it is smaller, mark
// that this is the new wait time, and that the unlock callback
// must be scheduled to unlock this later.
//
pLockInfo->lTimeLeft -= m_lSchedWaitTime; if (pLockInfo->lTimeLeft <= 0) {
pLockInfo->lTimeLeft = 0;
hr = UnlockDevice(pDevice); if (SUCCEEDED(hr)) {
hr = ClearLockInfo(pLockInfo); return; } } else {
if (pLockInfo->lTimeLeft < *pWaitTime) {
*pWaitTime = pLockInfo->lTimeLeft; } *pbMustSchedule = TRUE; } }
//
// We are finished updating the information, so re-signal
// that device is free.
//
SetEvent(pLockInfo->hDeviceIsFree);
} }
/**************************************************************************\
* EnumDeviceCallback * * This function is called once for every device in the ACTIVE_DEVICE * enumeration. * * Arguments: * * pDevice - A pointer to the ACTIVE_DEVICE node. * pContext - This is a pointer to an enumeration context. * The context contains a pointer to the Lock Manager, * the shortest wait time, and a bool indicating whether * AutoUnlock needs to be re-scheduled. * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/
VOID WINAPI EnumDeviceCallback(ACTIVE_DEVICE *pDevice, VOID *pContext) { DBG_FN(EnumDeviceCallback); EnumContext *pCtx = (EnumContext*) pContext;
if (pCtx) {
//
// Update the lock status on this device
//
pCtx->This->UpdateLockInfoStatus(pDevice, &pCtx->lShortestWaitTime, &pCtx->bMustSchedule); } }
/**************************************************************************\
* UnlockTimedCallback * * This function gets called when a lock is still active and may need to * be unlocked if the amount of idle time expires. * * Arguments: * * pArg - A pointer to the Lock Manager. * * Return Value: * * History: * * 15/1/1999 Original Version * \**************************************************************************/
VOID WINAPI UnlockTimedCallback(VOID *pArg) { DBG_FN(UnlockTimedCallback); StiLockMgr *pLockMgr = (StiLockMgr*) pArg;
if (pLockMgr) {
__try { pLockMgr->AutoUnlock(); } __except (EXCEPTION_EXECUTE_HANDLER) { #ifdef DEBUG
OutputDebugStringA("Exception in UnlockTimedCallback"); #endif
} } }
|