2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/remlock.c
5 * PURPOSE: Remove Lock Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Filip Navara (navaraf@reactos.org)
8 * Pierre Schweitzer (pierre.schweitzer@reactos.org)
11 /* INCLUDES ******************************************************************/
17 typedef struct _IO_REMOVE_LOCK_TRACKING_BLOCK
19 PIO_REMOVE_LOCK_TRACKING_BLOCK Next
;
21 LARGE_INTEGER LockMoment
;
24 } IO_REMOVE_LOCK_TRACKING_BLOCK
;
26 /* FUNCTIONS *****************************************************************/
33 IoInitializeRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock
,
35 IN ULONG MaxLockedMinutes
,
36 IN ULONG HighWatermark
,
39 PEXTENDED_IO_REMOVE_LOCK Lock
= (PEXTENDED_IO_REMOVE_LOCK
)RemoveLock
;
42 ASSERT(HighWatermark
< MAXLONG
);
44 /* If no lock given, nothing to do */
52 /* Check if this is a debug lock */
53 case (sizeof(IO_REMOVE_LOCK_DBG_BLOCK
) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
)):
54 /* Setup debug parameters */
55 Lock
->Dbg
.Signature
= 'COLR';
56 Lock
->Dbg
.HighWatermark
= HighWatermark
;
57 Lock
->Dbg
.MaxLockedTicks
= KeQueryTimeIncrement() * MaxLockedMinutes
* 600000000;
58 Lock
->Dbg
.AllocateTag
= AllocateTag
;
59 KeInitializeSpinLock(&(Lock
->Dbg
.Spin
));
60 Lock
->Dbg
.LowMemoryCount
= 0;
61 Lock
->Dbg
.Blocks
= NULL
;
63 case sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
):
64 /* Setup a free block */
65 Lock
->Common
.Removed
= FALSE
;
66 Lock
->Common
.IoCount
= 1;
67 KeInitializeEvent(&Lock
->Common
.RemoveEvent
,
78 IoAcquireRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock
,
79 IN OPTIONAL PVOID Tag
,
86 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock
;
87 PEXTENDED_IO_REMOVE_LOCK Lock
= (PEXTENDED_IO_REMOVE_LOCK
)RemoveLock
;
89 /* Increase the lock count */
90 LockValue
= InterlockedIncrement(&(Lock
->Common
.IoCount
));
91 ASSERT(LockValue
> 0);
92 if (!Lock
->Common
.Removed
)
94 /* Check what kind of lock this is */
95 if (RemlockSize
== (sizeof(IO_REMOVE_LOCK_DBG_BLOCK
) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
)))
97 ASSERT(Lock
->Dbg
.HighWatermark
== 0 || LockValue
<= Lock
->Dbg
.HighWatermark
);
99 /* Allocate a tracking block */
100 TrackingBlock
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_REMOVE_LOCK_TRACKING_BLOCK
), Lock
->Dbg
.AllocateTag
);
103 /* Keep count of failures for lock release and missing tags */
104 InterlockedIncrement(&(Lock
->Dbg
.LowMemoryCount
));
108 /* Initialize block */
109 RtlZeroMemory(TrackingBlock
, sizeof(IO_REMOVE_LOCK_TRACKING_BLOCK
));
110 TrackingBlock
->Tag
= Tag
;
111 TrackingBlock
->File
= File
;
112 TrackingBlock
->Line
= Line
;
113 KeQueryTickCount(&(TrackingBlock
->LockMoment
));
115 /* Queue the block */
116 KeAcquireSpinLock(&(Lock
->Dbg
.Spin
), &OldIrql
);
117 TrackingBlock
->Next
= Lock
->Dbg
.Blocks
;
118 Lock
->Dbg
.Blocks
= TrackingBlock
;
119 KeReleaseSpinLock(&(Lock
->Dbg
.Spin
), OldIrql
);
125 /* Otherwise, decrement the count and check if it's gone */
126 if (!InterlockedDecrement(&(Lock
->Common
.IoCount
)))
128 /* Signal the event */
129 KeSetEvent(&(Lock
->Common
.RemoveEvent
), IO_NO_INCREMENT
, FALSE
);
132 /* Return pending delete */
133 return STATUS_DELETE_PENDING
;
136 /* Otherwise, return success */
137 return STATUS_SUCCESS
;
145 IoReleaseRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock
,
147 IN ULONG RemlockSize
)
152 LARGE_INTEGER CurrentMoment
;
153 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock
;
154 PIO_REMOVE_LOCK_TRACKING_BLOCK
*TrackingBlockLink
;
155 PEXTENDED_IO_REMOVE_LOCK Lock
= (PEXTENDED_IO_REMOVE_LOCK
)RemoveLock
;
157 /* Check what kind of lock this is */
158 if (RemlockSize
== (sizeof(IO_REMOVE_LOCK_DBG_BLOCK
) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
)))
160 /* Acquire blocks queue */
161 KeAcquireSpinLock(&(Lock
->Dbg
.Spin
), &OldIrql
);
163 /* Get the release moment */
164 KeQueryTickCount(&CurrentMoment
);
166 /* Start browsing tracking blocks to find a block that would match given tag */
168 TrackingBlock
= Lock
->Dbg
.Blocks
;
169 TrackingBlockLink
= &(Lock
->Dbg
.Blocks
);
170 while (TrackingBlock
!= NULL
)
172 /* First of all, check if the lock was locked for too long */
173 if (Lock
->Dbg
.MaxLockedTicks
&&
174 CurrentMoment
.QuadPart
- TrackingBlock
->LockMoment
.QuadPart
> Lock
->Dbg
.MaxLockedTicks
)
176 DPRINT("Lock %#08lx (with tag %#08lx) was supposed to be held at max %I64d ticks but lasted longer\n",
177 Lock
, TrackingBlock
->Tag
, Lock
->Dbg
.MaxLockedTicks
);
178 DPRINT("Lock was acquired in file %s at line %lu\n", TrackingBlock
->File
, TrackingBlock
->Line
);
182 /* Check if this is the first matching tracking block */
183 if ((TagFound
== FALSE
) && (TrackingBlock
->Tag
== Tag
))
185 /* Unlink this tracking block, and free it */
187 *TrackingBlockLink
= TrackingBlock
->Next
;
188 ExFreePoolWithTag(TrackingBlock
, Lock
->Dbg
.AllocateTag
);
189 TrackingBlock
= *TrackingBlockLink
;
193 /* Go to the next tracking block */
194 TrackingBlockLink
= &(TrackingBlock
->Next
);
195 TrackingBlock
= TrackingBlock
->Next
;
199 /* We're done, release queue lock */
200 KeReleaseSpinLock(&(Lock
->Dbg
.Spin
), OldIrql
);
202 /* If we didn't find any matching block */
203 if (TagFound
== FALSE
)
205 /* Check if it was because we were low in memory
206 * If yes, then ignore, that's normal
208 if (InterlockedDecrement(&(Lock
->Dbg
.LowMemoryCount
)) < 0)
210 /* Otherwise signal the issue, it shouldn't happen */
211 InterlockedIncrement(&(Lock
->Dbg
.LowMemoryCount
));
212 DPRINT("Failed finding block for tag: %#08lx\n", Tag
);
218 /* Decrement the lock count */
219 LockValue
= InterlockedDecrement(&(Lock
->Common
.IoCount
));
220 ASSERT(LockValue
>= 0);
224 /* Someone should be waiting... */
225 ASSERT(Lock
->Common
.Removed
);
227 /* Signal the event */
228 KeSetEvent(&(Lock
->Common
.RemoveEvent
), IO_NO_INCREMENT
, FALSE
);
237 IoReleaseRemoveLockAndWaitEx(IN PIO_REMOVE_LOCK RemoveLock
,
239 IN ULONG RemlockSize
)
242 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock
;
243 PEXTENDED_IO_REMOVE_LOCK Lock
= (PEXTENDED_IO_REMOVE_LOCK
)RemoveLock
;
246 /* Remove the lock and decrement the count */
247 Lock
->Common
.Removed
= TRUE
;
248 LockValue
= InterlockedDecrement(&Lock
->Common
.IoCount
);
249 ASSERT(LockValue
> 0);
251 /* If we are still > 0, then wait for the others to remove lock */
252 if (InterlockedDecrement(&Lock
->Common
.IoCount
) > 0)
255 KeWaitForSingleObject(&(Lock
->Common
.RemoveEvent
),
262 /* Check what kind of lock this is */
263 if (RemlockSize
== (sizeof(IO_REMOVE_LOCK_DBG_BLOCK
) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
)))
265 /* A block must be remaining */
266 ASSERT(Lock
->Dbg
.Blocks
);
269 TrackingBlock
= Lock
->Dbg
.Blocks
;
270 /* Tag should match */
271 if (TrackingBlock
->Tag
!= Tag
)
273 DPRINT("Last tracking block tag invalid! Expected: %p, having: %p\n", Tag
, TrackingBlock
->Tag
);
274 ASSERT(TrackingBlock
->Tag
!= Tag
);
278 ExFreePoolWithTag(Lock
->Dbg
.Blocks
, Lock
->Dbg
.AllocateTag
);