Hey Martin,
Another day, another problem. :-)
You can't unconditionally use overlapped operations... the code used to do that exactly, and it caused all sorts of breakages. eg. some (braindead) programs check that there is data is ready to read, then do an overlapped read, but expect it *not* to go overlapped.
I don't think WaitForMultipleObjectsEx(0,NULL,FALSE,0,TRUE) then read, is the right way...
I think the best solution is to let the server decide. Something like this:
BOOL ReadFile_overlapped () { ovp = new async_private; while(1) { ovp->func = FILE_AsyncReadService; wine_server_call(register_async); switch(GetLastError()) { case STATUS_SUCCESS: ovp->func(ovp); break; case STATUS_PENDING: return FALSE; case STATUS_TIMEOUT: goto finish_it; default: delete ovp; return FALSE; } if(count >= bytes_to_read) break; } finish_it: delete ovp; *bytes_read = count; }
This is the kind of structure i was trying to describe to you in a previous private mail... i was trying to address a different problem but it should address the one you mention too. In fact, WaitCommEvent also needs to work in this way. (Somebody on wine-users has a program that needs that behaviour now.)
There's a minefield of special cases... but the essence is that the wine server instructs the client to do one of the following things:
* perform the action (read/write/etc) * return from the call with success * return from the call with ERROR_PENDING * wait for an event flag (ie. not return yet)
Does that make sense to you?
Mike
Original message from: Martin Wilck Martin.Wilck@fujitsu-siemens.com
Hi Mike,
I just had the following thought on the immediate IO in ReadFile() / WriteFile() that you implemented:
Consider the following situation:
- There is no data available for reading on some fd (serial port,
pipe,
socket). 2. An application calls ReadFile() in overlapped mode. since no data is there, an overlapped request is scheduled. 3. Data becomes available before the app issues a wait request of any kind. 4. The application starts another ReadFile() request. Now data is there, and will be read immediately. No async request is scheduled. 5. Eventually, more data will become available, and the async request first scheduled will also return.
However, this basically destroys the order of the requests. I am unsure how Windows would behave in such a case, and it is
certainly
difficult to provide a test case for this situation. It can occur,
though,
and my guess is this is the wrong behaviour.
Thus, probably before doing the immediate read it should be checked if there are asynchronous requests already scheduled for reading, and if yes, ReadFile() should _not_ try to read immediately but go pending. Of course, this check would require a server call.
The same reasoning applies for writing, of course.
Things become even more confusing if you think of several threads reading from the same file.
A very simple workaround would be to go pending unconditonally for overlapped requests. Although not optimal, this would be certain not to corrupt data.
What do you think?
Martin
------------------------------------------ mailto:Mike_McCormack@start.com.au ph +82 16 430 0425
__________________________________________________________________ Get your free Australian email account at http://www.Looksmart.com.au
Hi Mike,
Another day, another problem. :-)
Indeed :-/
You can't unconditionally use overlapped operations...
OK, forget it. But we agree that to read() unconditionally is also The Wrong Thing (tm)?
I don't think WaitForMultipleObjectsEx(0,NULL,FALSE,0,TRUE) then read, is the right way...
My idea was rather WaitForSingleObject (overlapped->hEvent, 0). Why can't that work? AFAICS,
- the thread enters the wait state. - If data is available, the server will signal that. * If our request is first in the queue, it will read the data and return STATUS_SUCCESS (as your braindead app expects). It's hEvent will be activated, and we will leave the wait state with SUCCESS. * Otherwise, another async request will get activated, and we will receive a WAIT_TIMEOUT. After that we go pending (what else could we do?) - If no data is available, we'll also receive a timeout, and go pending as well. Again, there seems to be no other option.
I think the best solution is to let the server decide.
Basically this approach means we *do* let the server decide, or am I missing something ?
The only problematic case I can think of is: - a 1kB read request pending, - >2kB data arrive, - another 1kB read request.
In principle there would be enough data for the second read to complete with SUCCESS, but I am not sure my approach guarantees it, nor how Windows handles it. Compatibiliy has its limits here, since on Windows the requests run truly asynchronously, in Wine they don't.
Some (braindead) programs check that there is data is ready to read, then do an overlapped read, but expect it *not* to go overlapped.
How do you check if data is ready in Windows? I didn't find a select() equivalent, nor is it possible to wait on file handles.
switch(GetLastError()) { case STATUS_SUCCESS: ovp->func(ovp); break;
In what case would register_async return STATUS_SUCCESS ?
There's a minefield of special cases... but the essence is that the wine server instructs the client to do one of the following things:
- perform the action (read/write/etc)
- return from the call with success
- return from the call with ERROR_PENDING
- wait for an event flag (ie. not return yet)
What is the difference between case 1 and 2 ? Case 4 is excluded for overlapped operations. They are supposed to return "immediately" in any case. Waiting with timeout 0 seems to be justified IMO, but all else would be against the specs.
Wrt the minefield, a *really* braindead app could also schedule some overlapped requests followed by a non-overlapped one (at least in winsock that's possible, for file IO I think it's forbidden). I have no idea how windows behaves in that case.
Martin
On Wed, 9 Jan 2002, Martin Wilck wrote: ...
How do you check if data is ready in Windows? I didn't find a select() equivalent, nor is it possible to wait on file handles.
GetCommError and some other functions, I think, returns lpStat->cbInQue, which is in effect the number of characters immediately available to be read.
Martin
Lawson ---oof---