* Sync up to trunk head (r64921).
[reactos.git] / ntoskrnl / io / iomgr / remlock.c
1 /*
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)
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 typedef struct _IO_REMOVE_LOCK_TRACKING_BLOCK
18 {
19 PIO_REMOVE_LOCK_TRACKING_BLOCK Next;
20 PVOID Tag;
21 LARGE_INTEGER LockMoment;
22 LPCSTR File;
23 ULONG Line;
24 } IO_REMOVE_LOCK_TRACKING_BLOCK;
25
26 /* FUNCTIONS *****************************************************************/
27
28 /*
29 * @implemented
30 */
31 VOID
32 NTAPI
33 IoInitializeRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock,
34 IN ULONG AllocateTag,
35 IN ULONG MaxLockedMinutes,
36 IN ULONG HighWatermark,
37 IN ULONG RemlockSize)
38 {
39 PEXTENDED_IO_REMOVE_LOCK Lock = (PEXTENDED_IO_REMOVE_LOCK)RemoveLock;
40 PAGED_CODE();
41
42 ASSERT(HighWatermark < MAXLONG);
43
44 /* If no lock given, nothing to do */
45 if (!Lock)
46 {
47 return;
48 }
49
50 switch (RemlockSize)
51 {
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;
62
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,
68 SynchronizationEvent,
69 FALSE);
70 }
71 }
72
73 /*
74 * @implemented
75 */
76 NTSTATUS
77 NTAPI
78 IoAcquireRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock,
79 IN OPTIONAL PVOID Tag,
80 IN LPCSTR File,
81 IN ULONG Line,
82 IN ULONG RemlockSize)
83 {
84 KIRQL OldIrql;
85 LONG LockValue;
86 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock;
87 PEXTENDED_IO_REMOVE_LOCK Lock = (PEXTENDED_IO_REMOVE_LOCK)RemoveLock;
88
89 /* Increase the lock count */
90 LockValue = InterlockedIncrement(&(Lock->Common.IoCount));
91 ASSERT(LockValue > 0);
92 if (!Lock->Common.Removed)
93 {
94 /* Check what kind of lock this is */
95 if (RemlockSize == (sizeof(IO_REMOVE_LOCK_DBG_BLOCK) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK)))
96 {
97 ASSERT(Lock->Dbg.HighWatermark == 0 || LockValue <= Lock->Dbg.HighWatermark);
98
99 /* Allocate a tracking block */
100 TrackingBlock = ExAllocatePoolWithTag(NonPagedPool, sizeof(IO_REMOVE_LOCK_TRACKING_BLOCK), Lock->Dbg.AllocateTag);
101 if (!TrackingBlock)
102 {
103 /* Keep count of failures for lock release and missing tags */
104 InterlockedIncrement(&(Lock->Dbg.LowMemoryCount));
105 }
106 else
107 {
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));
114
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);
120 }
121 }
122 }
123 else
124 {
125 /* Otherwise, decrement the count and check if it's gone */
126 if (!InterlockedDecrement(&(Lock->Common.IoCount)))
127 {
128 /* Signal the event */
129 KeSetEvent(&(Lock->Common.RemoveEvent), IO_NO_INCREMENT, FALSE);
130 }
131
132 /* Return pending delete */
133 return STATUS_DELETE_PENDING;
134 }
135
136 /* Otherwise, return success */
137 return STATUS_SUCCESS;
138 }
139
140 /*
141 * @implemented
142 */
143 VOID
144 NTAPI
145 IoReleaseRemoveLockEx(IN PIO_REMOVE_LOCK RemoveLock,
146 IN PVOID Tag,
147 IN ULONG RemlockSize)
148 {
149 KIRQL OldIrql;
150 LONG LockValue;
151 BOOLEAN TagFound;
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;
156
157 /* Check what kind of lock this is */
158 if (RemlockSize == (sizeof(IO_REMOVE_LOCK_DBG_BLOCK) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK)))
159 {
160 /* Acquire blocks queue */
161 KeAcquireSpinLock(&(Lock->Dbg.Spin), &OldIrql);
162
163 /* Get the release moment */
164 KeQueryTickCount(&CurrentMoment);
165
166 /* Start browsing tracking blocks to find a block that would match given tag */
167 TagFound = FALSE;
168 TrackingBlock = Lock->Dbg.Blocks;
169 TrackingBlockLink = &(Lock->Dbg.Blocks);
170 while (TrackingBlock != NULL)
171 {
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)
175 {
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);
179 ASSERT(FALSE);
180 }
181
182 /* Check if this is the first matching tracking block */
183 if ((TagFound == FALSE) && (TrackingBlock->Tag == Tag))
184 {
185 /* Unlink this tracking block, and free it */
186 TagFound = TRUE;
187 *TrackingBlockLink = TrackingBlock->Next;
188 ExFreePoolWithTag(TrackingBlock, Lock->Dbg.AllocateTag);
189 TrackingBlock = *TrackingBlockLink;
190 }
191 else
192 {
193 /* Go to the next tracking block */
194 TrackingBlockLink = &(TrackingBlock->Next);
195 TrackingBlock = TrackingBlock->Next;
196 }
197 }
198
199 /* We're done, release queue lock */
200 KeReleaseSpinLock(&(Lock->Dbg.Spin), OldIrql);
201
202 /* If we didn't find any matching block */
203 if (TagFound == FALSE)
204 {
205 /* Check if it was because we were low in memory
206 * If yes, then ignore, that's normal
207 */
208 if (InterlockedDecrement(&(Lock->Dbg.LowMemoryCount)) < 0)
209 {
210 /* Otherwise signal the issue, it shouldn't happen */
211 InterlockedIncrement(&(Lock->Dbg.LowMemoryCount));
212 DPRINT("Failed finding block for tag: %#08lx\n", Tag);
213 ASSERT(FALSE);
214 }
215 }
216 }
217
218 /* Decrement the lock count */
219 LockValue = InterlockedDecrement(&(Lock->Common.IoCount));
220 ASSERT(LockValue >= 0);
221
222 if (!LockValue)
223 {
224 /* Someone should be waiting... */
225 ASSERT(Lock->Common.Removed);
226
227 /* Signal the event */
228 KeSetEvent(&(Lock->Common.RemoveEvent), IO_NO_INCREMENT, FALSE);
229 }
230 }
231
232 /*
233 * @implemented
234 */
235 VOID
236 NTAPI
237 IoReleaseRemoveLockAndWaitEx(IN PIO_REMOVE_LOCK RemoveLock,
238 IN PVOID Tag,
239 IN ULONG RemlockSize)
240 {
241 LONG LockValue;
242 PIO_REMOVE_LOCK_TRACKING_BLOCK TrackingBlock;
243 PEXTENDED_IO_REMOVE_LOCK Lock = (PEXTENDED_IO_REMOVE_LOCK)RemoveLock;
244 PAGED_CODE();
245
246 /* Remove the lock and decrement the count */
247 Lock->Common.Removed = TRUE;
248 LockValue = InterlockedDecrement(&Lock->Common.IoCount);
249 ASSERT(LockValue > 0);
250
251 /* If we are still > 0, then wait for the others to remove lock */
252 if (InterlockedDecrement(&Lock->Common.IoCount) > 0)
253 {
254 /* Wait for it */
255 KeWaitForSingleObject(&(Lock->Common.RemoveEvent),
256 Executive,
257 KernelMode,
258 FALSE,
259 NULL);
260 }
261
262 /* Check what kind of lock this is */
263 if (RemlockSize == (sizeof(IO_REMOVE_LOCK_DBG_BLOCK) + sizeof(IO_REMOVE_LOCK_COMMON_BLOCK)))
264 {
265 /* A block must be remaining */
266 ASSERT(Lock->Dbg.Blocks);
267
268 /* Get it */
269 TrackingBlock = Lock->Dbg.Blocks;
270 /* Tag should match */
271 if (TrackingBlock->Tag != Tag)
272 {
273 DPRINT("Last tracking block tag invalid! Expected: %p, having: %p\n", Tag, TrackingBlock->Tag);
274 ASSERT(TrackingBlock->Tag != Tag);
275 }
276
277 /* Release block */
278 ExFreePoolWithTag(Lock->Dbg.Blocks, Lock->Dbg.AllocateTag);
279 }
280 }
281
282 /* EOF */