2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmdelay.c
5 * PURPOSE: Routines for handling delay close and allocate.
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
17 WORK_QUEUE_ITEM CmpDelayDerefKCBWorkItem
;
19 ULONG CmpDelayedCloseSize
= 2048;
20 ULONG CmpDelayedCloseElements
;
21 KGUARDED_MUTEX CmpDelayedCloseTableLock
;
22 BOOLEAN CmpDelayCloseWorkItemActive
;
23 WORK_QUEUE_ITEM CmpDelayCloseWorkItem
;
24 LIST_ENTRY CmpDelayedLRUListHead
;
25 ULONG CmpDelayCloseIntervalInSeconds
= 5;
26 KDPC CmpDelayCloseDpc
;
27 KTIMER CmpDelayCloseTimer
;
29 KGUARDED_MUTEX CmpDelayDerefKCBLock
;
30 BOOLEAN CmpDelayDerefKCBWorkItemActive
;
31 LIST_ENTRY CmpDelayDerefKCBListHead
;
32 ULONG CmpDelayDerefKCBIntervalInSeconds
= 5;
33 KDPC CmpDelayDerefKCBDpc
;
34 KTIMER CmpDelayDerefKCBTimer
;
36 /* FUNCTIONS *****************************************************************/
38 _Function_class_(KDEFERRED_ROUTINE
)
41 CmpDelayCloseDpcRoutine(IN PKDPC Dpc
,
42 IN PVOID DeferredContext
,
43 IN PVOID SystemArgument1
,
44 IN PVOID SystemArgument2
)
47 ASSERT(CmpDelayCloseWorkItemActive
);
49 /* Queue the work item */
50 ExQueueWorkItem(&CmpDelayCloseWorkItem
, DelayedWorkQueue
);
53 _Function_class_(WORKER_THREAD_ROUTINE
)
56 CmpDelayCloseWorker(IN PVOID Context
)
58 PCM_DELAYED_CLOSE_ENTRY ListEntry
;
63 ASSERT(CmpDelayCloseWorkItemActive
);
65 /* Lock the registry */
68 /* Acquire the delayed close table lock */
69 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
72 for (i
= 0; i
< (CmpDelayedCloseSize
>> 2); i
++)
74 /* Break out of the loop if there is nothing to process */
75 if (CmpDelayedCloseElements
<= CmpDelayedCloseSize
) break;
78 ASSERT(!IsListEmpty(&CmpDelayedLRUListHead
));
81 ListEntry
= CONTAINING_RECORD(CmpDelayedLRUListHead
.Blink
,
82 CM_DELAYED_CLOSE_ENTRY
,
85 /* Save the ConvKey value of the KCB */
86 ConvKey
= ListEntry
->KeyControlBlock
->ConvKey
;
88 /* Release the delayed close table lock */
89 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
91 /* Acquire the KCB lock */
92 CmpAcquireKcbLockExclusiveByKey(ConvKey
);
94 /* Reacquire the delayed close table lock */
95 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
98 ListEntry
= CONTAINING_RECORD(CmpDelayedLRUListHead
.Blink
,
99 CM_DELAYED_CLOSE_ENTRY
,
102 /* Is the entry we have still the first one? */
103 if (CmpDelayedCloseElements
<= CmpDelayedCloseSize
)
105 /* No, someone already inserted an entry there */
106 CmpReleaseKcbLockByKey(ConvKey
);
110 /* Is it a different entry? */
111 if (ConvKey
!= ListEntry
->KeyControlBlock
->ConvKey
)
113 /* Release the delayed close table lock */
114 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
116 /* Release the KCB lock */
117 CmpReleaseKcbLockByKey(ConvKey
);
119 /* Reacquire the delayed close table lock */
120 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
126 /* Remove it from the end of the list */
128 (PCM_DELAYED_CLOSE_ENTRY
)RemoveTailList(&CmpDelayedLRUListHead
);
130 /* Get the containing entry */
131 ListEntry
= CONTAINING_RECORD(ListEntry
,
132 CM_DELAYED_CLOSE_ENTRY
,
135 /* Process the entry */
136 if ((ListEntry
->KeyControlBlock
->RefCount
) ||
137 (ListEntry
->KeyControlBlock
->DelayedCloseIndex
))
139 /* Add it to the beginning of the list */
140 InsertHeadList(&CmpDelayedLRUListHead
, &ListEntry
->DelayedLRUList
);
142 /* Release the delayed close table lock */
143 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
147 /* Release the delayed close table lock */
148 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
150 /* Zero out the DelayCloseEntry pointer */
151 ListEntry
->KeyControlBlock
->DelayCloseEntry
= NULL
;
153 /* Cleanup the KCB cache */
154 CmpCleanUpKcbCacheWithLock(ListEntry
->KeyControlBlock
, FALSE
);
156 /* Free the delay item */
157 CmpFreeDelayItem(ListEntry
);
159 /* Decrement delayed close elements count */
160 InterlockedDecrement((PLONG
)&CmpDelayedCloseElements
);
163 /* Release the KCB lock */
164 CmpReleaseKcbLockByKey(ConvKey
);
166 /* Reacquire the delayed close table lock */
167 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
170 if (CmpDelayedCloseElements
<= CmpDelayedCloseSize
)
172 /* We're not active anymore */
173 CmpDelayCloseWorkItemActive
= FALSE
;
177 /* We didn't process all things, so reschedule for the next time */
178 CmpArmDelayedCloseTimer();
181 /* Release the delayed close table lock */
182 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
184 /* Unlock the registry */
191 CmpInitializeDelayedCloseTable(VOID
)
194 /* Setup the delayed close lock */
195 KeInitializeGuardedMutex(&CmpDelayedCloseTableLock
);
197 /* Setup the work item */
198 ExInitializeWorkItem(&CmpDelayCloseWorkItem
, CmpDelayCloseWorker
, NULL
);
200 /* Setup the list head */
201 InitializeListHead(&CmpDelayedLRUListHead
);
203 /* Setup the DPC and its timer */
204 KeInitializeDpc(&CmpDelayCloseDpc
, CmpDelayCloseDpcRoutine
, NULL
);
205 KeInitializeTimer(&CmpDelayCloseTimer
);
208 _Function_class_(KDEFERRED_ROUTINE
)
211 CmpDelayDerefKCBDpcRoutine(IN PKDPC Dpc
,
212 IN PVOID DeferredContext
,
213 IN PVOID SystemArgument1
,
214 IN PVOID SystemArgument2
)
217 ASSERT(CmpDelayDerefKCBWorkItemActive
);
219 /* Queue the work item */
220 ExQueueWorkItem(&CmpDelayDerefKCBWorkItem
, DelayedWorkQueue
);
223 _Function_class_(WORKER_THREAD_ROUTINE
)
226 CmpDelayDerefKCBWorker(IN PVOID Context
)
228 PCM_DELAY_DEREF_KCB_ITEM Entry
;
232 ASSERT(CmpDelayDerefKCBWorkItemActive
);
234 /* Lock the registry and and list lock */
236 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
238 /* Check if the list is empty */
239 while (!IsListEmpty(&CmpDelayDerefKCBListHead
))
242 Entry
= (PVOID
)RemoveHeadList(&CmpDelayDerefKCBListHead
);
244 /* We can release the lock now */
245 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
247 /* Now grab the actual entry */
248 Entry
= CONTAINING_RECORD(Entry
, CM_DELAY_DEREF_KCB_ITEM
, ListEntry
);
249 Entry
->ListEntry
.Flink
= Entry
->ListEntry
.Blink
= NULL
;
251 /* Dereference and free */
252 CmpDereferenceKeyControlBlock(Entry
->Kcb
);
253 CmpFreeDelayItem(Entry
);
255 /* Lock the list again */
256 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
260 CmpDelayDerefKCBWorkItemActive
= FALSE
;
261 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
268 CmpInitDelayDerefKCBEngine(VOID
)
270 /* Initialize lock and list */
271 KeInitializeGuardedMutex(&CmpDelayDerefKCBLock
);
272 InitializeListHead(&CmpDelayDerefKCBListHead
);
274 /* Setup the work item */
275 ExInitializeWorkItem(&CmpDelayDerefKCBWorkItem
,
276 CmpDelayDerefKCBWorker
,
279 /* Setup the DPC and timer for it */
280 KeInitializeDpc(&CmpDelayDerefKCBDpc
, CmpDelayDerefKCBDpcRoutine
, NULL
);
281 KeInitializeTimer(&CmpDelayDerefKCBTimer
);
286 CmpDelayDerefKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
288 LONG OldRefCount
, NewRefCount
;
289 LARGE_INTEGER Timeout
;
290 PCM_DELAY_DEREF_KCB_ITEM Entry
;
292 CMTRACE(CM_REFERENCE_DEBUG
,
293 "%s - Dereferencing KCB: %p\n", __FUNCTION__
, Kcb
);
295 /* Get the previous reference count */
296 OldRefCount
= *(PLONG
)&Kcb
->RefCount
;
297 NewRefCount
= OldRefCount
- 1;
298 if (((NewRefCount
& 0xFFFF) > 0) &&
299 (InterlockedCompareExchange((PLONG
)&Kcb
->RefCount
,
301 OldRefCount
) == OldRefCount
))
303 /* KCB still had references, so we're done */
307 /* Allocate a delay item */
308 Entry
= CmpAllocateDelayItem();
314 /* Acquire the delayed deref table lock */
315 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
317 /* Insert the entry into the list */
318 InsertTailList(&CmpDelayDerefKCBListHead
, &Entry
->ListEntry
);
320 /* Check if we need to enable anything */
321 if (!CmpDelayDerefKCBWorkItemActive
)
323 /* Yes, we have no work item, setup the interval */
324 CmpDelayDerefKCBWorkItemActive
= TRUE
;
325 Timeout
.QuadPart
= CmpDelayDerefKCBIntervalInSeconds
* -10000000;
326 KeSetTimer(&CmpDelayDerefKCBTimer
, Timeout
, &CmpDelayDerefKCBDpc
);
329 /* Release the table lock */
330 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
335 CmpArmDelayedCloseTimer(VOID
)
337 LARGE_INTEGER Timeout
;
340 /* Set the worker active */
341 CmpDelayCloseWorkItemActive
= TRUE
;
343 /* Setup the interval */
344 Timeout
.QuadPart
= CmpDelayCloseIntervalInSeconds
* -10000000;
345 KeSetTimer(&CmpDelayCloseTimer
, Timeout
, &CmpDelayCloseDpc
);
350 CmpAddToDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb
,
351 IN BOOLEAN LockHeldExclusively
)
354 ULONG OldRefCount
, NewRefCount
;
355 PCM_DELAYED_CLOSE_ENTRY Entry
;
359 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
360 (CmpTestRegistryLockExclusive() == TRUE
));
362 /* Make sure it's valid */
363 if (Kcb
->DelayedCloseIndex
!= CmpDelayedCloseSize
) ASSERT(FALSE
);
366 ASSERT(Kcb
->RefCount
== 0);
367 ASSERT(IsListEmpty(&Kcb
->KeyBodyListHead
) == TRUE
);
368 for (i
= 0; i
< 4; i
++) ASSERT(Kcb
->KeyBodyArray
[i
] == NULL
);
370 /* Allocate a delay item */
371 Entry
= CmpAllocateDelayItem();
374 /* Cleanup immediately */
375 CmpCleanUpKcbCacheWithLock(Kcb
, LockHeldExclusively
);
380 if (Kcb
->InDelayClose
) ASSERT(FALSE
);
382 /* Get the previous reference count */
383 OldRefCount
= *(PLONG
)&Kcb
->InDelayClose
;
384 ASSERT(OldRefCount
== 0);
386 /* Write the new one */
388 if (InterlockedCompareExchange((PLONG
)&Kcb
->InDelayClose
,
390 OldRefCount
) != OldRefCount
)
396 /* Reset the delayed close index */
397 Kcb
->DelayedCloseIndex
= 0;
399 /* Set up the close entry */
400 Kcb
->DelayCloseEntry
= Entry
;
401 Entry
->KeyControlBlock
= Kcb
;
403 /* Increase the number of elements */
404 InterlockedIncrement((PLONG
)&CmpDelayedCloseElements
);
406 /* Acquire the delayed close table lock */
407 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
409 /* Insert the entry into the list */
410 InsertHeadList(&CmpDelayedLRUListHead
, &Entry
->DelayedLRUList
);
412 /* Check if we need to enable anything */
413 if ((CmpDelayedCloseElements
> CmpDelayedCloseSize
) &&
414 !(CmpDelayCloseWorkItemActive
))
416 /* Yes, we have too many elements to close, and no work item */
417 CmpArmDelayedCloseTimer();
420 /* Release the table lock */
421 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
426 CmpRemoveFromDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb
)
428 PCM_DELAYED_CLOSE_ENTRY Entry
;
429 ULONG NewRefCount
, OldRefCount
;
433 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
434 (CmpTestRegistryLockExclusive() == TRUE
));
435 if (Kcb
->DelayedCloseIndex
== CmpDelayedCloseSize
) ASSERT(FALSE
);
437 /* Get the entry and lock the table */
438 Entry
= Kcb
->DelayCloseEntry
;
440 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
442 /* Remove the entry */
443 RemoveEntryList(&Entry
->DelayedLRUList
);
445 /* Release the lock */
446 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
449 CmpFreeDelayItem(Entry
);
451 /* Reduce the number of elements */
452 InterlockedDecrement((PLONG
)&CmpDelayedCloseElements
);
455 if (!Kcb
->InDelayClose
) ASSERT(FALSE
);
457 /* Get the previous reference count */
458 OldRefCount
= *(PLONG
)&Kcb
->InDelayClose
;
459 ASSERT(OldRefCount
== 1);
461 /* Write the new one */
463 if (InterlockedCompareExchange((PLONG
)&Kcb
->InDelayClose
,
465 OldRefCount
) != OldRefCount
)
471 /* Remove the link to the entry */
472 Kcb
->DelayCloseEntry
= NULL
;
474 /* Set new delay size and remove the delete flag */
475 Kcb
->DelayedCloseIndex
= CmpDelayedCloseSize
;