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.

697 lines
22 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. notify.c
  5. Abstract:
  6. Registry notification processor for registry checkpoints.
  7. This is a fairly gnarly bit of code. Each resource can have multiple
  8. registry subtree notifications associated with it. Each active notification
  9. has an associated Registry Notify Block (RNB) chained off the FM_RESOURCE
  10. structure. A single registry notification thread can handle max 31 RNBs
  11. This is because WaitForMultipleObjects maxes out at 64 objects and each RNB
  12. takes two wait slots.
  13. When an RNB is created, an available notify thread is found (or created if
  14. there are none). Then the notify thread is woken up with its command event
  15. to insert the RNB into its array of wait events.
  16. Once a notification occurs, the notify thread sets the RNB to "pending" and
  17. sets its associated timer to go off in a few seconds. If another registry
  18. notification occurs, the timer is reset. Thus, the timer will not actually
  19. go off until there have been no registry notifications for a few seconds.
  20. When the RNB timer fires, the notify thread checkpoints its subtree and
  21. puts it back on the queue. If the notify thread is asked to remove a RNB
  22. that is in the "pending" state, it cancels the timer, checkpoints the registry,
  23. and removes the RNB from its list.
  24. Author:
  25. John Vert (jvert) 1/17/1997
  26. Revision History:
  27. --*/
  28. #include "cpp.h"
  29. CRITICAL_SECTION CppNotifyLock;
  30. LIST_ENTRY CpNotifyListHead;
  31. #define MAX_BLOCKS_PER_GROUP ((MAXIMUM_WAIT_OBJECTS-1)/2)
  32. #define LAZY_CHECKPOINT 3 // checkpoint 3 seconds after last update
  33. typedef struct _RNB {
  34. struct _RNB *Next;
  35. BOOL Pending;
  36. PFM_RESOURCE Resource;
  37. LPWSTR KeyName;
  38. DWORD dwId;
  39. HKEY hKey;
  40. HANDLE hEvent;
  41. HANDLE hTimer;
  42. struct _NOTIFY_GROUP *NotifyGroup;
  43. DWORD NotifySlot;
  44. } RNB, *PRNB;
  45. typedef enum {
  46. NotifyAddRNB,
  47. NotifyRemoveRNB
  48. } NOTIFY_COMMAND;
  49. typedef struct _NOTIFY_GROUP {
  50. LIST_ENTRY ListEntry; // Linkage onto CpNotifyListHead;
  51. HANDLE hCommandEvent;
  52. HANDLE hCommandComplete;
  53. HANDLE hThread;
  54. NOTIFY_COMMAND Command;
  55. ULONG_PTR CommandContext;
  56. DWORD BlockCount;
  57. HANDLE WaitArray[MAXIMUM_WAIT_OBJECTS];
  58. PRNB NotifyBlock[MAXIMUM_WAIT_OBJECTS-1];
  59. } NOTIFY_GROUP, *PNOTIFY_GROUP;
  60. //
  61. // Local function prototypes
  62. //
  63. DWORD
  64. CppRegNotifyThread(
  65. IN PNOTIFY_GROUP Group
  66. );
  67. DWORD
  68. CppNotifyCheckpoint(
  69. IN PRNB Rnb
  70. );
  71. DWORD
  72. CppRegisterNotify(
  73. IN PFM_RESOURCE Resource,
  74. IN LPCWSTR lpszKeyName,
  75. IN DWORD dwId
  76. )
  77. /*++
  78. Routine Description:
  79. Creates a registry notification block for the specified resource.
  80. Arguments:
  81. Resource - Supplies the resource the notification is for.
  82. KeyName - Supplies the registry subtree (relative to HKEY_LOCAL_MACHINE
  83. CheckpointId - Supplies the checkpoint ID.
  84. Return Value:
  85. ERROR_SUCCESS if successful
  86. Win32 error code otherwise
  87. --*/
  88. {
  89. DWORD Status;
  90. PLIST_ENTRY ListEntry;
  91. PNOTIFY_GROUP Group;
  92. PNOTIFY_GROUP CurrentGroup;
  93. PRNB Block;
  94. Block = CsAlloc(sizeof(RNB));
  95. Block->Resource = Resource;
  96. Block->KeyName = CsStrDup(lpszKeyName);
  97. Block->dwId = dwId;
  98. Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
  99. lpszKeyName,
  100. &Block->hKey);
  101. if (Status != ERROR_SUCCESS) {
  102. ClRtlLogPrint(LOG_UNUSUAL,
  103. "[CP] CppRegisterNotify failed to open key %1!ws! error %2!d!\n",
  104. lpszKeyName,
  105. Status);
  106. return(Status);
  107. }
  108. Block->hTimer = CreateWaitableTimer(NULL,FALSE,NULL);
  109. CL_ASSERT(Block->hTimer != NULL);
  110. Block->hEvent = CreateEventW(NULL,TRUE,FALSE,NULL);
  111. CL_ASSERT(Block->hEvent != NULL);
  112. Block->Pending = FALSE;
  113. //
  114. // Get the lock
  115. //
  116. EnterCriticalSection(&CppNotifyLock);
  117. //
  118. // Find a group with space for this notify block
  119. //
  120. Group = NULL;
  121. ListEntry = CpNotifyListHead.Flink;
  122. while (ListEntry != &CpNotifyListHead) {
  123. CurrentGroup = CONTAINING_RECORD(ListEntry,
  124. NOTIFY_GROUP,
  125. ListEntry);
  126. ListEntry = ListEntry->Flink;
  127. if (CurrentGroup->BlockCount < MAX_BLOCKS_PER_GROUP) {
  128. //
  129. // Found a group.
  130. //
  131. Group = CurrentGroup;
  132. break;
  133. }
  134. }
  135. if (Group == NULL) {
  136. DWORD ThreadId;
  137. HANDLE hThread;
  138. //
  139. // Need to spin up a new group
  140. //
  141. Group = CsAlloc(sizeof(NOTIFY_GROUP));
  142. ZeroMemory(Group, sizeof(NOTIFY_GROUP));
  143. Group->hCommandEvent = CreateEventW(NULL,FALSE,FALSE,NULL);
  144. if ( Group->hCommandEvent == NULL ) {
  145. CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
  146. }
  147. Group->hCommandComplete = CreateEventW(NULL,FALSE,FALSE,NULL);
  148. if ( Group->hCommandComplete == NULL ) {
  149. CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
  150. }
  151. hThread = CreateThread(NULL,
  152. 0,
  153. CppRegNotifyThread,
  154. Group,
  155. 0,
  156. &ThreadId);
  157. if (hThread == NULL) {
  158. Status = GetLastError();
  159. ClRtlLogPrint(LOG_CRITICAL,
  160. "[CP] CppRegisterNotify failed to create new notify thread %1!d!\n",
  161. Status);
  162. LocalFree(Group);
  163. goto error_exit2;
  164. }
  165. Group->hThread = hThread;
  166. InsertHeadList(&CpNotifyListHead, &Group->ListEntry);
  167. }
  168. //
  169. // Wake up the notify thread to insert the RNB for us.
  170. //
  171. Block->NotifyGroup = Group;
  172. Group->Command = NotifyAddRNB;
  173. Group->CommandContext = (ULONG_PTR)Block;
  174. SetEvent(Group->hCommandEvent);
  175. WaitForSingleObject(Group->hCommandComplete, INFINITE);
  176. Block->Next = (PRNB)Resource->CheckpointState;
  177. Resource->CheckpointState = Block;
  178. LeaveCriticalSection(&CppNotifyLock);
  179. return(ERROR_SUCCESS);
  180. error_exit2:
  181. LeaveCriticalSection(&CppNotifyLock);
  182. RegCloseKey(Block->hKey);
  183. CloseHandle(Block->hTimer);
  184. CloseHandle(Block->hEvent);
  185. CsFree(Block->KeyName);
  186. CsFree(Block);
  187. return(Status);
  188. }
  189. DWORD
  190. CppRegNotifyThread(
  191. IN PNOTIFY_GROUP Group
  192. )
  193. /*++
  194. Routine Description:
  195. Worker thread that handles multiple registry notification subtrees.
  196. Arguments:
  197. Group - Supplies the NOTIFY_GROUP control structure owned by this thread.
  198. Return Value:
  199. None.
  200. --*/
  201. {
  202. PRNB Rnb;
  203. DWORD Signalled;
  204. DWORD Index;
  205. BOOL Success;
  206. DWORD Slot;
  207. DWORD Status;
  208. Group->BlockCount = 0;
  209. Group->WaitArray[0] = Group->hCommandEvent;
  210. do {
  211. Signalled = WaitForMultipleObjects(Group->BlockCount*2 + 1,
  212. Group->WaitArray,
  213. FALSE,
  214. INFINITE);
  215. if (Signalled == Group->BlockCount*2) {
  216. switch (Group->Command) {
  217. case NotifyAddRNB:
  218. //
  219. // Add this notify block to our list.
  220. //
  221. CL_ASSERT(Group->BlockCount < MAX_BLOCKS_PER_GROUP);
  222. Rnb = (PRNB)Group->CommandContext;
  223. Status = RegNotifyChangeKeyValue(Rnb->hKey,
  224. TRUE,
  225. REG_LEGAL_CHANGE_FILTER,
  226. Rnb->hEvent,
  227. TRUE);
  228. if (Status != ERROR_SUCCESS) {
  229. CL_UNEXPECTED_ERROR(Status);
  230. }
  231. Index = Group->BlockCount*2;
  232. Group->WaitArray[Index] = Rnb->hEvent;
  233. Group->WaitArray[Index+1] = Rnb->hTimer;
  234. Rnb->NotifySlot = Group->BlockCount;
  235. Group->NotifyBlock[Rnb->NotifySlot] = Rnb;
  236. ++Group->BlockCount;
  237. Group->WaitArray[Group->BlockCount*2] = Group->hCommandEvent;
  238. break;
  239. case NotifyRemoveRNB:
  240. Rnb = (PRNB)Group->CommandContext;
  241. //
  242. // Check to see if the RNB is pending. If so, checkpoint it
  243. // now before we remove it.
  244. //
  245. if (Rnb->Pending) {
  246. DWORD Count = 60;
  247. ClRtlLogPrint(LOG_NOISE,
  248. "[CP] CppRegNotifyThread checkpointing key %1!ws! to id %2!d! due to removal while pending\n",
  249. Rnb->KeyName,
  250. Rnb->dwId);
  251. RetryCheckpoint:
  252. Status = CppNotifyCheckpoint(Rnb);
  253. if (Status != ERROR_SUCCESS)
  254. {
  255. WCHAR string[16];
  256. ClRtlLogPrint(LOG_CRITICAL,
  257. "[CP] CppRegNotifyThread, CppNotifyCheckpoint failed with Status=%1!u!\n",
  258. Status);
  259. if ((Status == ERROR_ACCESS_DENIED) ||
  260. (Status == ERROR_INVALID_FUNCTION) ||
  261. (Status == ERROR_NOT_READY) ||
  262. (Status == RPC_X_INVALID_PIPE_OPERATION) ||
  263. (Status == ERROR_BUSY) ||
  264. (Status == ERROR_SWAPERROR))
  265. {
  266. //SS: we should retry forever??
  267. //SS: Since we allow the quorum to come
  268. //offline after 30 seconds of waiting on
  269. //pending resources, the checkpointing should
  270. //be able to succeed
  271. if (Count--)
  272. {
  273. Sleep(1000);
  274. goto RetryCheckpoint;
  275. }
  276. }
  277. #if DBG
  278. if (IsDebuggerPresent())
  279. DebugBreak();
  280. #endif
  281. wsprintfW(&(string[0]), L"%u", Status);
  282. CL_LOGCLUSERROR2(CP_SAVE_REGISTRY_FAILURE, Rnb->KeyName, string);
  283. }
  284. // irrespective of the failure set pending to FALSE
  285. Rnb->Pending = FALSE;
  286. }
  287. //
  288. // Move everything down to take the previous RNB's slot.
  289. //
  290. Index = Rnb->NotifySlot * 2 ;
  291. Group->BlockCount--;
  292. for (Slot = Rnb->NotifySlot; Slot < Group->BlockCount; Slot++) {
  293. Group->NotifyBlock[Slot] = Group->NotifyBlock[Slot+1];
  294. Group->NotifyBlock[Slot]->NotifySlot--;
  295. Group->WaitArray[Index] = Group->WaitArray[Index+2];
  296. Group->WaitArray[Index+1] = Group->WaitArray[Index+3];
  297. Index += 2;
  298. }
  299. Group->WaitArray[Index] = NULL;
  300. Group->WaitArray[Index+1] = NULL;
  301. Group->WaitArray[Index+2] = NULL;
  302. Group->NotifyBlock[Group->BlockCount] = NULL;
  303. Group->WaitArray[Group->BlockCount*2] = Group->hCommandEvent;
  304. break;
  305. default:
  306. CL_UNEXPECTED_ERROR( Group->Command );
  307. break;
  308. }
  309. SetEvent(Group->hCommandComplete);
  310. } else {
  311. //
  312. // Either a registry notification or a timer has fired.
  313. // Process this.
  314. //
  315. Rnb = Group->NotifyBlock[(Signalled)/2];
  316. if (!(Signalled & 1)) {
  317. LARGE_INTEGER DueTime;
  318. //
  319. // This is a registry notification.
  320. // All we do for registry notifications is set the timer, issue
  321. // the RegNotify again, mark the RNB as pending, and rewait.
  322. //
  323. DueTime.QuadPart = -10 * 1000 * 1000 * LAZY_CHECKPOINT;
  324. Success = SetWaitableTimer(Rnb->hTimer,
  325. &DueTime,
  326. 0,
  327. NULL,
  328. NULL,
  329. FALSE);
  330. CL_ASSERT(Success);
  331. Status = RegNotifyChangeKeyValue(Rnb->hKey,
  332. TRUE,
  333. REG_LEGAL_CHANGE_FILTER,
  334. Rnb->hEvent,
  335. TRUE);
  336. if (Status != ERROR_SUCCESS) {
  337. ClRtlLogPrint(LOG_CRITICAL,
  338. "[CP] CppRegNotifyThread - error %1!d! attempting to reregister notification for %2!ws!\n",
  339. Status,
  340. Rnb->KeyName);
  341. }
  342. //
  343. // Mark it pending so if someone tries to remove it we know that
  344. // we should checkpoint first.
  345. //
  346. Rnb->Pending = TRUE;
  347. } else {
  348. //
  349. // This must be a timer firing
  350. //
  351. CL_ASSERT(Rnb->Pending);
  352. ClRtlLogPrint(LOG_NOISE,
  353. "[CP] CppRegNotifyThread checkpointing key %1!ws! to id %2!d! due to timer\n",
  354. Rnb->KeyName,
  355. Rnb->dwId);
  356. Status = CppNotifyCheckpoint(Rnb);
  357. if (Status == ERROR_SUCCESS)
  358. {
  359. Rnb->Pending = FALSE;
  360. }
  361. else
  362. {
  363. LARGE_INTEGER DueTime;
  364. ClRtlLogPrint(LOG_UNUSUAL,
  365. "[CP] CppRegNotifyThread CppNotifyCheckpoint due to timer failed, reset the timer.\n");
  366. //
  367. // This checkpoint on timer can fail because the quorum resource is
  368. // not available. This is because we do not sychronoze the quorum
  369. // state change with this timer and it is too inefficient to do so !
  370. DueTime.QuadPart = -10 * 1000 * 1000 * LAZY_CHECKPOINT;
  371. Success = SetWaitableTimer(Rnb->hTimer,
  372. &DueTime,
  373. 0,
  374. NULL,
  375. NULL,
  376. FALSE);
  377. CL_ASSERT(Success);
  378. //Pending remains set to TRUE.
  379. }
  380. }
  381. }
  382. } while ( Group->BlockCount > 0 );
  383. return(ERROR_SUCCESS);
  384. }
  385. DWORD
  386. CppNotifyCheckpoint(
  387. IN PRNB Rnb
  388. )
  389. /*++
  390. Routine Description:
  391. Checkpoints the registry subtree for the specified RNB
  392. Arguments:
  393. Rnb - Supplies the registry notification block to be checkpointed
  394. Return Value:
  395. None
  396. --*/
  397. {
  398. DWORD Status;
  399. Status = CppCheckpoint(Rnb->Resource,
  400. Rnb->hKey,
  401. Rnb->dwId,
  402. Rnb->KeyName);
  403. return(Status);
  404. }
  405. DWORD
  406. CppRundownCheckpoints(
  407. IN PFM_RESOURCE Resource
  408. )
  409. /*++
  410. Routine Description:
  411. Runs down, frees, and removes any registry notification blocks
  412. for the specified resource.
  413. Arguments:
  414. Resource - Supplies the resource
  415. Return Value:
  416. ERROR_SUCCESS if successful
  417. Win32 error code otherwise
  418. --*/
  419. {
  420. PRNB Rnb;
  421. PRNB NextRnb;
  422. PNOTIFY_GROUP Group;
  423. EnterCriticalSection(&CppNotifyLock);
  424. NextRnb = (PRNB)Resource->CheckpointState;
  425. while (NextRnb) {
  426. Rnb = NextRnb;
  427. NextRnb = NextRnb->Next;
  428. ClRtlLogPrint(LOG_NOISE,
  429. "[CP] CppRundownCheckpoints removing RNB for %1!ws!\n",
  430. Rnb->KeyName);
  431. Group = Rnb->NotifyGroup;
  432. //
  433. // Send a command to the group notify thread to remove the RNB.
  434. //
  435. if (Group->BlockCount == 1)
  436. {
  437. //
  438. // Remove this group, it is going to be empty. The worker thread
  439. // will exit after this command
  440. //
  441. ClRtlLogPrint(LOG_NOISE,
  442. "[CP] CppRundownCheckpoints removing empty group\n");
  443. RemoveEntryList(&Group->ListEntry);
  444. //dont wait, the notification thread for this group will automatically
  445. //exit when the block count drops to 0. It cleans up the hCommandEvent
  446. //and the hCompleteEvent on exit, so do not do a waitforsingleobject()
  447. //in this case.
  448. Group->Command = NotifyRemoveRNB;
  449. Group->CommandContext = (ULONG_PTR)Rnb;
  450. SetEvent(Group->hCommandEvent);
  451. //wait for the thread to exit
  452. WaitForSingleObject(Group->hThread, INFINITE);
  453. // Clean up the group structure
  454. CloseHandle(Group->hCommandEvent);
  455. CloseHandle(Group->hCommandComplete);
  456. CloseHandle(Group->hThread);
  457. CsFree(Group);
  458. }
  459. else
  460. {
  461. //the block count is greater than 1, remove the rnb, signal
  462. //the thread and wait
  463. Group->Command = NotifyRemoveRNB;
  464. Group->CommandContext = (ULONG_PTR)Rnb;
  465. SetEvent(Group->hCommandEvent);
  466. WaitForSingleObject(Group->hCommandComplete, INFINITE);
  467. }
  468. //
  469. // Clean up all the allocations and handles in the RNB.
  470. //
  471. CsFree(Rnb->KeyName);
  472. RegCloseKey(Rnb->hKey);
  473. CloseHandle(Rnb->hEvent);
  474. CloseHandle(Rnb->hTimer);
  475. CsFree(Rnb);
  476. }
  477. Resource->CheckpointState = 0;
  478. LeaveCriticalSection(&CppNotifyLock);
  479. return(ERROR_SUCCESS);
  480. }
  481. DWORD
  482. CppRundownCheckpointById(
  483. IN PFM_RESOURCE Resource,
  484. IN DWORD dwId
  485. )
  486. /*++
  487. Routine Description:
  488. Runs down, frees, and removes the registry notification block
  489. for the specified resource and checkpoint ID.
  490. Arguments:
  491. Resource - Supplies the resource
  492. Return Value:
  493. ERROR_SUCCESS if successful
  494. Win32 error code otherwise
  495. --*/
  496. {
  497. PRNB Rnb;
  498. PRNB NextRnb;
  499. PRNB *ppLastRnb;
  500. PNOTIFY_GROUP Group;
  501. EnterCriticalSection(&CppNotifyLock);
  502. NextRnb = (PRNB)Resource->CheckpointState;
  503. ppLastRnb = &((PRNB)Resource->CheckpointState);
  504. while (NextRnb) {
  505. Rnb = NextRnb;
  506. NextRnb = NextRnb->Next;
  507. if (Rnb->dwId == dwId) {
  508. ClRtlLogPrint(LOG_NOISE,
  509. "[CP] CppRundownCheckpointById removing RNB for %1!ws!\n",
  510. Rnb->KeyName);
  511. //remove from the list of checkpoint id's for the resource
  512. *ppLastRnb = NextRnb;
  513. Group = Rnb->NotifyGroup;
  514. //
  515. // Send a command to the group notify thread to remove the RNB.
  516. //
  517. if (Group->BlockCount == 1)
  518. {
  519. //
  520. // Remove this group, it is going to be empty. The worker thread
  521. // will exit after this command
  522. //
  523. ClRtlLogPrint(LOG_NOISE,
  524. "[CP] CppRundownCheckpointById removing empty group\n");
  525. RemoveEntryList(&Group->ListEntry);
  526. //dont wait, the notification thread for this group will automatically
  527. //exit when the block count drops to 0. It cleans up the hCommandEvent
  528. //and the hCompleteEvent on exit, so do not do a waitforsingleobject()
  529. //in this case.
  530. Group->Command = NotifyRemoveRNB;
  531. Group->CommandContext = (ULONG_PTR)Rnb;
  532. SetEvent(Group->hCommandEvent);
  533. //wait for the thread to exit
  534. WaitForSingleObject(Group->hThread, INFINITE);
  535. // Clean up the group structure
  536. CloseHandle(Group->hCommandEvent);
  537. CloseHandle(Group->hCommandComplete);
  538. CloseHandle(Group->hThread);
  539. CsFree(Group);
  540. }
  541. else
  542. {
  543. //the block count is greater than 1, remove the rnb, signal
  544. //the thread and wait
  545. Group->Command = NotifyRemoveRNB;
  546. Group->CommandContext = (ULONG_PTR)Rnb;
  547. SetEvent(Group->hCommandEvent);
  548. WaitForSingleObject(Group->hCommandComplete, INFINITE);
  549. }
  550. //
  551. // Clean up all the allocations and handles in the RNB.
  552. //
  553. CsFree(Rnb->KeyName);
  554. RegCloseKey(Rnb->hKey);
  555. CloseHandle(Rnb->hEvent);
  556. CloseHandle(Rnb->hTimer);
  557. CsFree(Rnb);
  558. LeaveCriticalSection(&CppNotifyLock);
  559. return(ERROR_SUCCESS);
  560. }
  561. ppLastRnb = &Rnb->Next;
  562. }
  563. ClRtlLogPrint(LOG_UNUSUAL,
  564. "[CP] CppRundownCheckpointById - could not find checkpoint %1!d! in resource %2!ws!\n",
  565. dwId,
  566. OmObjectName(Resource));
  567. LeaveCriticalSection(&CppNotifyLock);
  568. return(ERROR_FILE_NOT_FOUND);
  569. }