Leaked source code of windows server 2003
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.

699 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. CloseHandle ( Group->hCommandComplete );
  163. CloseHandle ( Group->hCommandEvent );
  164. LocalFree(Group);
  165. goto error_exit2;
  166. }
  167. Group->hThread = hThread;
  168. InsertHeadList(&CpNotifyListHead, &Group->ListEntry);
  169. }
  170. //
  171. // Wake up the notify thread to insert the RNB for us.
  172. //
  173. Block->NotifyGroup = Group;
  174. Group->Command = NotifyAddRNB;
  175. Group->CommandContext = (ULONG_PTR)Block;
  176. SetEvent(Group->hCommandEvent);
  177. WaitForSingleObject(Group->hCommandComplete, INFINITE);
  178. Block->Next = (PRNB)Resource->CheckpointState;
  179. Resource->CheckpointState = Block;
  180. LeaveCriticalSection(&CppNotifyLock);
  181. return(ERROR_SUCCESS);
  182. error_exit2:
  183. LeaveCriticalSection(&CppNotifyLock);
  184. RegCloseKey(Block->hKey);
  185. CloseHandle(Block->hTimer);
  186. CloseHandle(Block->hEvent);
  187. CsFree(Block->KeyName);
  188. CsFree(Block);
  189. return(Status);
  190. }
  191. DWORD
  192. CppRegNotifyThread(
  193. IN PNOTIFY_GROUP Group
  194. )
  195. /*++
  196. Routine Description:
  197. Worker thread that handles multiple registry notification subtrees.
  198. Arguments:
  199. Group - Supplies the NOTIFY_GROUP control structure owned by this thread.
  200. Return Value:
  201. None.
  202. --*/
  203. {
  204. PRNB Rnb;
  205. DWORD Signalled;
  206. DWORD Index;
  207. BOOL Success;
  208. DWORD Slot;
  209. DWORD Status;
  210. Group->BlockCount = 0;
  211. Group->WaitArray[0] = Group->hCommandEvent;
  212. do {
  213. Signalled = WaitForMultipleObjects(Group->BlockCount*2 + 1,
  214. Group->WaitArray,
  215. FALSE,
  216. INFINITE);
  217. if (Signalled == Group->BlockCount*2) {
  218. switch (Group->Command) {
  219. case NotifyAddRNB:
  220. //
  221. // Add this notify block to our list.
  222. //
  223. CL_ASSERT(Group->BlockCount < MAX_BLOCKS_PER_GROUP);
  224. Rnb = (PRNB)Group->CommandContext;
  225. Status = RegNotifyChangeKeyValue(Rnb->hKey,
  226. TRUE,
  227. REG_LEGAL_CHANGE_FILTER,
  228. Rnb->hEvent,
  229. TRUE);
  230. if (Status != ERROR_SUCCESS) {
  231. CL_UNEXPECTED_ERROR(Status);
  232. }
  233. Index = Group->BlockCount*2;
  234. Group->WaitArray[Index] = Rnb->hEvent;
  235. Group->WaitArray[Index+1] = Rnb->hTimer;
  236. Rnb->NotifySlot = Group->BlockCount;
  237. Group->NotifyBlock[Rnb->NotifySlot] = Rnb;
  238. ++Group->BlockCount;
  239. Group->WaitArray[Group->BlockCount*2] = Group->hCommandEvent;
  240. break;
  241. case NotifyRemoveRNB:
  242. Rnb = (PRNB)Group->CommandContext;
  243. //
  244. // Check to see if the RNB is pending. If so, checkpoint it
  245. // now before we remove it.
  246. //
  247. if (Rnb->Pending) {
  248. DWORD Count = 60;
  249. ClRtlLogPrint(LOG_NOISE,
  250. "[CP] CppRegNotifyThread checkpointing key %1!ws! to id %2!d! due to removal while pending\n",
  251. Rnb->KeyName,
  252. Rnb->dwId);
  253. RetryCheckpoint:
  254. Status = CppNotifyCheckpoint(Rnb);
  255. if (Status != ERROR_SUCCESS)
  256. {
  257. WCHAR string[16];
  258. ClRtlLogPrint(LOG_CRITICAL,
  259. "[CP] CppRegNotifyThread, CppNotifyCheckpoint failed with Status=%1!u!\n",
  260. Status);
  261. if ((Status == ERROR_ACCESS_DENIED) ||
  262. (Status == ERROR_INVALID_FUNCTION) ||
  263. (Status == ERROR_NOT_READY) ||
  264. (Status == RPC_X_INVALID_PIPE_OPERATION) ||
  265. (Status == ERROR_BUSY) ||
  266. (Status == ERROR_SWAPERROR))
  267. {
  268. //SS: we should retry forever??
  269. //SS: Since we allow the quorum to come
  270. //offline after 30 seconds of waiting on
  271. //pending resources, the checkpointing should
  272. //be able to succeed
  273. if (Count--)
  274. {
  275. Sleep(1000);
  276. goto RetryCheckpoint;
  277. }
  278. }
  279. #if DBG
  280. if (IsDebuggerPresent())
  281. DebugBreak();
  282. #endif
  283. wsprintfW(&(string[0]), L"%u", Status);
  284. CL_LOGCLUSERROR2(CP_SAVE_REGISTRY_FAILURE, Rnb->KeyName, string);
  285. }
  286. // irrespective of the failure set pending to FALSE
  287. Rnb->Pending = FALSE;
  288. }
  289. //
  290. // Move everything down to take the previous RNB's slot.
  291. //
  292. Index = Rnb->NotifySlot * 2 ;
  293. Group->BlockCount--;
  294. for (Slot = Rnb->NotifySlot; Slot < Group->BlockCount; Slot++) {
  295. Group->NotifyBlock[Slot] = Group->NotifyBlock[Slot+1];
  296. Group->NotifyBlock[Slot]->NotifySlot--;
  297. Group->WaitArray[Index] = Group->WaitArray[Index+2];
  298. Group->WaitArray[Index+1] = Group->WaitArray[Index+3];
  299. Index += 2;
  300. }
  301. Group->WaitArray[Index] = NULL;
  302. Group->WaitArray[Index+1] = NULL;
  303. Group->WaitArray[Index+2] = NULL;
  304. Group->NotifyBlock[Group->BlockCount] = NULL;
  305. Group->WaitArray[Group->BlockCount*2] = Group->hCommandEvent;
  306. break;
  307. default:
  308. CL_UNEXPECTED_ERROR( Group->Command );
  309. break;
  310. }
  311. SetEvent(Group->hCommandComplete);
  312. } else {
  313. //
  314. // Either a registry notification or a timer has fired.
  315. // Process this.
  316. //
  317. Rnb = Group->NotifyBlock[(Signalled)/2];
  318. if (!(Signalled & 1)) {
  319. LARGE_INTEGER DueTime;
  320. //
  321. // This is a registry notification.
  322. // All we do for registry notifications is set the timer, issue
  323. // the RegNotify again, mark the RNB as pending, and rewait.
  324. //
  325. DueTime.QuadPart = -10 * 1000 * 1000 * LAZY_CHECKPOINT;
  326. Success = SetWaitableTimer(Rnb->hTimer,
  327. &DueTime,
  328. 0,
  329. NULL,
  330. NULL,
  331. FALSE);
  332. CL_ASSERT(Success);
  333. Status = RegNotifyChangeKeyValue(Rnb->hKey,
  334. TRUE,
  335. REG_LEGAL_CHANGE_FILTER,
  336. Rnb->hEvent,
  337. TRUE);
  338. if (Status != ERROR_SUCCESS) {
  339. ClRtlLogPrint(LOG_CRITICAL,
  340. "[CP] CppRegNotifyThread - error %1!d! attempting to reregister notification for %2!ws!\n",
  341. Status,
  342. Rnb->KeyName);
  343. }
  344. //
  345. // Mark it pending so if someone tries to remove it we know that
  346. // we should checkpoint first.
  347. //
  348. Rnb->Pending = TRUE;
  349. } else {
  350. //
  351. // This must be a timer firing
  352. //
  353. CL_ASSERT(Rnb->Pending);
  354. ClRtlLogPrint(LOG_NOISE,
  355. "[CP] CppRegNotifyThread checkpointing key %1!ws! to id %2!d! due to timer\n",
  356. Rnb->KeyName,
  357. Rnb->dwId);
  358. Status = CppNotifyCheckpoint(Rnb);
  359. if (Status == ERROR_SUCCESS)
  360. {
  361. Rnb->Pending = FALSE;
  362. }
  363. else
  364. {
  365. LARGE_INTEGER DueTime;
  366. ClRtlLogPrint(LOG_UNUSUAL,
  367. "[CP] CppRegNotifyThread CppNotifyCheckpoint due to timer failed, reset the timer.\n");
  368. //
  369. // This checkpoint on timer can fail because the quorum resource is
  370. // not available. This is because we do not sychronoze the quorum
  371. // state change with this timer and it is too inefficient to do so !
  372. DueTime.QuadPart = -10 * 1000 * 1000 * LAZY_CHECKPOINT;
  373. Success = SetWaitableTimer(Rnb->hTimer,
  374. &DueTime,
  375. 0,
  376. NULL,
  377. NULL,
  378. FALSE);
  379. CL_ASSERT(Success);
  380. //Pending remains set to TRUE.
  381. }
  382. }
  383. }
  384. } while ( Group->BlockCount > 0 );
  385. return(ERROR_SUCCESS);
  386. }
  387. DWORD
  388. CppNotifyCheckpoint(
  389. IN PRNB Rnb
  390. )
  391. /*++
  392. Routine Description:
  393. Checkpoints the registry subtree for the specified RNB
  394. Arguments:
  395. Rnb - Supplies the registry notification block to be checkpointed
  396. Return Value:
  397. None
  398. --*/
  399. {
  400. DWORD Status;
  401. Status = CppCheckpoint(Rnb->Resource,
  402. Rnb->hKey,
  403. Rnb->dwId,
  404. Rnb->KeyName);
  405. return(Status);
  406. }
  407. DWORD
  408. CppRundownCheckpoints(
  409. IN PFM_RESOURCE Resource
  410. )
  411. /*++
  412. Routine Description:
  413. Runs down, frees, and removes any registry notification blocks
  414. for the specified resource.
  415. Arguments:
  416. Resource - Supplies the resource
  417. Return Value:
  418. ERROR_SUCCESS if successful
  419. Win32 error code otherwise
  420. --*/
  421. {
  422. PRNB Rnb;
  423. PRNB NextRnb;
  424. PNOTIFY_GROUP Group;
  425. EnterCriticalSection(&CppNotifyLock);
  426. NextRnb = (PRNB)Resource->CheckpointState;
  427. while (NextRnb) {
  428. Rnb = NextRnb;
  429. NextRnb = NextRnb->Next;
  430. ClRtlLogPrint(LOG_NOISE,
  431. "[CP] CppRundownCheckpoints removing RNB for %1!ws!\n",
  432. Rnb->KeyName);
  433. Group = Rnb->NotifyGroup;
  434. //
  435. // Send a command to the group notify thread to remove the RNB.
  436. //
  437. if (Group->BlockCount == 1)
  438. {
  439. //
  440. // Remove this group, it is going to be empty. The worker thread
  441. // will exit after this command
  442. //
  443. ClRtlLogPrint(LOG_NOISE,
  444. "[CP] CppRundownCheckpoints removing empty group\n");
  445. RemoveEntryList(&Group->ListEntry);
  446. //dont wait, the notification thread for this group will automatically
  447. //exit when the block count drops to 0. It cleans up the hCommandEvent
  448. //and the hCompleteEvent on exit, so do not do a waitforsingleobject()
  449. //in this case.
  450. Group->Command = NotifyRemoveRNB;
  451. Group->CommandContext = (ULONG_PTR)Rnb;
  452. SetEvent(Group->hCommandEvent);
  453. //wait for the thread to exit
  454. WaitForSingleObject(Group->hThread, INFINITE);
  455. // Clean up the group structure
  456. CloseHandle(Group->hCommandEvent);
  457. CloseHandle(Group->hCommandComplete);
  458. CloseHandle(Group->hThread);
  459. CsFree(Group);
  460. }
  461. else
  462. {
  463. //the block count is greater than 1, remove the rnb, signal
  464. //the thread and wait
  465. Group->Command = NotifyRemoveRNB;
  466. Group->CommandContext = (ULONG_PTR)Rnb;
  467. SetEvent(Group->hCommandEvent);
  468. WaitForSingleObject(Group->hCommandComplete, INFINITE);
  469. }
  470. //
  471. // Clean up all the allocations and handles in the RNB.
  472. //
  473. CsFree(Rnb->KeyName);
  474. RegCloseKey(Rnb->hKey);
  475. CloseHandle(Rnb->hEvent);
  476. CloseHandle(Rnb->hTimer);
  477. CsFree(Rnb);
  478. }
  479. Resource->CheckpointState = 0;
  480. LeaveCriticalSection(&CppNotifyLock);
  481. return(ERROR_SUCCESS);
  482. }
  483. DWORD
  484. CppRundownCheckpointById(
  485. IN PFM_RESOURCE Resource,
  486. IN DWORD dwId
  487. )
  488. /*++
  489. Routine Description:
  490. Runs down, frees, and removes the registry notification block
  491. for the specified resource and checkpoint ID.
  492. Arguments:
  493. Resource - Supplies the resource
  494. Return Value:
  495. ERROR_SUCCESS if successful
  496. Win32 error code otherwise
  497. --*/
  498. {
  499. PRNB Rnb;
  500. PRNB NextRnb;
  501. PRNB *ppLastRnb;
  502. PNOTIFY_GROUP Group;
  503. EnterCriticalSection(&CppNotifyLock);
  504. NextRnb = (PRNB)Resource->CheckpointState;
  505. ppLastRnb = &((PRNB)Resource->CheckpointState);
  506. while (NextRnb) {
  507. Rnb = NextRnb;
  508. NextRnb = NextRnb->Next;
  509. if (Rnb->dwId == dwId) {
  510. ClRtlLogPrint(LOG_NOISE,
  511. "[CP] CppRundownCheckpointById removing RNB for %1!ws!\n",
  512. Rnb->KeyName);
  513. //remove from the list of checkpoint id's for the resource
  514. *ppLastRnb = NextRnb;
  515. Group = Rnb->NotifyGroup;
  516. //
  517. // Send a command to the group notify thread to remove the RNB.
  518. //
  519. if (Group->BlockCount == 1)
  520. {
  521. //
  522. // Remove this group, it is going to be empty. The worker thread
  523. // will exit after this command
  524. //
  525. ClRtlLogPrint(LOG_NOISE,
  526. "[CP] CppRundownCheckpointById removing empty group\n");
  527. RemoveEntryList(&Group->ListEntry);
  528. //dont wait, the notification thread for this group will automatically
  529. //exit when the block count drops to 0. It cleans up the hCommandEvent
  530. //and the hCompleteEvent on exit, so do not do a waitforsingleobject()
  531. //in this case.
  532. Group->Command = NotifyRemoveRNB;
  533. Group->CommandContext = (ULONG_PTR)Rnb;
  534. SetEvent(Group->hCommandEvent);
  535. //wait for the thread to exit
  536. WaitForSingleObject(Group->hThread, INFINITE);
  537. // Clean up the group structure
  538. CloseHandle(Group->hCommandEvent);
  539. CloseHandle(Group->hCommandComplete);
  540. CloseHandle(Group->hThread);
  541. CsFree(Group);
  542. }
  543. else
  544. {
  545. //the block count is greater than 1, remove the rnb, signal
  546. //the thread and wait
  547. Group->Command = NotifyRemoveRNB;
  548. Group->CommandContext = (ULONG_PTR)Rnb;
  549. SetEvent(Group->hCommandEvent);
  550. WaitForSingleObject(Group->hCommandComplete, INFINITE);
  551. }
  552. //
  553. // Clean up all the allocations and handles in the RNB.
  554. //
  555. CsFree(Rnb->KeyName);
  556. RegCloseKey(Rnb->hKey);
  557. CloseHandle(Rnb->hEvent);
  558. CloseHandle(Rnb->hTimer);
  559. CsFree(Rnb);
  560. LeaveCriticalSection(&CppNotifyLock);
  561. return(ERROR_SUCCESS);
  562. }
  563. ppLastRnb = &Rnb->Next;
  564. }
  565. ClRtlLogPrint(LOG_UNUSUAL,
  566. "[CP] CppRundownCheckpointById - could not find checkpoint %1!d! in resource %2!ws!\n",
  567. dwId,
  568. OmObjectName(Resource));
  569. LeaveCriticalSection(&CppNotifyLock);
  570. return(ERROR_FILE_NOT_FOUND);
  571. }