2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/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 DPRINT("%s(%p 0x%08x %u %u %u)\n", __FUNCTION__
, RemoveLock
, AllocateTag
, MaxLockedMinutes
, HighWatermark
, RemlockSize
);
44 ASSERT(HighWatermark
< MAXLONG
);
46 /* If no lock given, nothing to do */
54 /* Check if this is a debug lock */
55 case (sizeof(IO_REMOVE_LOCK_DBG_BLOCK
) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
)):
56 /* Setup debug parameters */
57 Lock
->Dbg
.Signature
= 'COLR';
58 Lock
->Dbg
.HighWatermark
= HighWatermark
;
59 Lock
->Dbg
.MaxLockedTicks
= KeQueryTimeIncrement() * MaxLockedMinutes
* 600000000;
60 Lock
->Dbg
.AllocateTag
= AllocateTag
;
61 KeInitializeSpinLock(&(Lock
->Dbg
.Spin
));
62 Lock
->Dbg
.LowMemoryCount
= 0;
63 Lock
->Dbg
.Blocks
= NULL
;
65 case sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
):
66 /* Setup a free block */
67 Lock
->Common
.Removed
= FALSE
;
68 Lock
->Common
.IoCount
= 1;
69 KeInitializeEvent(&Lock
->Common
.RemoveEvent
,
80 IoAcquireRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock
,
81 IN OPTIONAL PVOID Tag
,
88 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock
;
89 PEXTENDED_IO_REMOVE_LOCK Lock
= (PEXTENDED_IO_REMOVE_LOCK
)RemoveLock
;
91 DPRINT("%s(%p %p %s %u %u)\n", __FUNCTION__
, RemoveLock
, Tag
, File
, Line
, RemlockSize
);
93 /* Increase the lock count */
94 LockValue
= InterlockedIncrement(&(Lock
->Common
.IoCount
));
95 ASSERT(LockValue
> 0);
96 if (!Lock
->Common
.Removed
)
98 /* Check what kind of lock this is */
99 if (RemlockSize
== (sizeof(IO_REMOVE_LOCK_DBG_BLOCK
) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
)))
101 ASSERT(Lock
->Dbg
.HighWatermark
== 0 || LockValue
<= Lock
->Dbg
.HighWatermark
);
103 /* Allocate a tracking block */
104 TrackingBlock
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_REMOVE_LOCK_TRACKING_BLOCK
), Lock
->Dbg
.AllocateTag
);
107 /* Keep count of failures for lock release and missing tags */
108 InterlockedIncrement(&(Lock
->Dbg
.LowMemoryCount
));
112 /* Initialize block */
113 RtlZeroMemory(TrackingBlock
, sizeof(IO_REMOVE_LOCK_TRACKING_BLOCK
));
114 TrackingBlock
->Tag
= Tag
;
115 TrackingBlock
->File
= File
;
116 TrackingBlock
->Line
= Line
;
117 KeQueryTickCount(&(TrackingBlock
->LockMoment
));
119 /* Queue the block */
120 KeAcquireSpinLock(&(Lock
->Dbg
.Spin
), &OldIrql
);
121 TrackingBlock
->Next
= Lock
->Dbg
.Blocks
;
122 Lock
->Dbg
.Blocks
= TrackingBlock
;
123 KeReleaseSpinLock(&(Lock
->Dbg
.Spin
), OldIrql
);
129 /* Otherwise, decrement the count and check if it's gone */
130 if (!InterlockedDecrement(&(Lock
->Common
.IoCount
)))
132 /* Signal the event */
133 KeSetEvent(&(Lock
->Common
.RemoveEvent
), IO_NO_INCREMENT
, FALSE
);
136 /* Return pending delete */
137 return STATUS_DELETE_PENDING
;
140 /* Otherwise, return success */
141 return STATUS_SUCCESS
;
149 IoReleaseRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock
,
151 IN ULONG RemlockSize
)
156 LARGE_INTEGER CurrentMoment
;
157 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock
;
158 PIO_REMOVE_LOCK_TRACKING_BLOCK
*TrackingBlockLink
;
159 PEXTENDED_IO_REMOVE_LOCK Lock
= (PEXTENDED_IO_REMOVE_LOCK
)RemoveLock
;
161 DPRINT("%s(%p %p %u)\n", __FUNCTION__
, RemoveLock
, Tag
, RemlockSize
);
163 /* Check what kind of lock this is */
164 if (RemlockSize
== (sizeof(IO_REMOVE_LOCK_DBG_BLOCK
) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
)))
166 /* Acquire blocks queue */
167 KeAcquireSpinLock(&(Lock
->Dbg
.Spin
), &OldIrql
);
169 /* Get the release moment */
170 KeQueryTickCount(&CurrentMoment
);
172 /* Start browsing tracking blocks to find a block that would match given tag */
174 TrackingBlock
= Lock
->Dbg
.Blocks
;
175 TrackingBlockLink
= &(Lock
->Dbg
.Blocks
);
176 while (TrackingBlock
!= NULL
)
178 /* First of all, check if the lock was locked for too long */
179 if (Lock
->Dbg
.MaxLockedTicks
&&
180 CurrentMoment
.QuadPart
- TrackingBlock
->LockMoment
.QuadPart
> Lock
->Dbg
.MaxLockedTicks
)
182 DPRINT("Lock %#08lx (with tag %#08lx) was supposed to be held at max %I64d ticks but lasted longer\n",
183 Lock
, TrackingBlock
->Tag
, Lock
->Dbg
.MaxLockedTicks
);
184 DPRINT("Lock was acquired in file %s at line %lu\n", TrackingBlock
->File
, TrackingBlock
->Line
);
188 /* Check if this is the first matching tracking block */
189 if ((TagFound
== FALSE
) && (TrackingBlock
->Tag
== Tag
))
191 /* Unlink this tracking block, and free it */
193 *TrackingBlockLink
= TrackingBlock
->Next
;
194 ExFreePoolWithTag(TrackingBlock
, Lock
->Dbg
.AllocateTag
);
195 TrackingBlock
= *TrackingBlockLink
;
199 /* Go to the next tracking block */
200 TrackingBlockLink
= &(TrackingBlock
->Next
);
201 TrackingBlock
= TrackingBlock
->Next
;
205 /* We're done, release queue lock */
206 KeReleaseSpinLock(&(Lock
->Dbg
.Spin
), OldIrql
);
208 /* If we didn't find any matching block */
209 if (TagFound
== FALSE
)
211 /* Check if it was because we were low in memory
212 * If yes, then ignore, that's normal
214 if (InterlockedDecrement(&(Lock
->Dbg
.LowMemoryCount
)) < 0)
216 /* Otherwise signal the issue, it shouldn't happen */
217 InterlockedIncrement(&(Lock
->Dbg
.LowMemoryCount
));
218 DPRINT("Failed finding block for tag: %#08lx\n", Tag
);
224 /* Decrement the lock count */
225 LockValue
= InterlockedDecrement(&(Lock
->Common
.IoCount
));
226 ASSERT(LockValue
>= 0);
230 /* Someone should be waiting... */
231 ASSERT(Lock
->Common
.Removed
);
233 /* Signal the event */
234 KeSetEvent(&(Lock
->Common
.RemoveEvent
), IO_NO_INCREMENT
, FALSE
);
243 IoReleaseRemoveLockAndWaitEx(IN PIO_REMOVE_LOCK RemoveLock
,
245 IN ULONG RemlockSize
)
248 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock
;
249 PEXTENDED_IO_REMOVE_LOCK Lock
= (PEXTENDED_IO_REMOVE_LOCK
)RemoveLock
;
252 DPRINT("%s(%p %p %u)\n", __FUNCTION__
, RemoveLock
, Tag
, RemlockSize
);
254 /* Remove the lock and decrement the count */
255 Lock
->Common
.Removed
= TRUE
;
256 LockValue
= InterlockedDecrement(&Lock
->Common
.IoCount
);
257 ASSERT(LockValue
> 0);
259 /* If we are still > 0, then wait for the others to remove lock */
260 if (InterlockedDecrement(&Lock
->Common
.IoCount
) > 0)
263 KeWaitForSingleObject(&(Lock
->Common
.RemoveEvent
),
270 /* Check what kind of lock this is */
271 if (RemlockSize
== (sizeof(IO_REMOVE_LOCK_DBG_BLOCK
) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK
)))
273 /* A block must be remaining */
274 ASSERT(Lock
->Dbg
.Blocks
);
277 TrackingBlock
= Lock
->Dbg
.Blocks
;
278 /* Tag should match */
279 if (TrackingBlock
->Tag
!= Tag
)
281 DPRINT("Last tracking block tag invalid! Expected: %p, having: %p\n", Tag
, TrackingBlock
->Tag
);
282 ASSERT(TrackingBlock
->Tag
!= Tag
);
286 ExFreePoolWithTag(Lock
->Dbg
.Blocks
, Lock
->Dbg
.AllocateTag
);