On 13 Apr 2006, at 20:24, Jeremy Bettis wrote:
Someone broke NSFileHandles pointing to pipes on mingw, so I am
looking at the right way to fix it.
It's been broken a long time (assuming it ever worked) ... at least
since the rewrite to use native windows event handling.
Ok, so here is the problem. On windows, a file handle to a pipe or
a file (as opposed to a network socket) doesn't signal when you can
read/write. This is very different from unix. For normal files
you can use overlapped IO to read in the background, however for
pipes you cannot. The only way to have a handle that you could add
to the NSRunLoop you would have to:
1) Create an event object (CreateEvent)
2) Create a new thread, which will block reading/writing data on
the pipe handle
3) When the thread gets data signal the event object
4) The main thread can wait on the event object using
WaitForMultipleObjects.
Yes ... pipes on windows are fairly horrible ... but named pipes are
not quite as bad. With a named pipe you can do overlapped I/O, which
removes the need to create a separate thread.
I implemented NSStream support for pipes using windows named pipes
with unique hidden names for the pipes ... so you can treat them as
anonymous pipe streams as far as the public API is concerned.
The intention was to re-implement NSPipe on top of NSStream so that
the complicated/messy low-level code would be localised (and later to
reimplement other I/O code in terms of NSStream too).
This seems like it might be overkill. So I noticed that NSRunLoop
has a ET_TRIGGER event type which the comments say is for the
NSStream class. So I implemented NSFileHandle to use this,
however, now if you call -[NSFileHandle readInBackgroundAndNotify]
the program will go to 100% cpu usage.
What is the right way to use this ET_TRIGGER mode?
The current SVN code (experimental) adds ET_TRIGGER as a watcher type
which is triggered every time the loop is run ... so if an object of
this type sits in the loop without doing anything else. you would
expect the process to use all the free CPU. This allows you to add a
watcher which will be triggered as soon as the loop is run and then
remove itsself.
However, there is also a new method for watchers ... if the object
responds to the -runLoopShouldBlock: selector, the loop will call it
immediately before each iteration of the loop and override behavior
as follows...
The argument to the method is used to return a BOOL value which tells
the run loop whether the whether is to be considered to be in a
'triggered' state or not. If it is YES, the loop will call the
callback method of the object once it has done its select/poll/sleep.
The return value of the method specifies whether the loop should
block or not. If it is YES then the loop will add the resource of
the watcher to the set of select/poll inputs it waits for, otherwise
it will not.
If any watcher returns YES for both values (ie it is in a triggered
state and the loop should not block for it), then the poll/select
will have an instant timeout as it means there is already a triggered
event available to process. This is the default behavior assumed for
a watcher of type ET_TRIGGER which does not respond to -
runLoopShouldBlock:
The fun thing about this is the ability for a watcher to dynamically
modify the way the runloop handles it depending on its current
state. It allows NSStream to set off an overlapped read/write and
have the loop block waiting for completion when no other I/O is
possible, but also have the loop repeatedly trigger as long as the
client code has not consumed all the data read or provided anythign
to write.
_______________________________________________
Gnustep-dev mailing list
Gnustep-dev@gnu.org
http://lists.gnu.org/mailman/listinfo/gnustep-dev