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 *****************************************************************/
40 CmpDelayCloseDpcRoutine(IN PKDPC Dpc
,
41 IN PVOID DeferredContext
,
42 IN PVOID SystemArgument1
,
43 IN PVOID SystemArgument2
)
46 ASSERT(CmpDelayCloseWorkItemActive
);
48 /* Queue the work item */
49 ExQueueWorkItem(&CmpDelayCloseWorkItem
, DelayedWorkQueue
);
54 CmpDelayCloseWorker(IN PVOID Context
)
56 PCM_DELAYED_CLOSE_ENTRY ListEntry
;
61 ASSERT(CmpDelayCloseWorkItemActive
);
63 /* Lock the registry */
66 /* Acquire the delayed close table lock */
67 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
70 for (i
= 0; i
< (CmpDelayedCloseSize
>> 2); i
++)
72 /* Break out of the loop if there is nothing to process */
73 if (CmpDelayedCloseElements
<= CmpDelayedCloseSize
) break;
76 ASSERT(!IsListEmpty(&CmpDelayedLRUListHead
));
79 ListEntry
= CONTAINING_RECORD(CmpDelayedLRUListHead
.Blink
,
80 CM_DELAYED_CLOSE_ENTRY
,
83 /* Save the ConvKey value of the KCB */
84 ConvKey
= ListEntry
->KeyControlBlock
->ConvKey
;
86 /* Release the delayed close table lock */
87 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
89 /* Acquire the KCB lock */
90 CmpAcquireKcbLockExclusiveByKey(ConvKey
);
92 /* Reacquire the delayed close table lock */
93 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
96 ListEntry
= CONTAINING_RECORD(CmpDelayedLRUListHead
.Blink
,
97 CM_DELAYED_CLOSE_ENTRY
,
100 /* Is the entry we have still the first one? */
101 if (CmpDelayedCloseElements
<= CmpDelayedCloseSize
)
103 /* No, someone already inserted an entry there */
104 CmpReleaseKcbLockByKey(ConvKey
);
108 /* Is it a different entry? */
109 if (ConvKey
!= ListEntry
->KeyControlBlock
->ConvKey
)
111 /* Release the delayed close table lock */
112 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
114 /* Release the KCB lock */
115 CmpReleaseKcbLockByKey(ConvKey
);
117 /* Reacquire the delayed close table lock */
118 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
124 /* Remove it from the end of the list */
126 (PCM_DELAYED_CLOSE_ENTRY
)RemoveTailList(&CmpDelayedLRUListHead
);
128 /* Get the containing entry */
129 ListEntry
= CONTAINING_RECORD(ListEntry
,
130 CM_DELAYED_CLOSE_ENTRY
,
133 /* Process the entry */
134 if ((ListEntry
->KeyControlBlock
->RefCount
) ||
135 (ListEntry
->KeyControlBlock
->DelayedCloseIndex
))
137 /* Add it to the beginning of the list */
138 InsertHeadList(&CmpDelayedLRUListHead
, &ListEntry
->DelayedLRUList
);
140 /* Release the delayed close table lock */
141 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
145 /* Release the delayed close table lock */
146 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
148 /* Zero out the DelayCloseEntry pointer */
149 ListEntry
->KeyControlBlock
->DelayCloseEntry
= NULL
;
151 /* Cleanup the KCB cache */
152 CmpCleanUpKcbCacheWithLock(ListEntry
->KeyControlBlock
, FALSE
);
154 /* Free the delay item */
155 CmpFreeDelayItem(ListEntry
);
157 /* Decrement delayed close elements count */
158 InterlockedDecrement((PLONG
)&CmpDelayedCloseElements
);
161 /* Release the KCB lock */
162 CmpReleaseKcbLockByKey(ConvKey
);
164 /* Reacquire the delayed close table lock */
165 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
168 if (CmpDelayedCloseElements
<= CmpDelayedCloseSize
)
170 /* We're not active anymore */
171 CmpDelayCloseWorkItemActive
= FALSE
;
175 /* We didn't process all things, so reschedule for the next time */
176 CmpArmDelayedCloseTimer();
179 /* Release the delayed close table lock */
180 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
182 /* Unlock the registry */
189 CmpInitializeDelayedCloseTable(VOID
)
192 /* Setup the delayed close lock */
193 KeInitializeGuardedMutex(&CmpDelayedCloseTableLock
);
195 /* Setup the work item */
196 ExInitializeWorkItem(&CmpDelayCloseWorkItem
, CmpDelayCloseWorker
, NULL
);
198 /* Setup the list head */
199 InitializeListHead(&CmpDelayedLRUListHead
);
201 /* Setup the DPC and its timer */
202 KeInitializeDpc(&CmpDelayCloseDpc
, CmpDelayCloseDpcRoutine
, NULL
);
203 KeInitializeTimer(&CmpDelayCloseTimer
);
208 CmpDelayDerefKCBDpcRoutine(IN PKDPC Dpc
,
209 IN PVOID DeferredContext
,
210 IN PVOID SystemArgument1
,
211 IN PVOID SystemArgument2
)
214 ASSERT(CmpDelayDerefKCBWorkItemActive
);
216 /* Queue the work item */
217 ExQueueWorkItem(&CmpDelayDerefKCBWorkItem
, DelayedWorkQueue
);
222 CmpDelayDerefKCBWorker(IN PVOID Context
)
224 PCM_DELAY_DEREF_KCB_ITEM Entry
;
228 ASSERT(CmpDelayDerefKCBWorkItemActive
);
230 /* Lock the registry and and list lock */
232 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
234 /* Check if the list is empty */
235 while (!IsListEmpty(&CmpDelayDerefKCBListHead
))
238 Entry
= (PVOID
)RemoveHeadList(&CmpDelayDerefKCBListHead
);
240 /* We can release the lock now */
241 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
243 /* Now grab the actual entry */
244 Entry
= CONTAINING_RECORD(Entry
, CM_DELAY_DEREF_KCB_ITEM
, ListEntry
);
245 Entry
->ListEntry
.Flink
= Entry
->ListEntry
.Blink
= NULL
;
247 /* Dereference and free */
248 CmpDereferenceKeyControlBlock(Entry
->Kcb
);
249 CmpFreeDelayItem(Entry
);
251 /* Lock the list again */
252 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
256 CmpDelayDerefKCBWorkItemActive
= FALSE
;
257 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
264 CmpInitDelayDerefKCBEngine(VOID
)
266 /* Initialize lock and list */
267 KeInitializeGuardedMutex(&CmpDelayDerefKCBLock
);
268 InitializeListHead(&CmpDelayDerefKCBListHead
);
270 /* Setup the work item */
271 ExInitializeWorkItem(&CmpDelayDerefKCBWorkItem
,
272 CmpDelayDerefKCBWorker
,
275 /* Setup the DPC and timer for it */
276 KeInitializeDpc(&CmpDelayDerefKCBDpc
, CmpDelayDerefKCBDpcRoutine
, NULL
);
277 KeInitializeTimer(&CmpDelayDerefKCBTimer
);
282 CmpDelayDerefKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
284 LONG OldRefCount
, NewRefCount
;
285 LARGE_INTEGER Timeout
;
286 PCM_DELAY_DEREF_KCB_ITEM Entry
;
288 CMTRACE(CM_REFERENCE_DEBUG
,
289 "%s - Dereferencing KCB: %p\n", __FUNCTION__
, Kcb
);
291 /* Get the previous reference count */
292 OldRefCount
= *(PLONG
)&Kcb
->RefCount
;
293 NewRefCount
= OldRefCount
- 1;
294 if (((NewRefCount
& 0xFFFF) > 0) &&
295 (InterlockedCompareExchange((PLONG
)&Kcb
->RefCount
,
297 OldRefCount
) == OldRefCount
))
299 /* KCB still had references, so we're done */
303 /* Allocate a delay item */
304 Entry
= CmpAllocateDelayItem();
310 /* Acquire the delayed deref table lock */
311 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
313 /* Insert the entry into the list */
314 InsertTailList(&CmpDelayDerefKCBListHead
, &Entry
->ListEntry
);
316 /* Check if we need to enable anything */
317 if (!CmpDelayDerefKCBWorkItemActive
)
319 /* Yes, we have no work item, setup the interval */
320 CmpDelayDerefKCBWorkItemActive
= TRUE
;
321 Timeout
.QuadPart
= CmpDelayDerefKCBIntervalInSeconds
* -10000000;
322 KeSetTimer(&CmpDelayDerefKCBTimer
, Timeout
, &CmpDelayDerefKCBDpc
);
325 /* Release the table lock */
326 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
331 CmpArmDelayedCloseTimer(VOID
)
333 LARGE_INTEGER Timeout
;
336 /* Set the worker active */
337 CmpDelayCloseWorkItemActive
= TRUE
;
339 /* Setup the interval */
340 Timeout
.QuadPart
= CmpDelayCloseIntervalInSeconds
* -10000000;
341 KeSetTimer(&CmpDelayCloseTimer
, Timeout
, &CmpDelayCloseDpc
);
346 CmpAddToDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb
,
347 IN BOOLEAN LockHeldExclusively
)
350 ULONG OldRefCount
, NewRefCount
;
351 PCM_DELAYED_CLOSE_ENTRY Entry
;
355 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
356 (CmpTestRegistryLockExclusive() == TRUE
));
358 /* Make sure it's valid */
359 if (Kcb
->DelayedCloseIndex
!= CmpDelayedCloseSize
) ASSERT(FALSE
);
362 ASSERT(Kcb
->RefCount
== 0);
363 ASSERT(IsListEmpty(&Kcb
->KeyBodyListHead
) == TRUE
);
364 for (i
= 0; i
< 4; i
++) ASSERT(Kcb
->KeyBodyArray
[i
] == NULL
);
366 /* Allocate a delay item */
367 Entry
= CmpAllocateDelayItem();
370 /* Cleanup immediately */
371 CmpCleanUpKcbCacheWithLock(Kcb
, LockHeldExclusively
);
376 if (Kcb
->InDelayClose
) ASSERT(FALSE
);
378 /* Get the previous reference count */
379 OldRefCount
= *(PLONG
)&Kcb
->InDelayClose
;
380 ASSERT(OldRefCount
== 0);
382 /* Write the new one */
384 if (InterlockedCompareExchange((PLONG
)&Kcb
->InDelayClose
,
386 OldRefCount
) != OldRefCount
)
392 /* Reset the delayed close index */
393 Kcb
->DelayedCloseIndex
= 0;
395 /* Set up the close entry */
396 Kcb
->DelayCloseEntry
= Entry
;
397 Entry
->KeyControlBlock
= Kcb
;
399 /* Increase the number of elements */
400 InterlockedIncrement((PLONG
)&CmpDelayedCloseElements
);
402 /* Acquire the delayed close table lock */
403 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
405 /* Insert the entry into the list */
406 InsertHeadList(&CmpDelayedLRUListHead
, &Entry
->DelayedLRUList
);
408 /* Check if we need to enable anything */
409 if ((CmpDelayedCloseElements
> CmpDelayedCloseSize
) &&
410 !(CmpDelayCloseWorkItemActive
))
412 /* Yes, we have too many elements to close, and no work item */
413 CmpArmDelayedCloseTimer();
416 /* Release the table lock */
417 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
422 CmpRemoveFromDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb
)
424 PCM_DELAYED_CLOSE_ENTRY Entry
;
425 ULONG NewRefCount
, OldRefCount
;
429 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
430 (CmpTestRegistryLockExclusive() == TRUE
));
431 if (Kcb
->DelayedCloseIndex
== CmpDelayedCloseSize
) ASSERT(FALSE
);
433 /* Get the entry and lock the table */
434 Entry
= Kcb
->DelayCloseEntry
;
436 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
438 /* Remove the entry */
439 RemoveEntryList(&Entry
->DelayedLRUList
);
441 /* Release the lock */
442 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
445 CmpFreeDelayItem(Entry
);
447 /* Reduce the number of elements */
448 InterlockedDecrement((PLONG
)&CmpDelayedCloseElements
);
451 if (!Kcb
->InDelayClose
) ASSERT(FALSE
);
453 /* Get the previous reference count */
454 OldRefCount
= *(PLONG
)&Kcb
->InDelayClose
;
455 ASSERT(OldRefCount
== 1);
457 /* Write the new one */
459 if (InterlockedCompareExchange((PLONG
)&Kcb
->InDelayClose
,
461 OldRefCount
) != OldRefCount
)
467 /* Remove the link to the entry */
468 Kcb
->DelayCloseEntry
= NULL
;
470 /* Set new delay size and remove the delete flag */
471 Kcb
->DelayedCloseIndex
= CmpDelayedCloseSize
;