2 * Cancel-Safe Queue Library
3 * Created in 2004 by Vizzini (vizzini@plasmic.com)
5 * THIS SOFTWARE IS NOT COPYRIGHTED
7 * This source code is offered for use in the public domain. You may
8 * use, modify or distribute it freely.
10 * This code is distributed in the hope that it will be useful but
11 * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
12 * DISCLAIMED. This includes but is not limited to warranties of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * This header defines the interface to the ReactOS Cancel-Safe Queue library.
17 * This interface is based on and is similar to the Microsoft Cancel-Safe
22 * IRP queuing is a royal pain in the butt, due to the fact that there are
23 * tons of built-in race conditions. IRP handling is difficult in general,
24 * but the cancel logic has been particularly complicated due to some subtle
25 * races, coupled with the fact that the system interfaces have changed over
28 * Walter Oney (2nd. Ed. of Programming the Windows Driver Model) states a
29 * common opinion among driver developers when he says that it is foolish
30 * to try to roll your own cancel logic. There are only a very few people
31 * who have gotten it right in the past. He suggests, instead, that you
32 * either use his own well-tested code, or use the code in the Microsoft
33 * Cancel-Safe Queue Library.
35 * We cannot do either, of course, due to copyright issues. I have therefore
36 * created this clone of the Microsoft library in order to concentrate all
37 * of the IRP-queuing bugs in one place. I'm quite sure there are problems
38 * here, so if you are a driver writer, I'd be glad to hear your feedback.
40 * Apart from that, please try to use these routines, rather than building
41 * your own. If you think you have found a bug, please bring it up with me
42 * or on-list, as this is complicated and non-obvious stuff. Don't just
43 * change this and hope for the best!
47 * This library follows exactly the same interface as the Microsoft Cancel-Safe
48 * Queue routines (IoCsqXxx()). As such, the authoritative reference is the
49 * current DDK. There is also a DDK sample called "cancel" that has an
50 * example of how to use this code. I have also provided a sample driver
51 * that makes use of this queue. Finally, please do read the header and the
52 * source if you're curious about the inner workings of these routines.
64 * Prevent including the CSQ definitions twice. They're present in NTDDK
65 * now too, except the *_EX versions.
67 #ifndef IO_TYPE_CSQ_IRP_CONTEXT
69 typedef struct _IO_CSQ IO_CSQ
, *PIO_CSQ
;
74 * NOTE: Please do not use these directly. You will make incompatible code
75 * if you do. Always only use the documented IoCsqXxx() interfaces and you
76 * will amass much Good Karma.
78 #define IO_TYPE_CSQ_IRP_CONTEXT 1
82 * IO_CSQ_IRP_CONTEXT - Context used to track an IRP in the CSQ
84 typedef struct _IO_CSQ_IRP_CONTEXT
{
88 } IO_CSQ_IRP_CONTEXT
, *PIO_CSQ_IRP_CONTEXT
;
93 * The cancel-safe queue is implemented as a set of IoCsqXxx() OS routines
94 * copuled with a set of driver callbacks to handle the basic operations of
95 * the queue. You need to supply one of each of these functions in your own
96 * driver. These routines are also documented in the DDK under CsqXxx().
97 * That is the authoritative documentation.
101 * Function to insert an IRP in the queue. No need to worry about locking;
102 * just tack it onto your list or something.
104 * Sample implementation:
106 VOID NTAPI CsqInsertIrp(PIO_CSQ Csq, PIRP Irp)
108 KdPrint(("Inserting IRP 0x%x into CSQ\n", Irp));
109 InsertTailList(&IrpQueue, &Irp->Tail.Overlay.ListEntry);
114 (NTAPI IO_CSQ_INSERT_IRP
)(
115 IN
struct _IO_CSQ
*Csq
,
117 typedef IO_CSQ_INSERT_IRP
*PIO_CSQ_INSERT_IRP
;
120 * Function to remove an IRP from the queue.
124 VOID NTAPI CsqRemoveIrp(PIO_CSQ Csq, PIRP Irp)
126 KdPrint(("Removing IRP 0x%x from CSQ\n", Irp));
127 RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
132 (NTAPI IO_CSQ_REMOVE_IRP
)(
133 IN
struct _IO_CSQ
*Csq
,
135 typedef IO_CSQ_REMOVE_IRP
*PIO_CSQ_REMOVE_IRP
;
138 * Function to look for an IRP in the queue
142 PIRP NTAPI CsqPeekNextIrp(PIO_CSQ Csq, PIRP Irp, PVOID PeekContext)
144 KdPrint(("Peeking for next IRP\n"));
147 return CONTAINING_RECORD(&Irp->Tail.Overlay.ListEntry.Flink, IRP, Tail.Overlay.ListEntry);
149 if(IsListEmpty(&IrpQueue))
152 return CONTAINING_RECORD(IrpQueue.Flink, IRP, Tail.Overlay.ListEntry);
157 (NTAPI IO_CSQ_PEEK_NEXT_IRP
)(
158 IN
struct _IO_CSQ
*Csq
,
160 IN PVOID PeekContext
);
161 typedef IO_CSQ_PEEK_NEXT_IRP
*PIO_CSQ_PEEK_NEXT_IRP
;
164 * Lock the queue. This can be a spinlock, a mutex, or whatever
165 * else floats your boat.
169 VOID NTAPI CsqAcquireLock(PIO_CSQ Csq, PKIRQL Irql)
171 KdPrint(("Acquiring spin lock\n"));
172 KeAcquireSpinLock(&IrpQueueLock, Irql);
177 (NTAPI IO_CSQ_ACQUIRE_LOCK
)(
178 IN
struct _IO_CSQ
*Csq
,
180 typedef IO_CSQ_ACQUIRE_LOCK
*PIO_CSQ_ACQUIRE_LOCK
;
185 VOID NTAPI CsqReleaseLock(PIO_CSQ Csq, KIRQL Irql)
187 KdPrint(("Releasing spin lock\n"));
188 KeReleaseSpinLock(&IrpQueueLock, Irql);
193 (NTAPI IO_CSQ_RELEASE_LOCK
)(
194 IN
struct _IO_CSQ
*Csq
,
196 typedef IO_CSQ_RELEASE_LOCK
*PIO_CSQ_RELEASE_LOCK
;
199 * Finally, this is called by the queue library when it wants to complete
204 VOID NTAPI CsqCompleteCancelledIrp(PIO_CSQ Csq, PIRP Irp)
206 KdPrint(("cancelling irp 0x%x\n", Irp));
207 Irp->IoStatus.Status = STATUS_CANCELLED;
208 Irp->IoStatus.Information = 0;
209 IoCompleteRequest(Irp, IO_NO_INCREMENT);
214 (NTAPI IO_CSQ_COMPLETE_CANCELED_IRP
)(
215 IN
struct _IO_CSQ
*Csq
,
217 typedef IO_CSQ_COMPLETE_CANCELED_IRP
*PIO_CSQ_COMPLETE_CANCELED_IRP
;
220 * IO_CSQ - Queue control structure
222 typedef struct _IO_CSQ
{
224 PIO_CSQ_INSERT_IRP CsqInsertIrp
;
225 PIO_CSQ_REMOVE_IRP CsqRemoveIrp
;
226 PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp
;
227 PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock
;
228 PIO_CSQ_RELEASE_LOCK CsqReleaseLock
;
229 PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp
;
230 PVOID ReservePointer
; /* must be NULL */
233 #endif /* IO_TYPE_CSQ_IRP_CONTEXT */
235 #ifndef IO_TYPE_CSQ_EX
237 /* See IO_TYPE_CSQ_* above */
238 #define IO_TYPE_CSQ_EX 3
241 * Function to insert an IRP into the queue with extended context information.
242 * This is useful if you need to be able to de-queue particular IRPs more
243 * easily in some cases.
245 * Same deal as above; sample implementation:
247 NTSTATUS NTAPI CsqInsertIrpEx(PIO_CSQ Csq, PIRP Irp, PVOID InsertContext)
249 CsqInsertIrp(Csq, Irp);
250 return STATUS_PENDING;
255 (NTAPI IO_CSQ_INSERT_IRP_EX
)(
256 IN
struct _IO_CSQ
*Csq
,
258 IN PVOID InsertContext
);
259 typedef IO_CSQ_INSERT_IRP_EX
*PIO_CSQ_INSERT_IRP_EX
;
261 #endif /* IO_TYPE_CSQ_EX */
264 * CANCEL-SAFE QUEUE DDIs
266 * These device driver interfaces are called to make use of the queue. Again,
267 * authoritative documentation for these functions is in the DDK. The csqtest
268 * driver also makes use of some of them.
273 * Call this in DriverEntry or similar in order to set up the Csq structure.
274 * As long as the Csq struct and the functions you pass in are resident,
275 * there are no IRQL restrictions.
278 NTSTATUS NTAPI
IoCsqInitialize(PIO_CSQ Csq
,
279 PIO_CSQ_INSERT_IRP CsqInsertIrp
,
280 PIO_CSQ_REMOVE_IRP CsqRemoveIrp
,
281 PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp
,
282 PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock
,
283 PIO_CSQ_RELEASE_LOCK CsqReleaseLock
,
284 PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp
);
287 * Same as above, except you provide a CsqInsertIrpEx routine instead of
288 * CsqInsertIrp. This eventually allows you to supply extra tracking
289 * information for use with the queue.
292 NTSTATUS NTAPI
IoCsqInitializeEx(PIO_CSQ Csq
,
293 PIO_CSQ_INSERT_IRP_EX CsqInsertIrpEx
,
294 PIO_CSQ_REMOVE_IRP CsqRemoveIrp
,
295 PIO_CSQ_PEEK_NEXT_IRP CsqPeekNextIrp
,
296 PIO_CSQ_ACQUIRE_LOCK CsqAcquireLock
,
297 PIO_CSQ_RELEASE_LOCK CsqReleaseLock
,
298 PIO_CSQ_COMPLETE_CANCELED_IRP CsqCompleteCanceledIrp
);
301 * Insert an IRP into the queue
304 VOID NTAPI
IoCsqInsertIrp(PIO_CSQ Csq
,
306 PIO_CSQ_IRP_CONTEXT Context
);
309 * Insert an IRP into the queue, with special context maintained that
310 * makes it easy to find IRPs in the queue
313 NTSTATUS NTAPI
IoCsqInsertIrpEx(PIO_CSQ Csq
,
315 PIO_CSQ_IRP_CONTEXT Context
,
316 PVOID InsertContext
);
319 * Remove a particular IRP from the queue
322 PIRP NTAPI
IoCsqRemoveIrp(PIO_CSQ Csq
,
323 PIO_CSQ_IRP_CONTEXT Context
);
326 * Remove the next IRP from the queue
329 PIRP NTAPI
IoCsqRemoveNextIrp(PIO_CSQ Csq
,