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
;
18 LIST_ENTRY CmpFreeDelayItemsListHead
;
20 ULONG CmpDelayedCloseSize
= 2048;
21 ULONG CmpDelayedCloseElements
;
22 KGUARDED_MUTEX CmpDelayedCloseTableLock
;
23 BOOLEAN CmpDelayCloseWorkItemActive
;
24 WORK_QUEUE_ITEM CmpDelayCloseWorkItem
;
25 LIST_ENTRY CmpDelayedLRUListHead
;
26 ULONG CmpDelayCloseIntervalInSeconds
= 5;
27 KDPC CmpDelayCloseDpc
;
28 KTIMER CmpDelayCloseTimer
;
30 KGUARDED_MUTEX CmpDelayDerefKCBLock
;
31 BOOLEAN CmpDelayDerefKCBWorkItemActive
;
32 LIST_ENTRY CmpDelayDerefKCBListHead
;
33 ULONG CmpDelayDerefKCBIntervalInSeconds
= 5;
34 KDPC CmpDelayDerefKCBDpc
;
35 KTIMER CmpDelayDerefKCBTimer
;
37 /* FUNCTIONS *****************************************************************/
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
);
55 CmpDelayCloseWorker(IN PVOID Context
)
60 ASSERT(CmpDelayCloseWorkItemActive
);
68 CmpInitializeDelayedCloseTable(VOID
)
71 /* Setup the delayed close lock */
72 KeInitializeGuardedMutex(&CmpDelayedCloseTableLock
);
74 /* Setup the work item */
75 ExInitializeWorkItem(&CmpDelayCloseWorkItem
, CmpDelayCloseWorker
, NULL
);
77 /* Setup the list head */
78 InitializeListHead(&CmpDelayedLRUListHead
);
80 /* Setup the DPC and its timer */
81 KeInitializeDpc(&CmpDelayCloseDpc
, CmpDelayCloseDpcRoutine
, NULL
);
82 KeInitializeTimer(&CmpDelayCloseTimer
);
87 CmpDelayDerefKCBDpcRoutine(IN PKDPC Dpc
,
88 IN PVOID DeferredContext
,
89 IN PVOID SystemArgument1
,
90 IN PVOID SystemArgument2
)
93 ASSERT(CmpDelayDerefKCBWorkItemActive
);
95 /* Queue the work item */
96 ExQueueWorkItem(&CmpDelayDerefKCBWorkItem
, DelayedWorkQueue
);
101 CmpDelayDerefKCBWorker(IN PVOID Context
)
103 PCM_DELAY_DEREF_KCB_ITEM Entry
;
107 ASSERT(CmpDelayDerefKCBWorkItemActive
);
109 /* Lock the registry and and list lock */
111 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
113 /* Check if the list is empty */
114 while (!IsListEmpty(&CmpDelayDerefKCBListHead
))
117 Entry
= (PVOID
)RemoveHeadList(&CmpDelayDerefKCBListHead
);
119 /* We can release the lock now */
120 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
122 /* Now grab the actual entry */
123 Entry
= CONTAINING_RECORD(Entry
, CM_DELAY_DEREF_KCB_ITEM
, ListEntry
);
124 Entry
->ListEntry
.Flink
= Entry
->ListEntry
.Blink
= NULL
;
126 /* Dereference and free */
127 CmpDereferenceKeyControlBlock(Entry
->Kcb
);
128 CmpFreeDelayItem(Entry
);
130 /* Lock the list again */
131 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
135 CmpDelayDerefKCBWorkItemActive
= FALSE
;
136 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
142 CmpInitDelayDerefKCBEngine(VOID
)
144 /* Initialize lock and list */
145 KeInitializeGuardedMutex(&CmpDelayDerefKCBLock
);
146 InitializeListHead(&CmpDelayDerefKCBListHead
);
148 /* Setup the work item */
149 ExInitializeWorkItem(&CmpDelayDerefKCBWorkItem
,
150 CmpDelayDerefKCBWorker
,
153 /* Setup the DPC and timer for it */
154 KeInitializeDpc(&CmpDelayDerefKCBDpc
, CmpDelayDerefKCBDpcRoutine
, NULL
);
155 KeInitializeTimer(&CmpDelayDerefKCBTimer
);
160 CmpDelayDerefKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb
)
162 LONG OldRefCount
, NewRefCount
;
163 LARGE_INTEGER Timeout
;
164 PCM_DELAY_DEREF_KCB_ITEM Entry
;
167 /* Get the previous reference count */
168 OldRefCount
= *(PLONG
)&Kcb
->RefCount
;
169 NewRefCount
= OldRefCount
- 1;
170 if (((NewRefCount
& 0xFFFF) > 0) &&
171 (InterlockedCompareExchange((PLONG
)&Kcb
->RefCount
,
173 OldRefCount
) == OldRefCount
))
175 /* KCB still had references, so we're done */
179 /* Allocate a delay item */
180 Entry
= CmpAllocateDelayItem();
186 /* Acquire the delayed deref table lock */
187 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock
);
189 /* Insert the entry into the list */
190 InsertTailList(&CmpDelayDerefKCBListHead
, &Entry
->ListEntry
);
192 /* Check if we need to enable anything */
193 if (!CmpDelayDerefKCBWorkItemActive
)
195 /* Yes, we have no work item, setup the interval */
196 CmpDelayDerefKCBWorkItemActive
= TRUE
;
197 Timeout
.QuadPart
= CmpDelayDerefKCBIntervalInSeconds
* -10000000;
198 KeSetTimer(&CmpDelayDerefKCBTimer
, Timeout
, &CmpDelayDerefKCBDpc
);
201 /* Release the table lock */
202 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock
);
207 CmpArmDelayedCloseTimer(VOID
)
209 LARGE_INTEGER Timeout
;
212 /* Set the worker active */
213 CmpDelayCloseWorkItemActive
= TRUE
;
215 /* Setup the interval */
216 Timeout
.QuadPart
= CmpDelayCloseIntervalInSeconds
* -10000000;
217 KeSetTimer(&CmpDelayCloseTimer
, Timeout
, &CmpDelayCloseDpc
);
222 CmpAddToDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb
,
223 IN BOOLEAN LockHeldExclusively
)
226 ULONG OldRefCount
, NewRefCount
;
227 PCM_DELAYED_CLOSE_ENTRY Entry
;
231 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
232 (CmpTestRegistryLockExclusive() == TRUE
));
234 /* Make sure it's valid */
235 if (Kcb
->DelayedCloseIndex
!= CmpDelayedCloseSize
) ASSERT(FALSE
);
238 ASSERT(Kcb
->RefCount
== 0);
239 ASSERT(IsListEmpty(&Kcb
->KeyBodyListHead
) == TRUE
);
240 for (i
= 0; i
< 4; i
++) ASSERT(Kcb
->KeyBodyArray
[i
] == NULL
);
242 /* Allocate a delay item */
243 Entry
= CmpAllocateDelayItem();
246 /* Cleanup immediately */
247 CmpCleanUpKcbCacheWithLock(Kcb
, LockHeldExclusively
);
252 if (Kcb
->InDelayClose
) ASSERT(FALSE
);
254 /* Get the previous reference count */
255 OldRefCount
= *(PLONG
)&Kcb
->InDelayClose
;
256 ASSERT(OldRefCount
== 0);
258 /* Write the new one */
260 if (InterlockedCompareExchange((PLONG
)&Kcb
->InDelayClose
,
262 OldRefCount
) != OldRefCount
)
268 /* Reset the delayed close index */
269 Kcb
->DelayedCloseIndex
= 0;
271 /* Set up the close entry */
272 Kcb
->DelayCloseEntry
= Entry
;
273 Entry
->KeyControlBlock
= Kcb
;
275 /* Increase the number of elements */
276 InterlockedIncrement((PLONG
)&CmpDelayedCloseElements
);
278 /* Acquire the delayed close table lock */
279 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
281 /* Insert the entry into the list */
282 InsertHeadList(&CmpDelayedLRUListHead
, &Entry
->DelayedLRUList
);
284 /* Check if we need to enable anything */
285 if ((CmpDelayedCloseElements
> CmpDelayedCloseSize
) &&
286 !(CmpDelayCloseWorkItemActive
))
288 /* Yes, we have too many elements to close, and no work item */
289 CmpArmDelayedCloseTimer();
292 /* Release the table lock */
293 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
298 CmpRemoveFromDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb
)
300 PCM_DELAYED_CLOSE_ENTRY Entry
;
301 ULONG NewRefCount
, OldRefCount
;
305 ASSERT((CmpIsKcbLockedExclusive(Kcb
) == TRUE
) ||
306 (CmpTestRegistryLockExclusive() == TRUE
));
307 if (Kcb
->DelayedCloseIndex
== CmpDelayedCloseSize
) ASSERT(FALSE
);
309 /* Get the entry and lock the table */
310 Entry
= Kcb
->DelayCloseEntry
;
312 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock
);
314 /* Remove the entry */
315 RemoveEntryList(&Entry
->DelayedLRUList
);
317 /* Release the lock */
318 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock
);
321 CmpFreeDelayItem(Entry
);
323 /* Reduce the number of elements */
324 InterlockedDecrement((PLONG
)&CmpDelayedCloseElements
);
327 if (!Kcb
->InDelayClose
) ASSERT(FALSE
);
329 /* Get the previous reference count */
330 OldRefCount
= *(PLONG
)&Kcb
->InDelayClose
;
331 ASSERT(OldRefCount
== 1);
333 /* Write the new one */
335 if (InterlockedCompareExchange((PLONG
)&Kcb
->InDelayClose
,
337 OldRefCount
) != OldRefCount
)
343 /* Remove the link to the entry */
344 Kcb
->DelayCloseEntry
= NULL
;
346 /* Set new delay size and remove the delete flag */
347 Kcb
->DelayedCloseIndex
= CmpDelayedCloseSize
;