Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1570 lines
46 KiB

  1. /*++
  2. Copyright (c) 1995-1997 Microsoft Corporation
  3. Module Name:
  4. reslist.c
  5. Abstract:
  6. Implements the management of the resource list. This includes
  7. adding resources to the list and deleting them from the list.
  8. Author:
  9. John Vert (jvert) 1-Dec-1995
  10. Revision History:
  11. Sivaprasad Padisetty (sivapad) 06-18-1997 Added the COM support
  12. --*/
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include "resmonp.h"
  17. #include "stdio.h" //RNGBUG - remove all of these in all .c files
  18. #define RESMON_MODULE RESMON_MODULE_RESLIST
  19. DWORD RmpLogLevel = LOG_ERROR;
  20. //
  21. // Function prototypes local to this module
  22. //
  23. BOOL
  24. RmpChkdskNotRunning(
  25. IN PRESOURCE Resource
  26. );
  27. DWORD
  28. RmpSetResourceStatus(
  29. IN RESOURCE_HANDLE ResourceHandle,
  30. IN PRESOURCE_STATUS ResourceStatus
  31. )
  32. /*++
  33. Routine Description:
  34. Update the status of a resource.
  35. Arguments:
  36. ResourceHandle - Handle (pointer to) the resource to update.
  37. ResourceStatus - Pointer to a resource status structure for update.
  38. Returns:
  39. ResourceExitStateContinue - if the thread does not have to terminate.
  40. ResourceExitStateTerminate - if the thread must terminat now.
  41. --*/
  42. {
  43. BOOL bSuccess;
  44. DWORD status;
  45. PRESOURCE resource = (PRESOURCE)ResourceHandle;
  46. DWORD retryCount = ( resource->PendingTimeout/100 >= 600 ) ?
  47. ( resource->PendingTimeout/100 - 400 ):200;
  48. PPOLL_EVENT_LIST eventList;
  49. HANDLE OnlineEvent;
  50. //
  51. // Check if we're only updating the checkpoint value. If so, then
  52. // don't use any locks.
  53. //
  54. if ( ResourceStatus->ResourceState >= ClusterResourcePending ) {
  55. resource->CheckPoint = ResourceStatus->CheckPoint;
  56. return ResourceExitStateContinue;
  57. }
  58. //
  59. // Acquire the lock first to prevent race conditions if the resource
  60. // DLL manages to set resource status from a different thread before
  61. // returning PENDING from its online/offline entrypoint.
  62. //
  63. eventList = (PPOLL_EVENT_LIST) resource->EventList;
  64. status = TryEnterCriticalSection( &eventList->ListLock );
  65. while ( !status &&
  66. retryCount-- ) {
  67. //
  68. // Chittur Subbaraman (chitturs) - 10/18/99
  69. //
  70. // Comment out this unprotected check. The same check is done
  71. // protected further downstream. Unprotected checking could
  72. // cause a resource to attempt to enter here even before
  73. // the timer event has been created by s_RmOnlineResource or
  74. // s_RmOfflineResource and in such a case the resource will
  75. // not be able to set the resource status. This will cause
  76. // the resmon to wrongly time out the resource.
  77. //
  78. #if 0
  79. //
  80. // Check if the resource is shutting down as we're waiting.
  81. //
  82. if ( resource->TimerEvent == NULL ) {
  83. ClRtlLogPrint(LOG_UNUSUAL,
  84. "[RM] Resource (%1!ws!) TimerEvent became NULL, state (%2!d!)!\n",
  85. resource->ResourceName,
  86. resource->State);
  87. if ( resource->OnlineEvent ) {
  88. CloseHandle( resource->OnlineEvent );
  89. resource->OnlineEvent = NULL;
  90. }
  91. return ResourceExitStateTerminate;
  92. }
  93. #endif
  94. //
  95. // Chittur Subbaraman (chitturs) - 12/8/99
  96. //
  97. // Check if the "Terminate" or "Close" entry point has been
  98. // called for this resource. If so, then no need to set the
  99. // resource status. Moreover, those entry points could be
  100. // blocked waiting for the pending thread to terminate and
  101. // if the pending thread is stuck looping here waiting for
  102. // the lock held by the former thread, we are in a deadlock-like
  103. // situation. [Note that the fact that you are at this point
  104. // means that it is not the "Terminate"/"Close" itself calling
  105. // this function since the eventlist lock can be obtained since
  106. // it was obtained by resmon prior to calling the resdll entry.]
  107. //
  108. if( ( resource->dwEntryPoint ) &
  109. ( RESDLL_ENTRY_TERMINATE | RESDLL_ENTRY_CLOSE ) )
  110. {
  111. ClRtlLogPrint(LOG_UNUSUAL,
  112. "[RM] RmpSetResourceStatus: Resource <%1!ws!> not setting status since "
  113. "%2!ws! is called, lock owner=0x%3!x!, resource=%4!ws!, state=%5!u!...\n",
  114. resource->ResourceName,
  115. (resource->dwEntryPoint == RESDLL_ENTRY_TERMINATE) ?
  116. L"Terminate":L"Close",
  117. eventList->ListLock.OwningThread,
  118. (eventList->LockOwnerResource != NULL) ? eventList->LockOwnerResource->ResourceName:L"Unknown resource",
  119. eventList->MonitorState);
  120. if ( resource->OnlineEvent ) {
  121. CloseHandle( resource->OnlineEvent );
  122. resource->OnlineEvent = NULL;
  123. }
  124. return ResourceExitStateTerminate;
  125. }
  126. Sleep(100); // Delay a little
  127. status = TryEnterCriticalSection( &eventList->ListLock );
  128. }
  129. //
  130. // If we couldn't proceed, we're stuck. Just return now.
  131. //
  132. if ( !status ) {
  133. //
  134. // We're unsynchronized, but clean up a bit.
  135. //
  136. if ( resource->OnlineEvent ) {
  137. CloseHandle( resource->OnlineEvent );
  138. resource->OnlineEvent = NULL;
  139. }
  140. ClRtlLogPrint(LOG_UNUSUAL,
  141. "[RM] Resource (%1!ws!) Failed TryEnterCriticalSection after too many "
  142. "tries, state=%2!d!, lock owner=%3!x!, resource=%4!ws!, state=%5!u!\n",
  143. resource->ResourceName,
  144. resource->State,
  145. eventList->ListLock.OwningThread,
  146. (eventList->LockOwnerResource != NULL) ? eventList->LockOwnerResource->ResourceName:L"Unknown resource",
  147. eventList->MonitorState);
  148. //
  149. // SS: Why do we let the resource continue ?
  150. //
  151. return ResourceExitStateContinue;
  152. }
  153. //
  154. // SS: If the timer thread has timed us out, there is no
  155. // point in continuing.
  156. //
  157. // First check if the resource is shutting down.
  158. //
  159. if ( resource->TimerEvent == NULL ) {
  160. //
  161. // Just return asking the resource dll to terminate, but clean
  162. // up a bit.
  163. //
  164. if ( resource->OnlineEvent ) {
  165. CloseHandle( resource->OnlineEvent );
  166. resource->OnlineEvent = NULL;
  167. }
  168. ReleaseEventListLock( eventList );
  169. ClRtlLogPrint(LOG_UNUSUAL,
  170. "[RM] Timer Event is NULL when resource (%1!ws!) tries to set state=%2!d! !\n",
  171. resource->ResourceName,
  172. resource->State);
  173. return ResourceExitStateTerminate;
  174. }
  175. //
  176. // Synchronize with the online thread.
  177. //
  178. if ( resource->OnlineEvent != NULL ) {
  179. OnlineEvent = resource->OnlineEvent;
  180. resource->OnlineEvent = NULL;
  181. ReleaseEventListLock( eventList );
  182. WaitForSingleObject( OnlineEvent, INFINITE );
  183. AcquireEventListLock( eventList );
  184. CloseHandle( OnlineEvent );
  185. }
  186. //
  187. // If the state of the resource is not pending, then return immediately
  188. //
  189. if ( resource->State < ClusterResourcePending ) {
  190. ClRtlLogPrint(LOG_UNUSUAL,
  191. "[RM] Resource (%1!ws!) attempted to set status while state was not pending (%2!d!)!\n",
  192. resource->ResourceName,
  193. resource->State);
  194. CL_LOGFAILURE(ERROR_INVALID_SERVER_STATE);
  195. ReleaseEventListLock( eventList );
  196. return ResourceExitStateContinue;
  197. }
  198. resource->State = ResourceStatus->ResourceState;
  199. // resource->WaitHint = ResourceStatus->WaitHint;
  200. resource->CheckPoint = ResourceStatus->CheckPoint;
  201. //
  202. // If the state has stabilized, stop the timer thread.
  203. //
  204. if ( resource->State < ClusterResourcePending ) {
  205. //
  206. // Add any events to our eventlist if the resource is reporting its state as online.
  207. //
  208. if ( ResourceStatus->EventHandle ) {
  209. if ( resource->State == ClusterResourceOnline ) {
  210. if ( (ULONG_PTR)ResourceStatus->EventHandle > 0x2000 ) {
  211. ClRtlLogPrint(LOG_UNUSUAL,
  212. "[RM] SetResourceStatus: Resource <%1!ws!> attempted to set a bogus event %2!lx!.\n",
  213. resource->ResourceName,
  214. ResourceStatus->EventHandle );
  215. } else {
  216. status = RmpAddPollEvent( eventList,
  217. ResourceStatus->EventHandle,
  218. resource );
  219. if ( status != ERROR_SUCCESS ) {
  220. resource->State = ClusterResourceFailed;
  221. ClRtlLogPrint( LOG_UNUSUAL, "[RM] ResourceStatus, failed to add event to list.\n");
  222. }
  223. //
  224. // Signal poller that event list changed.
  225. //
  226. if ( status == ERROR_SUCCESS ) {
  227. RmpSignalPoller( eventList );
  228. }
  229. }
  230. } else {
  231. ClRtlLogPrint(LOG_ERROR,
  232. "[RM] RmpSetResourceStatus: Resource '%1!ws!' supplies event handle 0x%2!08lx! while reporting state %3!u!...\n",
  233. resource->ResourceName,
  234. ResourceStatus->EventHandle,
  235. resource->State );
  236. }
  237. }
  238. //
  239. // The event may have been closed by the timer thread
  240. // if this is happening too late, ignore the error.
  241. //
  242. if( resource->TimerEvent != NULL )
  243. {
  244. bSuccess = SetEvent( resource->TimerEvent );
  245. if ( !bSuccess )
  246. ClRtlLogPrint(LOG_UNUSUAL,
  247. "[RM] RmpSetResourceStatus, Error %1!u! calling SetEvent to wake timer thread\n",
  248. GetLastError());
  249. }
  250. //
  251. // Chittur Subbaraman (chitturs) - 1/12/99
  252. //
  253. // Post a notification to the cluster service regarding a state
  254. // change AFTER sending a signal to a timer. This will reduce
  255. // the probability of the cluster service sending in another
  256. // request before the timer thread had a chance to close out
  257. // the event handle.
  258. //
  259. ClRtlLogPrint(LOG_NOISE,
  260. "[RM] RmpSetResourceStatus, Posting state %2!u! notification for resource <%1!ws!>\n",
  261. resource->ResourceName,
  262. resource->State);
  263. RmpPostNotify( resource, NotifyResourceStateChange );
  264. }
  265. ReleaseEventListLock( eventList );
  266. return ResourceExitStateContinue;
  267. } // RmpSetResourceStatus
  268. VOID
  269. RmpLogEvent(
  270. IN RESOURCE_HANDLE ResourceHandle,
  271. IN LOG_LEVEL LogLevel,
  272. IN LPCWSTR FormatString,
  273. ...
  274. )
  275. /*++
  276. Routine Description:
  277. Log an event for the given resource.
  278. Arguments:
  279. ResourceHandle - Handle (pointer to) the resource to update.
  280. LogLevel - Supplies the level of this log event.
  281. FormatString - Supplies a format string for this log message.
  282. Returns:
  283. None.
  284. --*/
  285. {
  286. LPWSTR headerBuffer;
  287. LPWSTR messageBuffer;
  288. DWORD bufferLength;
  289. PRESOURCE resource = (PRESOURCE)ResourceHandle;
  290. PVOID argArray[2];
  291. HKEY resKey;
  292. DWORD status;
  293. DWORD valueType;
  294. #ifdef SLOW_RMP_LOG_EVENT
  295. WCHAR resourceName[128];
  296. #endif
  297. va_list argList;
  298. ULONG rtlLogLevel;
  299. //
  300. // map resmon log levels to those used by ClRtlLogPrint
  301. //
  302. switch ( LogLevel ) {
  303. case LOG_INFORMATION:
  304. rtlLogLevel = LOG_NOISE;
  305. break;
  306. case LOG_WARNING:
  307. rtlLogLevel = LOG_UNUSUAL;
  308. break;
  309. case LOG_ERROR:
  310. case LOG_SEVERE:
  311. default:
  312. rtlLogLevel = LOG_CRITICAL;
  313. }
  314. if ( (resource == NULL) ||
  315. (resource->Signature != RESOURCE_SIGNATURE) ) {
  316. LPWSTR resourcePrefix = (LPWSTR)ResourceHandle;
  317. //
  318. // Some resource DLLs have threads that do some
  319. // work on behalf of this resource DLL, but has no
  320. // relation to a particular resource. Thus they cannot
  321. // provide a resource handle, necessary to log an event.
  322. //
  323. // The following hack allows them to supply a string
  324. // to be printed before the message.
  325. //
  326. // This string should start with unicode 'r' and 't'
  327. // characters. "rt" is interpreted as a signature and is not printed.
  328. //
  329. if (resourcePrefix &&
  330. resourcePrefix[0] == L'r' &&
  331. resourcePrefix[1] == L't')
  332. {
  333. resourcePrefix += 2; // skip the signature
  334. } else {
  335. resourcePrefix = L"<Unknown Resource>";
  336. //CL_LOGFAILURE((DWORD)resource);
  337. }
  338. va_start( argList, FormatString );
  339. //
  340. // Print out the actual message
  341. //
  342. if ( bufferLength = FormatMessageW(FORMAT_MESSAGE_FROM_STRING |
  343. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  344. FormatString,
  345. 0,
  346. 0,
  347. (LPWSTR)&messageBuffer,
  348. 0,
  349. &argList) )
  350. {
  351. ClRtlLogPrint( rtlLogLevel, "%1!ws!: %2!ws!", resourcePrefix, messageBuffer);
  352. LocalFree(messageBuffer);
  353. }
  354. va_end( argList );
  355. return;
  356. }
  357. //CL_ASSERT(resource->Signature == RESOURCE_SIGNATURE);
  358. #ifdef SLOW_RMP_LOG_EVENT
  359. status = ClusterRegOpenKey( RmpResourcesKey,
  360. resource->ResourceId,
  361. KEY_READ,
  362. &resKey );
  363. if ( status != ERROR_SUCCESS ) {
  364. return;
  365. }
  366. bufferLength = 128;
  367. status = ClusterRegQueryValue( resKey,
  368. CLUSREG_NAME_RES_NAME,
  369. &valueType,
  370. (LPBYTE)&resourceName,
  371. &bufferLength );
  372. ClusterRegCloseKey( resKey );
  373. if ( status != ERROR_SUCCESS ) {
  374. return;
  375. }
  376. #endif
  377. //
  378. // Print out the prefix string
  379. //
  380. argArray[0] = resource->ResourceType;
  381. #ifdef SLOW_RMP_LOG_EVENT
  382. argArray[1] = resourceName;
  383. #else
  384. argArray[1] = resource->ResourceName;
  385. #endif
  386. if ( bufferLength = FormatMessageW(FORMAT_MESSAGE_FROM_STRING |
  387. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  388. FORMAT_MESSAGE_ARGUMENT_ARRAY,
  389. L"%1!ws! <%2!ws!>: ",
  390. 0,
  391. 0,
  392. (LPWSTR)&headerBuffer,
  393. 0,
  394. (va_list*)&argArray) ) {
  395. } else {
  396. return;
  397. }
  398. va_start( argList, FormatString );
  399. //
  400. // Print out the actual message
  401. //
  402. if ( bufferLength = FormatMessageW(FORMAT_MESSAGE_FROM_STRING |
  403. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  404. FormatString,
  405. 0,
  406. 0,
  407. (LPWSTR)&messageBuffer,
  408. 0,
  409. &argList) )
  410. {
  411. ClRtlLogPrint( rtlLogLevel, "%1!ws!%2!ws!", headerBuffer, messageBuffer);
  412. LocalFree(messageBuffer);
  413. }
  414. LocalFree(headerBuffer);
  415. va_end( argList );
  416. } // RmpLogEvent
  417. VOID
  418. RmpLostQuorumResource(
  419. IN RESOURCE_HANDLE ResourceHandle
  420. )
  421. /*++
  422. Routine Description:
  423. Stop the cluster service... since we lost our quorum resource.
  424. Arguments:
  425. ResourceHandle - Handle (pointer to) the resource to update.
  426. Returns:
  427. None.
  428. --*/
  429. {
  430. PRESOURCE resource = (PRESOURCE)ResourceHandle;
  431. //
  432. // Kill the cluster service alone. Take no action for this process since the main
  433. // thread in resmon.c would detect the termination of the cluster service process
  434. // and cleanly shut down hosted resources and the process itself.
  435. //
  436. TerminateProcess( RmpClusterProcess, 1 );
  437. ClRtlLogPrint( LOG_CRITICAL, "[RM] LostQuorumResource, cluster service terminated...\n");
  438. return;
  439. } // RmpLostQuorumResource
  440. BOOL
  441. RmpChkdskNotRunning(
  442. IN PRESOURCE Resource
  443. )
  444. /*++
  445. Routine Description:
  446. If this is a storage class resource, make sure CHKDSK is not running.
  447. Arguments:
  448. Resource - A pointer to the resource to check.
  449. Returns:
  450. TRUE - if this is not a STORAGE resource or CHKDSK is not running.
  451. FALSE - if this is a STORAGE resource AND CHKDSK is running.
  452. --*/
  453. {
  454. PSYSTEM_PROCESS_INFORMATION processInfo;
  455. NTSTATUS ntStatus;
  456. DWORD status;
  457. DWORD size = 4096;
  458. ANSI_STRING pname;
  459. PCHAR commonBuffer = NULL;
  460. PCHAR ptr;
  461. DWORD totalOffset = 0;
  462. CLUS_RESOURCE_CLASS_INFO resClassInfo;
  463. DWORD bytesReturned;
  464. #if 1
  465. //
  466. // Get the class of resource... if not a STORAGE class then fail now.
  467. //
  468. if ( Resource->dwType == RESMON_TYPE_DLL ) {
  469. status = (Resource->pResourceTypeControl)( Resource->Id,
  470. CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO,
  471. NULL,
  472. 0,
  473. &resClassInfo,
  474. sizeof(resClassInfo),
  475. &bytesReturned );
  476. } else {
  477. HRESULT hr ;
  478. VARIANT vtIn, vtOut ;
  479. SAFEARRAY sfIn = {1, 0, 1, 0, NULL, {0, 0} } ;
  480. SAFEARRAY sfOut = {1, FADF_FIXEDSIZE, 1, 0, &resClassInfo, {sizeof(resClassInfo), 0} } ;
  481. SAFEARRAY *psfOut = &sfOut ;
  482. vtIn.vt = VT_ARRAY | VT_UI1 ;
  483. vtOut.vt = VT_ARRAY | VT_UI1 | VT_BYREF ;
  484. vtIn.parray = &sfIn ;
  485. vtOut.pparray = &psfOut ;
  486. hr = IClusterResControl_ResourceControl (
  487. Resource->pClusterResControl,
  488. (OLERESID)Resource->Id,
  489. (long)CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO,
  490. &vtIn,
  491. &vtOut,
  492. (long *)&bytesReturned,
  493. &status);
  494. if (FAILED(hr)) {
  495. CL_LOGFAILURE(hr); // Use the default processing
  496. status = ERROR_INVALID_FUNCTION;
  497. }
  498. }
  499. if ( (status != ERROR_SUCCESS) ||
  500. (resClassInfo.rc != CLUS_RESCLASS_STORAGE) ) {
  501. return TRUE; // fail now
  502. }
  503. #endif
  504. retry:
  505. RmpFree( commonBuffer );
  506. commonBuffer = RmpAlloc( size );
  507. if ( !commonBuffer ) {
  508. return TRUE; // fail now
  509. }
  510. ntStatus = NtQuerySystemInformation(
  511. SystemProcessInformation,
  512. commonBuffer,
  513. size,
  514. NULL );
  515. if ( ntStatus == STATUS_INFO_LENGTH_MISMATCH ) {
  516. size += 4096;
  517. goto retry;
  518. }
  519. if ( !NT_SUCCESS(ntStatus) ) {
  520. return TRUE; // fail now
  521. }
  522. processInfo = (PSYSTEM_PROCESS_INFORMATION)commonBuffer;
  523. while ( TRUE ) {
  524. if ( processInfo->ImageName.Buffer ) {
  525. if ( ( ntStatus = RtlUnicodeStringToAnsiString( &pname,
  526. &processInfo->ImageName,
  527. TRUE ) ) != STATUS_SUCCESS ) {
  528. ClRtlLogPrint(LOG_UNUSUAL,
  529. "[RM] ChkdskNotRunning: Unable to convert Unicode string to Ansi, status = 0x%lx...\n",
  530. ntStatus);
  531. break;
  532. }
  533. ptr = strrchr( pname.Buffer, '\\' );
  534. if ( ptr ) {
  535. ptr++;
  536. } else {
  537. ptr = pname.Buffer;
  538. }
  539. if ( lstrcmpiA( ptr, "CHKDSK.EXE" ) == 0 ) {
  540. ClRtlLogPrint(LOG_UNUSUAL,
  541. "[RM] ChkdskNotRunning: Found process %1!ws!.\n",
  542. processInfo->ImageName.Buffer );
  543. RmpFree( pname.Buffer );
  544. RmpFree( commonBuffer );
  545. return FALSE; // chkdsk is running
  546. }
  547. RmpFree( pname.Buffer );
  548. }
  549. if ( processInfo->NextEntryOffset == 0 ) break;
  550. totalOffset += processInfo->NextEntryOffset;
  551. processInfo = (PSYSTEM_PROCESS_INFORMATION)&commonBuffer[totalOffset];
  552. }
  553. RmpFree( commonBuffer );
  554. return TRUE; // CHKDSK is not running
  555. } // RmpChkdskNotRunning
  556. DWORD
  557. RmpTimerThread(
  558. IN LPVOID Context
  559. )
  560. /*++
  561. Routine Description:
  562. Thread to wait on transition of a resource from pending to a stable state.
  563. Arguments:
  564. Context - A pointer to the resource being timed.
  565. Returns:
  566. Win32 error code.
  567. --*/
  568. {
  569. PRESOURCE resource = (PRESOURCE)Context;
  570. DWORD status;
  571. HANDLE timerEvent;
  572. DWORD prevCheckPoint;
  573. CL_ASSERT( resource != NULL );
  574. timerEvent = resource->TimerEvent;
  575. if ( !timerEvent ) {
  576. return(ERROR_SUCCESS);
  577. }
  578. //
  579. // Loop waiting for resource to complete pending operation or to
  580. // shutdown processing.
  581. //
  582. while ( timerEvent ) {
  583. prevCheckPoint = resource->CheckPoint;
  584. status = WaitForSingleObject( timerEvent,
  585. resource->PendingTimeout );
  586. //
  587. // If we were asked to stop, then exit quietly.
  588. //
  589. if ( status != WAIT_TIMEOUT ) {
  590. //
  591. // The thread that cleans the timer event must close the handle.
  592. //
  593. CloseHandle(timerEvent);
  594. resource->TimerEvent = NULL;
  595. return(ERROR_SUCCESS);
  596. }
  597. //
  598. // Check if the resource has not made forward progress... if not,
  599. // then let break out now.
  600. //
  601. // Also if this is a storage class resource make sure that
  602. // CHKDSK is not running.
  603. //
  604. if ( (prevCheckPoint == resource->CheckPoint) &&
  605. RmpChkdskNotRunning( resource ) ) {
  606. break;
  607. }
  608. ClRtlLogPrint(LOG_NOISE,
  609. "[RM] RmpTimerThread: Giving a reprieve for resource %1!ws!...\n",
  610. resource->ResourceName);
  611. }
  612. //
  613. // Indicate that this resource failed!
  614. //
  615. AcquireEventListLock( (PPOLL_EVENT_LIST)resource->EventList );
  616. if ( resource->TimerEvent != NULL ) {
  617. ClRtlLogPrint(LOG_UNUSUAL,
  618. "[RM] RmpTimerThread: Resource %1!ws! pending timed out "
  619. "- setting state to failed.\n",
  620. resource->ResourceName);
  621. CloseHandle(resource->TimerEvent);
  622. resource->TimerEvent = NULL;
  623. resource->State = ClusterResourceFailed;
  624. //
  625. // Log an event
  626. //
  627. status = ERROR_TIMEOUT;
  628. ClusterLogEvent1(LOG_CRITICAL,
  629. LOG_CURRENT_MODULE,
  630. __FILE__,
  631. __LINE__,
  632. RMON_RESOURCE_TIMEOUT,
  633. sizeof(status),
  634. &status,
  635. resource->ResourceName);
  636. //
  637. // Chittur Subbaraman (chitturs) - 4/5/99
  638. //
  639. // Since the resource has failed, there is no point in having
  640. // the OnlineEvent hanging around. If the OnlineEvent is not
  641. // closed out, then you cannot call s_RmOnlineResource or
  642. // s_RmOfflineResource.
  643. //
  644. if ( resource->OnlineEvent != NULL ) {
  645. CloseHandle( resource->OnlineEvent );
  646. resource->OnlineEvent = NULL;
  647. }
  648. RmpPostNotify( resource, NotifyResourceStateChange );
  649. }
  650. ReleaseEventListLock( (PPOLL_EVENT_LIST)resource->EventList );
  651. return(ERROR_SUCCESS);
  652. } // RmpTimerThread
  653. DWORD
  654. RmpOfflineResource(
  655. IN RESID ResourceId,
  656. IN BOOL Shutdown,
  657. OUT DWORD *pdwState
  658. )
  659. /*++
  660. Routine Description:
  661. Brings the specified resource into the offline state.
  662. Arguments:
  663. ResourceId - Supplies the resource to be brought online.
  664. Shutdown - Specifies whether the resource is to be shutdown gracefully
  665. TRUE - resource will be shutdown gracefully
  666. FALSE - resource will be immediately taken offline
  667. pdwState - the new resource state is returned in here.
  668. Return Value:
  669. The new state of the resource.
  670. Notes:
  671. The resource's eventlist lock must NOT be held.
  672. --*/
  673. {
  674. DWORD status=ERROR_SUCCESS;
  675. BOOL success;
  676. PRESOURCE Resource;
  677. HANDLE timerThread;
  678. DWORD threadId;
  679. DWORD loopCount;
  680. BOOL fLockAcquired;
  681. Resource = (PRESOURCE)ResourceId;
  682. CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE);
  683. *pdwState = Resource->State;
  684. //if this is a graceful close,create the online/offline
  685. //event such that if a resource calls rmpsetresourcestatus
  686. //soon after online for that resource is called and before
  687. //the timer thread/event is even created then we wont have
  688. //an event leak and an abadoned thread
  689. if (Shutdown)
  690. {
  691. //
  692. // We should not be in a Pending state.
  693. //
  694. if ( Resource->State > ClusterResourcePending )
  695. {
  696. return(ERROR_INVALID_STATE);
  697. }
  698. //
  699. // Create an event to allow the SetResourceStatus callback to synchronize
  700. // execution with this thread.
  701. //
  702. if ( Resource->OnlineEvent )
  703. {
  704. return(ERROR_NOT_READY);
  705. }
  706. Resource->OnlineEvent = CreateEvent( NULL,
  707. FALSE,
  708. FALSE,
  709. NULL );
  710. if ( Resource->OnlineEvent == NULL )
  711. {
  712. return(GetLastError());
  713. }
  714. }
  715. //
  716. // Lock the EventList Lock, insert the
  717. // resource into the list, and take the resource offline.
  718. // The lock is required to synchronize access to the resource list and
  719. // to serialize any calls to resource DLLs. Only one thread may be
  720. // calling a resource DLL at any time. This prevents resource DLLs
  721. // from having to worry about being thread-safe.
  722. //
  723. AcquireListLock(); // We need this lock for the potential remove below!
  724. AcquireEventListLock( (PPOLL_EVENT_LIST)Resource->EventList );
  725. //
  726. // Stop any previous timer threads. Should this be done before the lock
  727. // is held?
  728. //
  729. if ( Resource->TimerEvent != NULL ) {
  730. success = SetEvent( Resource->TimerEvent );
  731. }
  732. //
  733. // Update shared state to indicate we are taking a resource offline
  734. //
  735. RmpSetMonitorState(RmonOfflineResource, Resource);
  736. //
  737. // If we have an error signal event, then remove it from our lists.
  738. //
  739. if ( Resource->EventHandle ) {
  740. RmpRemovePollEvent( Resource->EventHandle );
  741. }
  742. //
  743. // Call Offline entrypoint.
  744. //
  745. if ( Shutdown )
  746. {
  747. CL_ASSERT( (Resource->State < ClusterResourcePending) );
  748. try {
  749. #ifdef COMRES
  750. status = RESMON_OFFLINE (Resource) ;
  751. #else
  752. status = (Resource->Offline)(Resource->Id);
  753. #endif
  754. } except (EXCEPTION_EXECUTE_HANDLER) {
  755. status = GetExceptionCode();
  756. }
  757. //
  758. // If the Resource DLL returns pending, then start a timer.
  759. //
  760. if (status == ERROR_SUCCESS) {
  761. //close the event
  762. SetEvent( Resource->OnlineEvent );
  763. CloseHandle( Resource->OnlineEvent );
  764. Resource->OnlineEvent = NULL;
  765. Resource->State = ClusterResourceOffline;
  766. }
  767. else if ( status == ERROR_IO_PENDING ) {
  768. CL_ASSERT(Resource->TimerEvent == NULL );
  769. Resource->TimerEvent = CreateEvent( NULL,
  770. FALSE,
  771. FALSE,
  772. NULL );
  773. if ( Resource->TimerEvent == NULL ) {
  774. CL_UNEXPECTED_ERROR(status = GetLastError());
  775. } else {
  776. timerThread = CreateThread( NULL,
  777. 0,
  778. RmpTimerThread,
  779. Resource,
  780. 0,
  781. &threadId );
  782. if ( timerThread == NULL ) {
  783. CL_UNEXPECTED_ERROR(status = GetLastError());
  784. } else {
  785. Resource->State = ClusterResourceOfflinePending;
  786. //Resource->WaitHint = PENDING_TIMEOUT;
  787. //Resource->CheckPoint = 0;
  788. //
  789. // Chittur Subbaraman (chitturs) - 1/12/99
  790. //
  791. // Raise the timer thread priority to highest. This
  792. // is necessary to avoid certain cases in which the
  793. // timer thread is sluggish to close out the timer event
  794. // handle before a second offline. Note that there are
  795. // no major performance implications by doing this since
  796. // the timer thread is in a wait state most of the time.
  797. //
  798. if ( !SetThreadPriority( timerThread, THREAD_PRIORITY_HIGHEST ) )
  799. {
  800. ClRtlLogPrint(LOG_UNUSUAL,
  801. "[RM] RmpOfflineResource:Error setting priority of timer "
  802. "thread for resource %1!ws!\n",
  803. Resource->ResourceName);
  804. CL_LOGFAILURE( GetLastError() );
  805. }
  806. CloseHandle( timerThread );
  807. }
  808. }
  809. Resource->State = ClusterResourceOfflinePending;
  810. SetEvent(Resource->OnlineEvent);
  811. }
  812. else {
  813. CloseHandle( Resource->OnlineEvent );
  814. Resource->OnlineEvent = NULL;
  815. ClRtlLogPrint(LOG_UNUSUAL,
  816. "[RM] OfflinelineResource failed, resource %1!ws!, status = %2!u!.\n",
  817. Resource->ResourceName,
  818. status);
  819. ClusterLogEvent1(LOG_CRITICAL,
  820. LOG_CURRENT_MODULE,
  821. __FILE__,
  822. __LINE__,
  823. RMON_OFFLINE_FAILED,
  824. sizeof(status),
  825. &status,
  826. Resource->ResourceName);
  827. Resource->State = ClusterResourceFailed;
  828. }
  829. } else {
  830. Resource->dwEntryPoint = RESDLL_ENTRY_TERMINATE;
  831. try {
  832. #ifdef COMRES
  833. RESMON_TERMINATE (Resource) ;
  834. #else
  835. (Resource->Terminate)(Resource->Id);
  836. #endif
  837. } except (EXCEPTION_EXECUTE_HANDLER) {
  838. }
  839. Resource->dwEntryPoint = 0;
  840. Resource->State = ClusterResourceOffline;
  841. }
  842. RmpSetMonitorState(RmonIdle, NULL);
  843. ReleaseEventListLock( (PPOLL_EVENT_LIST)Resource->EventList );
  844. ReleaseListLock();
  845. *pdwState = Resource->State;
  846. return(status);
  847. } // RmpOfflineResource
  848. VOID
  849. RmpRemoveResourceList(
  850. IN PRESOURCE Resource
  851. )
  852. /*++
  853. Routine Description:
  854. Removes a resource into the monitoring list.
  855. Arguments:
  856. Resource - Supplies the resource to be removed from the list.
  857. Return Value:
  858. None.
  859. --*/
  860. {
  861. PPOLL_EVENT_LIST EventList = (PPOLL_EVENT_LIST)Resource->EventList;
  862. AcquireEventListLock( EventList );
  863. //
  864. // Make sure it is really in the list.
  865. //
  866. CL_ASSERT(Resource->Flags & RESOURCE_INSERTED);
  867. CL_ASSERT(Resource->ListEntry.Flink->Blink == &Resource->ListEntry);
  868. CL_ASSERT(Resource->ListEntry.Blink->Flink == &Resource->ListEntry);
  869. CL_ASSERT(EventList->NumberOfResources);
  870. RemoveEntryList(&Resource->ListEntry);
  871. Resource->Flags &= ~RESOURCE_INSERTED;
  872. --EventList->NumberOfResources;
  873. ReleaseEventListLock( EventList );
  874. } // RmpRemoveResourceList
  875. DWORD
  876. RmpInsertResourceList(
  877. IN PRESOURCE Resource,
  878. IN OPTIONAL PPOLL_EVENT_LIST pPollEventList
  879. )
  880. /*++
  881. Routine Description:
  882. Inserts a resource into the monitoring list.
  883. Each resource is placed in a list along with other resources with the
  884. same poll interval. The IsAlive and LooksAlive timeouts are adjusted
  885. so that the IsAlive interval is an even multiple of the LooksAlive interval.
  886. Thus, the IsAlive poll can simply be done every Nth poll instead of the normal
  887. LooksAlive poll.
  888. Arguments:
  889. Resource - Supplies the resource to be added to the list.
  890. pPollEventList - Supplies the eventlist in which the resource is to
  891. be added. Optional.
  892. Return Value:
  893. None.
  894. --*/
  895. {
  896. DWORD Temp1, Temp2;
  897. ULONG i;
  898. PMONITOR_BUCKET NewBucket;
  899. PMONITOR_BUCKET Bucket;
  900. DWORDLONG PollInterval;
  901. PPOLL_EVENT_LIST EventList;
  902. PPOLL_EVENT_LIST MinEventList;
  903. PLIST_ENTRY ListEntry;
  904. DWORD dwError = ERROR_SUCCESS;
  905. CL_ASSERT((Resource->Flags & RESOURCE_INSERTED) == 0);
  906. //
  907. // If we have no LooksAlivePollInterval, then poll the IsAlive on every
  908. // poll interval. Otherwise, poll IsAlive every N LooksAlive poll
  909. // intervals.
  910. //
  911. if ( Resource->LooksAlivePollInterval == 0 ) {
  912. //
  913. // Round IsAlivePollInterval up to the system granularity
  914. //
  915. Temp1 = Resource->IsAlivePollInterval;
  916. Temp1 = Temp1 + POLL_GRANULARITY - 1;
  917. //if this has rolled over
  918. if (Temp1 < Resource->IsAlivePollInterval)
  919. Temp1 = 0xFFFFFFFF;
  920. Temp1 = Temp1 / POLL_GRANULARITY;
  921. Temp1 = Temp1 * POLL_GRANULARITY;
  922. Resource->IsAlivePollInterval = Temp1;
  923. Resource->IsAliveRollover = 1;
  924. //
  925. // Convert poll interval from ms to 100ns units
  926. //
  927. PollInterval = Resource->IsAlivePollInterval * 10 * 1000;
  928. } else {
  929. //
  930. // First round LooksAlivePollInterval up to the system granularity
  931. //
  932. Temp1 = Resource->LooksAlivePollInterval;
  933. Temp1 = (Temp1 + POLL_GRANULARITY - 1) ;
  934. //check for rollover
  935. if (Temp1 < Resource->LooksAlivePollInterval)
  936. Temp1 = 0xFFFFFFFF;
  937. Temp1 = Temp1/POLL_GRANULARITY;
  938. Temp1 = Temp1 * POLL_GRANULARITY;
  939. Resource->LooksAlivePollInterval = Temp1;
  940. //
  941. // Now round IsAlivePollInterval to a multiple of LooksAlivePollInterval
  942. //
  943. Temp2 = Resource->IsAlivePollInterval;
  944. Temp2 = (Temp2 + Temp1 - 1) / Temp1;
  945. Temp2 = Temp2 * Temp1;
  946. Resource->IsAlivePollInterval = Temp2;
  947. Resource->IsAliveRollover = (ULONG)(Temp2 / Temp1);
  948. CL_ASSERT((Temp2 / Temp1) * Temp1 == Temp2);
  949. //
  950. // Convert poll interval from ms to 100ns units
  951. //
  952. PollInterval = Resource->LooksAlivePollInterval * 10 * 1000;
  953. }
  954. if ( PollInterval > 0xFFFFFFFF ) {
  955. PollInterval = 0xFFFFFFFF;
  956. }
  957. Resource->IsAliveCount = 0;
  958. //
  959. // Chittur Subbaraman (chitturs) - 1/30/2000
  960. //
  961. // If an eventlist is supplied as parameter, do not attempt to
  962. // find a new eventlist.
  963. //
  964. if( ARGUMENT_PRESENT( pPollEventList ) ) {
  965. MinEventList = pPollEventList;
  966. goto skip_eventlist_find;
  967. }
  968. //
  969. // First find the EventList with the fewest number of entries.
  970. //
  971. AcquireListLock();
  972. ListEntry = RmpEventListHead.Flink;
  973. MinEventList = CONTAINING_RECORD(ListEntry,
  974. POLL_EVENT_LIST,
  975. Next );
  976. CL_ASSERT( ListEntry != &RmpEventListHead );
  977. for ( ListEntry = RmpEventListHead.Flink;
  978. ListEntry != &RmpEventListHead;
  979. ListEntry = ListEntry->Flink ) {
  980. EventList = CONTAINING_RECORD( ListEntry, POLL_EVENT_LIST, Next );
  981. if ( EventList->NumberOfResources < MinEventList->NumberOfResources ) {
  982. MinEventList = EventList;
  983. }
  984. }
  985. ReleaseListLock();
  986. if ( MinEventList->NumberOfResources >= MAX_RESOURCES_PER_THREAD ) {
  987. MinEventList = RmpCreateEventList();
  988. }
  989. skip_eventlist_find:
  990. if ( MinEventList == NULL ) {
  991. dwError = GetLastError();
  992. goto FnExit;
  993. }
  994. EventList = MinEventList;
  995. AcquireEventListLock( EventList );
  996. Resource->EventList = (PVOID)EventList;
  997. //
  998. // Search the list for a bucket with the same period as this resource.
  999. //
  1000. Bucket = CONTAINING_RECORD(EventList->BucketListHead.Flink,
  1001. MONITOR_BUCKET,
  1002. BucketList);
  1003. while (&Bucket->BucketList != &EventList->BucketListHead) {
  1004. if (Bucket->Period == PollInterval) {
  1005. break;
  1006. }
  1007. Bucket = CONTAINING_RECORD(Bucket->BucketList.Flink,
  1008. MONITOR_BUCKET,
  1009. BucketList);
  1010. }
  1011. if (&Bucket->BucketList == &EventList->BucketListHead) {
  1012. //
  1013. // Need to add a new bucket with this period.
  1014. //
  1015. Bucket = RmpAlloc(sizeof(MONITOR_BUCKET));
  1016. if (Bucket == NULL) {
  1017. CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
  1018. }
  1019. InsertTailList(&EventList->BucketListHead, &Bucket->BucketList);
  1020. InitializeListHead(&Bucket->ResourceList);
  1021. GetSystemTimeAsFileTime((LPFILETIME)&Bucket->DueTime);
  1022. Bucket->Period = PollInterval;
  1023. if ( PollInterval == 0 ) {
  1024. // The following constant should be over 136 years
  1025. Bucket->DueTime += (DWORDLONG)((DWORDLONG)1000 * (DWORD) -1);
  1026. } else {
  1027. Bucket->DueTime += Bucket->Period;
  1028. }
  1029. EventList->NumberOfBuckets++;
  1030. }
  1031. InsertHeadList(&Bucket->ResourceList, &Resource->ListEntry);
  1032. Resource->Flags |= RESOURCE_INSERTED;
  1033. ++EventList->NumberOfResources;
  1034. ReleaseEventListLock( EventList );
  1035. FnExit:
  1036. return (dwError);
  1037. } // RmpInsertResourceList
  1038. VOID
  1039. RmpRundownResources(
  1040. VOID
  1041. )
  1042. /*++
  1043. Routine Description:
  1044. Runs down the list of active resources and terminates/closes
  1045. each one.
  1046. Arguments:
  1047. None
  1048. Return Value:
  1049. None.
  1050. --*/
  1051. {
  1052. PLIST_ENTRY ListEntry;
  1053. PMONITOR_BUCKET Bucket;
  1054. PRESOURCE Resource;
  1055. PPOLL_EVENT_LIST EventList;
  1056. DWORD i;
  1057. BOOL fLockAcquired;
  1058. AcquireListLock();
  1059. while (!IsListEmpty(&RmpEventListHead)) {
  1060. ListEntry = RemoveHeadList(&RmpEventListHead);
  1061. EventList = CONTAINING_RECORD(ListEntry,
  1062. POLL_EVENT_LIST,
  1063. Next);
  1064. AcquireEventListLock( EventList );
  1065. //
  1066. // Find all resources on the bucket list and close them.
  1067. //
  1068. while (!IsListEmpty(&EventList->BucketListHead)) {
  1069. ListEntry = RemoveHeadList(&EventList->BucketListHead);
  1070. Bucket = CONTAINING_RECORD(ListEntry,
  1071. MONITOR_BUCKET,
  1072. BucketList);
  1073. while (!IsListEmpty(&Bucket->ResourceList)) {
  1074. ListEntry = RemoveHeadList(&Bucket->ResourceList);
  1075. Resource = CONTAINING_RECORD(ListEntry,
  1076. RESOURCE,
  1077. ListEntry);
  1078. //
  1079. // Acquire spin lock for synchronizing with arbitrate.
  1080. //
  1081. fLockAcquired = RmpAcquireSpinLock( Resource, TRUE );
  1082. //
  1083. // If the resource is in online or in pending state, terminate it. Note that
  1084. // we need to terminate pending resources as well, otherwise our close call
  1085. // down below would cause those threads to AV.
  1086. //
  1087. if ((Resource->State == ClusterResourceOnline) ||
  1088. (Resource->State > ClusterResourcePending)) {
  1089. Resource->dwEntryPoint = RESDLL_ENTRY_TERMINATE;
  1090. ClRtlLogPrint( LOG_NOISE,
  1091. "[RM] RundownResources, terminate resource <%1!ws!>.\n",
  1092. Resource->ResourceName );
  1093. #ifdef COMRES
  1094. RESMON_TERMINATE (Resource) ;
  1095. #else
  1096. (Resource->Terminate)(Resource->Id);
  1097. #endif
  1098. Resource->dwEntryPoint = 0;
  1099. }
  1100. //
  1101. // If the resource has been arbitrated for, release it.
  1102. //
  1103. if (Resource->IsArbitrated) {
  1104. #ifdef COMRES
  1105. RESMON_RELEASE (Resource) ;
  1106. #else
  1107. (Resource->Release)(Resource->Id);
  1108. #endif
  1109. }
  1110. //
  1111. // Close the resource.
  1112. //
  1113. Resource->dwEntryPoint = RESDLL_ENTRY_CLOSE;
  1114. #ifdef COMRES
  1115. RESMON_CLOSE (Resource) ;
  1116. #else
  1117. (Resource->Close)(Resource->Id);
  1118. #endif
  1119. Resource->dwEntryPoint = 0;
  1120. //
  1121. // Zero the resource links so that RmCloseResource can tell
  1122. // that this resource is already terminated and closed.
  1123. //
  1124. Resource->ListEntry.Flink = Resource->ListEntry.Blink = NULL;
  1125. if ( fLockAcquired ) RmpReleaseSpinLock ( Resource );
  1126. }
  1127. }
  1128. //
  1129. // Stop the thread processing this event list, and the notify event.
  1130. //
  1131. CL_ASSERT(EventList->ThreadHandle != NULL);
  1132. CL_ASSERT( EventList->ListNotify != NULL );
  1133. SetEvent(EventList->ListNotify);
  1134. ReleaseEventListLock( EventList );
  1135. //
  1136. // Wait for the thread to finish up before freeing the memory it is
  1137. // referencing.
  1138. //
  1139. ReleaseListLock(); // Release the list lock while waiting...
  1140. //
  1141. // Wait for 60 seconds for the threads to finish up.
  1142. //
  1143. WaitForSingleObject(EventList->ThreadHandle, 60000);
  1144. AcquireListLock();
  1145. CloseHandle(EventList->ThreadHandle);
  1146. EventList->ThreadHandle = NULL;
  1147. //
  1148. // We will assume that all of the event handles were closed as a
  1149. // result of calling the Close entrypoint.
  1150. //
  1151. RmpFree( EventList );
  1152. }
  1153. ReleaseListLock();
  1154. //
  1155. // Post shutdown of notification thread.
  1156. //
  1157. ClRtlLogPrint( LOG_NOISE, "[RM] RundownResources posting shutdown notification.\n");
  1158. RmpPostNotify( NULL, NotifyShutdown );
  1159. return;
  1160. } // RmpRundownResources
  1161. VOID
  1162. RmpSetEventListLockOwner(
  1163. IN PRESOURCE pResource,
  1164. IN DWORD dwMonitorState
  1165. )
  1166. /*++
  1167. Routine Description:
  1168. Saves the resource and the resource DLL entry point into the eventlist structure just
  1169. before the resource DLL entry point is called.
  1170. Arguments:
  1171. pResource - Pointer to the resource structure.
  1172. dwMonitorState - The state of the resource monitor, what resource DLL entry point it has called.
  1173. Return Value:
  1174. None.
  1175. --*/
  1176. {
  1177. PPOLL_EVENT_LIST pEventList;
  1178. if ( pResource == NULL ) return;
  1179. pEventList = (PPOLL_EVENT_LIST) pResource->EventList;
  1180. if ( pEventList != NULL )
  1181. {
  1182. pEventList->LockOwnerResource = pResource;
  1183. pEventList->MonitorState = dwMonitorState;
  1184. }
  1185. }
  1186. BOOL
  1187. RmpAcquireSpinLock(
  1188. IN PRESOURCE pResource,
  1189. IN BOOL fSpin
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. Acquire a spin lock.
  1194. Arguments:
  1195. pResource - Pointer to the resource structure.
  1196. fSpin - TRUE if we must spin if the lock is unavailable. FALSE we shouldn't spin but return
  1197. a failure.
  1198. Return Value:
  1199. TRUE - Lock is acquired.
  1200. FALSE - Lock is not acquired.
  1201. --*/
  1202. {
  1203. DWORD dwRetryCount = 0;
  1204. //
  1205. // This resource is not one that supports arbitrate. Return failure. Note that resources
  1206. // other than the quorum resource support this entry point. We use this instead of the
  1207. // pResource->IsArbitrate since that variable is set only after the first arbitrate is called
  1208. // and we need to use this function before the arbitrate entry point is called.
  1209. //
  1210. if ( pResource->pArbitrate == NULL ) return FALSE;
  1211. //
  1212. // Initial check for lock availability.
  1213. //
  1214. if ( InterlockedCompareExchange( &pResource->fArbLock, 1, 0 ) == 0 ) return TRUE;
  1215. //
  1216. // Did not get the lock. Check if we must spin.
  1217. //
  1218. if ( fSpin == FALSE )
  1219. {
  1220. ClRtlLogPrint(LOG_UNUSUAL,
  1221. "[RM] RmpAcquireSpinLock: Could not get spinlock for resource %1!ws! after no wait...\n",
  1222. pResource->ResourceName);
  1223. return FALSE;
  1224. }
  1225. //
  1226. // We must spin. Spin until timeout.
  1227. //
  1228. while ( ( dwRetryCount < RESMON_MAX_SLOCK_RETRIES ) &&
  1229. ( InterlockedCompareExchange( &pResource->fArbLock, 1, 0 ) ) )
  1230. {
  1231. Sleep ( 500 );
  1232. dwRetryCount ++;
  1233. }
  1234. if ( dwRetryCount == RESMON_MAX_SLOCK_RETRIES )
  1235. {
  1236. ClRtlLogPrint(LOG_ERROR,
  1237. "[RM] RmpAcquireSpinLock: Could not get spinlock for resource %1!ws! after spinning...\n",
  1238. pResource->ResourceName);
  1239. return FALSE;
  1240. }
  1241. return TRUE;
  1242. } // RmpAcquireSpinLock
  1243. VOID
  1244. RmpReleaseSpinLock(
  1245. IN PRESOURCE pResource
  1246. )
  1247. /*++
  1248. Routine Description:
  1249. Release a spin lock.
  1250. Arguments:
  1251. pResource - Pointer to the resource structure.
  1252. Return Value:
  1253. None.
  1254. --*/
  1255. {
  1256. DWORD dwRetryCount = 0;
  1257. //
  1258. // This resource is not one that supports arbitrate. Return failure.
  1259. //
  1260. if ( pResource->pArbitrate == NULL ) return;
  1261. InterlockedExchange( &pResource->fArbLock, 0 );
  1262. } // RmpReleaseSpinLock