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 */
188 CmpInitializeDelayedCloseTable(VOID
)
191 /* Setup the delayed close lock */
192 KeInitializeGuardedMutex(&CmpDelayedCloseTableLock
);
194 /* Setup the work item */
195 ExInitializeWorkItem(&CmpDelayCloseWorkItem
, CmpDelayCloseWorker
, NULL
);
197 /* Setup the list head */
198 InitializeListHead(&CmpDelayedLRUListHead
);
200 /* Setup the DPC and its timer */
201 KeInitializeDpc(&CmpDelayCloseDpc
, CmpDelayCloseDpcRoutine
, NULL
);
202 KeInitializeTimer(&CmpDelayCloseTimer
);
207 CmpDelayDerefKCBDpcRoutine(IN PKDPC Dpc
,
208 IN PVOID DeferredContext
,
209 IN PVOID SystemArgument1
,
210 IN PVOID SystemArgument2
)
213 ASSERT(CmpDelayDerefKCBWorkItemActive
);
215 /* Queue the work item */
216 ExQueueWorkItem(&CmpDelayDerefKCBWorkItem
, DelayedWorkQueue
);
221 CmpDelayDerefKCBWorker(IN PVOID Context
)
223 PCM_DELAY_DEREF_KCB_ITEM Entry
;
227 ASSERT(CmpDelayDerefKCBWorkItemActive
);
229 /* Lock the registry and and list lock */
231 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
233 /* Check if the list is empty */
234 while (!IsListEmpty(&CmpDelayDerefKCBListHead
))
237 Entry
= (PVOID
)RemoveHeadList(&CmpDelayDerefKCBListHead
);
239 /* We can release the lock now */
240 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
242 /* Now grab the actual entry */
243 Entry
= CONTAINING_RECORD(Entry
, CM_DELAY_DEREF_KCB_ITEM
, ListEntry
);
244 Entry
->ListEntry
.Flink
= Entry
->ListEntry
.Blink
= NULL
;
246 /* Dereference and free */
247 CmpDereferenceKeyControlBlock(Entry
->Kcb
);
248 CmpFreeDelayItem(Entry
);
250 /* Lock the list again */
251 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
255 CmpDelayDerefKCBWorkItemActive
= FALSE
;
256 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
262 CmpInitDelayDerefKCBEngine(VOID
)
264 /* Initialize lock and list */
265 KeInitializeGuardedMutex(&CmpDelayDerefKCBLock
);
266 InitializeListHead(&CmpDelayDerefKCBListHead
);
268 /* Setup the work item */
269 ExInitializeWorkItem(&CmpDelayDerefKCBWorkItem
,
270 CmpDelayDerefKCBWorker
,
273 /* Setup the DPC and timer for it */
274 KeInitializeDpc(&CmpDelayDerefKCBDpc
, CmpDelayDerefKCBDpcRoutine
, NULL
);
275 KeInitializeTimer(&CmpDelayDerefKCBTimer
);
280 CmpDelayDerefKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
282 LONG OldRefCount
, NewRefCount
;
283 LARGE_INTEGER Timeout
;
284 PCM_DELAY_DEREF_KCB_ITEM Entry
;
286 CMTRACE(CM_REFERENCE_DEBUG
,
287 "%s - Dereferencing KCB: %p\n", __FUNCTION__
, Kcb
);
289 /* Get the previous reference count */
290 OldRefCount
= *(PLONG
)&Kcb
->RefCount
;
291 NewRefCount
= OldRefCount
- 1;
292 if (((NewRefCount
& 0xFFFF) > 0) &&
293 (InterlockedCompareExchange((PLONG
)&Kcb
->RefCount
,
295 OldRefCount
) == OldRefCount
))
297 /* KCB still had references, so we're done */
301 /* Allocate a delay item */
302 Entry
= CmpAllocateDelayItem();
308 /* Acquire the delayed deref table lock */
309 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
311 /* Insert the entry into the list */
312 InsertTailList(&CmpDelayDerefKCBListHead
, &Entry
->ListEntry
);
314 /* Check if we need to enable anything */
315 if (!CmpDelayDerefKCBWorkItemActive
)
317 /* Yes, we have no work item, setup the interval */
318 CmpDelayDerefKCBWorkItemActive
= TRUE
;
319 Timeout
.QuadPart
= CmpDelayDerefKCBIntervalInSeconds
* -10000000;
320 KeSetTimer(&CmpDelayDerefKCBTimer
, Timeout
, &CmpDelayDerefKCBDpc
);
323 /* Release the table lock */
324 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
329 CmpArmDelayedCloseTimer(VOID
)
331 LARGE_INTEGER Timeout
;
334 /* Set the worker active */
335 CmpDelayCloseWorkItemActive
= TRUE
;
337 /* Setup the interval */
338 Timeout
.QuadPart
= CmpDelayCloseIntervalInSeconds
* -10000000;
339 KeSetTimer(&CmpDelayCloseTimer
, Timeout
, &CmpDelayCloseDpc
);
344 CmpAddToDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb
,
345 IN BOOLEAN LockHeldExclusively
)
348 ULONG OldRefCount
, NewRefCount
;
349 PCM_DELAYED_CLOSE_ENTRY Entry
;
353 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
354 (CmpTestRegistryLockExclusive() == TRUE
));
356 /* Make sure it's valid */
357 if (Kcb
->DelayedCloseIndex
!= CmpDelayedCloseSize
) ASSERT(FALSE
);
360 ASSERT(Kcb
->RefCount
== 0);
361 ASSERT(IsListEmpty(&Kcb
->KeyBodyListHead
) == TRUE
);
362 for (i
= 0; i
< 4; i
++) ASSERT(Kcb
->KeyBodyArray
[i
] == NULL
);
364 /* Allocate a delay item */
365 Entry
= CmpAllocateDelayItem();
368 /* Cleanup immediately */
369 CmpCleanUpKcbCacheWithLock(Kcb
, LockHeldExclusively
);
374 if (Kcb
->InDelayClose
) ASSERT(FALSE
);
376 /* Get the previous reference count */
377 OldRefCount
= *(PLONG
)&Kcb
->InDelayClose
;
378 ASSERT(OldRefCount
== 0);
380 /* Write the new one */
382 if (InterlockedCompareExchange((PLONG
)&Kcb
->InDelayClose
,
384 OldRefCount
) != OldRefCount
)
390 /* Reset the delayed close index */
391 Kcb
->DelayedCloseIndex
= 0;
393 /* Set up the close entry */
394 Kcb
->DelayCloseEntry
= Entry
;
395 Entry
->KeyControlBlock
= Kcb
;
397 /* Increase the number of elements */
398 InterlockedIncrement((PLONG
)&CmpDelayedCloseElements
);
400 /* Acquire the delayed close table lock */
401 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
403 /* Insert the entry into the list */
404 InsertHeadList(&CmpDelayedLRUListHead
, &Entry
->DelayedLRUList
);
406 /* Check if we need to enable anything */
407 if ((CmpDelayedCloseElements
> CmpDelayedCloseSize
) &&
408 !(CmpDelayCloseWorkItemActive
))
410 /* Yes, we have too many elements to close, and no work item */
411 CmpArmDelayedCloseTimer();
414 /* Release the table lock */
415 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
420 CmpRemoveFromDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb
)
422 PCM_DELAYED_CLOSE_ENTRY Entry
;
423 ULONG NewRefCount
, OldRefCount
;
427 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
428 (CmpTestRegistryLockExclusive() == TRUE
));
429 if (Kcb
->DelayedCloseIndex
== CmpDelayedCloseSize
) ASSERT(FALSE
);
431 /* Get the entry and lock the table */
432 Entry
= Kcb
->DelayCloseEntry
;
434 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
436 /* Remove the entry */
437 RemoveEntryList(&Entry
->DelayedLRUList
);
439 /* Release the lock */
440 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
443 CmpFreeDelayItem(Entry
);
445 /* Reduce the number of elements */
446 InterlockedDecrement((PLONG
)&CmpDelayedCloseElements
);
449 if (!Kcb
->InDelayClose
) ASSERT(FALSE
);
451 /* Get the previous reference count */
452 OldRefCount
= *(PLONG
)&Kcb
->InDelayClose
;
453 ASSERT(OldRefCount
== 1);
455 /* Write the new one */
457 if (InterlockedCompareExchange((PLONG
)&Kcb
->InDelayClose
,
459 OldRefCount
) != OldRefCount
)
465 /* Remove the link to the entry */
466 Kcb
->DelayCloseEntry
= NULL
;
468 /* Set new delay size and remove the delete flag */
469 Kcb
->DelayedCloseIndex
= CmpDelayedCloseSize
;