Mr. Bassov… REALLY?
Indeed…
The problem here isn’t the use of the Alertable _parameter at all, it’s the overall design of how the driver does the wait, isn’t it.
Doing a wait that might never be satisfied isn’t a recipe for creating good code. Yes, you can counter this
“wait might never be satisfied” by setting _Alertable _to TRUE. But the problem isn’t, per se, setting _Alertable to FALSE…
the problem is that the wait might never be terminated.
Actually, I don’t see any “problem” here - after all, if the (possibly negative) event that you may potentially want to react to does not occur, why do you think it should necessarily pose a problem to anyone? More on it below
Have you ever coded an Alertable wait, Mr. Bassov? Do tell us, please. Share with us how you’ve recently done this in
your vast Windows kernel-mode development experience.
Well, although, luckily for me, my " (not-so) vast Windows kernel-mode development experience" had ended in the early
Windows Vista days, I can easily recall using the Alertable KM wait back in the Windows XP ones…
I had a packet filter that might potentially want to do some data processing in the userland before (possibly, but not necessarily)
re-injecting it into the network stack. Therefore, I had an NDIS IM filter and a userland app that communicated with it via a standalone DO created by NdisIMRegisterDevice().
As far as data capturing path was concerned, my filter handled 2 types of IOCTLs (both used METHOD_BUFFERED IO method). The only thing that IOCTL of the type A was doing was waiting (yes, it was an Alertable wait) on a synch (i.e. auto-reset) event that I was using as a binary semaphore. When my filter’s data-receiving path detected the data of interest it was placing a NDIS packet in question on the list, and signaling the above mentioned event if the list was empty at the moment. After KeWaitForSingleObject() had returned control IOCTL A handler indicated to the app how much (if any at all )data was available on the list at the moment by completing the request. It was doing so regardless of the return value of KeWaitForSingleObject() - the only thing it was concerned about was data availability.
After IOCTL of type A had been completed the app was submitting (possibly multiple) IOCTLs of type B that were actually copying data from NDIS packets on the list to the system buffer until no more packets were left on the list. After that the app signaled the userland event that its actual data-processing part was waiting on, and the infinite ( schematic , of course)
“while(1) { if( IOCTL_A() ==DATA_AVAILABLE) { IOCTL_B(); SetEvent(C);} }”
loop that data-capturing one was based upon continued.
With this approach I could easily handle an app termination without having had to worry even about KeWaitForSingleObject()'s
return value, let alone about when (and if) the data of interest having had arrived. Why do you think it is, in your terms, " isn’t a recipe for creating good code"? What is so terribly wrong with this approach? Could you please explain it to me.
Certainly, you can point out to me that I had to submit unnecessary IOCTLs of type A in (extremely unlikely) case if the wait was interrupted by the user APC at the time when no data was actually available. Even more, I could have done some extra optimisations (like, for example, a shared buffer) for the sake of the extra efficiency. However, this part would come at the cost of the additional code complexity which simply did not make sense, especially taking into the account that the above mentioned “simplistic” approach sufficed for the purpose.
P.S. Please note that all the above took place well before the advent of the “inverted calls”, let alone of “Big Honking IRPs”, so that, objectively, I cannot be blamed for ignoring “the best coding practices” - they simply did not exist at the time .
Also, if you’re going to quote Mr. Roberts, have the courtesy and good engineering discipline to quote him fully:
Well, probably we just happen to be abiding by the totally different “Rules of Thought”, but, in my understanding, the above mentioned statement consists of 2 mutually exclusive parts that simply cannot get reconciled with one another. In my understanding of things, you may either “Always do XYZ. Always.” (i.e. unconditionally do XYZ no matter what), or, alternatively, you may “need to violate this rule”
(i.e. conditionally avoid doing XYZ in some rare cases).
In other words, the statement in question sounds (at least to me) pretty much like
“Always have your cake with you. Always. When you need to eat it at the same time …(etc)”
Therefore, I quoted only the part that I disagreed with…
Anton Bassov