已有结论(又要自问自答了,囧。。。)
不会存在问题,前提是线程2是IoCallDriver以及IoCancelIrp的同一个发起者;
以下分析涉及到的代码参考自reactos及wrk;
(假如哪位前辈觉着此结论有问题,请告知下,免得我误人误己,呵呵)
以ring3成的CancelIo为例,它的调用路径为:CancelIo->NtCancelIoFile->IoCancelIrp;
其中在NtCancelIoFile中会调用KeRaiseIrql(APC_LEVEL, &OldIrql);然后遍历此线程中所有相关要取消的IRP列表,并依次调用IoCancelIrp;
而对于异步IRP,其在IofCompleteRequest内部,不会直接调用IoFreeIrp,而是把其放入到APC队列中,如:
/* Initialize the APC */
KeInitializeApc(&Irp->Tail.Apc,
&Thread->Tcb,
Irp->ApcEnvironment,
IopCompleteRequest,
NULL,
NULL,
KernelMode,
NULL);
/* Queue it */
KeInsertQueueApc(&Irp->Tail.Apc,
FileObject,
NULL, /* This is used for REPARSE stuff */
PriorityBoost);
假如用户层没有注册APC回调,那么IoFreeIrp就在IopCompleteRequest中完成;
而我们知道,APC函数触发时,会把其调度到同一个线程的上下文中才执行,所以当NtCancelIoFile执行KeRaiseIrql(APC_LEVEL, &OldIrql);时,就不需要采用自旋锁考虑多处理器的问题了,且这时如果能得到IRP,那么这个IRP一定有效,即还未调用IoFreeIrp;
其中wrk在NtCancelIoFile的KeRaiseIrql操作前有一句注释说的很明白,具体如下:
//
// Walk the list of IRPs on the thread's pending I/O queue looking for IRPs
// which specify the same file as the FileHandle refers to. For each IRP
// found, set its cancel flag. If no IRPs are found, simply complete the
// I/O here. The only synchronization needed here is to block out all APCs
// for this thread so that no I/O can complete and remove packets from the
// queue. No considerations need be made for multi-processing since this
// thread can only be running on one processor at a time and this routine
// has control of the thread for now.
//
KeRaiseIrql(APC_LEVEL, &irql);
假如在内核层直接调用IoCancelIrp,理由类似,也均不会出先我问题中所描述担忧情况;