Hello,
SmokingRope wrote:
Hi, I'm poking through the pieces of the System.Core/System.IO.Pipes
code looking to build out anonymous pipes. I have at least a basic
working Unix implementation now. Though i imagine Win32 and Named
pipes have been broken (if they ever worked) in the process, and there
is undoubtedly some cleanup work required. I'd like to get some
feedback from people more knowledgeable than myself about mono at this
point so i can try and wrap this project up into a pull request.
That's great! Thanks for taking care of this.
I'm skipping some parts of your questions and writing for some parts
that I can answer...
My initial test case is this example from MSDN:
http://msdn.microsoft.com/en-us/library/bb546102.aspx
Handle Inheritance
-------------------------
Presently, Process.Start() arbitrarily closes all handles in the
fork()'d process, except stdin / stdout / stderr. There exists a todo
comment in this same code (io-layer/processes.c) pointing out that
(when inherit_handles is TRUE, which it is) that something should be
done differently than when not...
if (/inherit_handles/ != TRUE) {
/* FIXME: do something here */
}
Without a far more expansive knowledge of mono i can't make a very
well informed decision about the liberties i can take in addressing this.
The cleanest solution might be to use FD_CLOEXEC on all the existing
fd's and (at least in process.Start case) the handles that should be
cleaned up would be closed automatically / inherited automatically.
Are there compatibility reasons this cannot be done? Has there been
any consideration for this that could be drawn upon?
>From information i do have, it seems like these pipe handles could
also be flagged as 'inheritable' in the internal handle registry and
then after fork() mono could leave those open, while closing
everything else.
In addition to this basic test case, i'm not currently aware of how
anonymous pipes should behave when you invoke Process.Start() several
times with an anonymous pipe. Without using FD_CLOEXEC it would be
difficult and messy to (in a 'execd' child process) to identify all
the pipe handles at startup and register them in the mono handle
registry as inheritable again (so that Process.Start in that child
process would pass these handles on again to its children). If
anonymous pipes are supposed to support this, it lends further support
for the FD_CLOEXEC solution.
API Methods Difficult To Implement In Unix
-------------------------
There doesn't seem to be a clear specification for the anonymous pipe
feature and i've so far had to make some little assumptions /
inferences based on the documentation on MSDN. That said, the posix
anonymous pipe standard lacks a few features which are pertinent to
the API for anonymous pipe streams. In particular there are two
conflicting features:
* WaitForPipeDrain
<http://msdn.microsoft.com/en-us/library/system.io.pipes.pipestream.waitforpipedrain.aspx>()
defined on the AnonymousPipeStream requires some ability to peak into
an anonymous pipe and wait for the pipe's buffer to become empty
* Write / Read / etc... is supposed to throw an IOException if
the other end of the pipe stream has been closed (i have interpreted
the wording 'pipe is broken' from the msdn docs to mean this, although
there is a chance i am mis-interpreting this too)
These two features conflict when trying to implement them in unix
through minor hacks / workarounds.
I can implement WaitForPipeDrain() by leaving the read end of the pipe
handle open in the writer process, and calling poll(3) on the read end
repeatedly until the POLL_IN flag is gone.
I can detect that the pipe is broken by closing the read end on the
writer, (and the write end on the reader) and calling poll(3),
throwing this "pipe broken" error when a POLLERR /. POLLHUP is issued.
One of these two flags will be set if all handles to the other end of
the pipe have been closed.
In order to implement both of these conflicting features i actually
need to create two pipes (one where i can leave the read end open on
the writer process, and one where i can close it). This is not very
nice in the case that system resources might be at a premium, however
i haven't been able to devise any other way to get both features
either. Would there be any major objection to implementing it like
this (which as i have noted seems to be the only way)?
I would say that opening two pipes is of less concern. If people need
very efficient named pipes then they should rather use platform-specific
stuff.
Using two separate pipes like this, the GetClientHandleAsString()
method is going to be significantly different from the win32 version.
When the client is supposed to be the writer, the client handle as
string must contain three separate fds. When the client is the reader
two fd's must be passed. I don't see any reason that this would bother
anybody, but if it's relevant let me know!
That method can return totally different string than what .NET returns.
It's weird that if an application totally depends on certain format of
the return values of it and I believe we can ignore them.
Compatibility
-------------------------
I can create pipes and manipulate them without adding any new library
references but i do depend on using the poll(3) function more heavily
than it seems to be presently (it's only exposed for Network Sockets
presently). Are there any portability / compatibility considerations
with poll(3) that might need to be addressed?
I guess Mono.Posix.dll has a lot more bound API that you want to use (it
is already referenced by System.Core.dll for desktop CLR_PROFILE in
System.Core/Makefile).
If that still needs to touch our runtime (io-layer) then it is of our
concern yes, as we are building runtime not only for desktop. Yet we
have couple of defs (PLATFORM_ANDROID etc.), so as long as things can be
easily conditioned out it would work.
Versioning
-------------------------
I'm not familiar with how versioning between .NET 1/2/3/etc... is
being handled. Anonymous pipes are introduced in .NET 3.5. Do these
classes need to be hidden to older versions, etc... Maybe there is a
wiki page i haven't found discussing some of this?
Our least supported Framework API is 2.0 runtime, which covers 2.0, 3.0,
3.5 and 3.5 SP1 API. So, you don't need to add any condition. For 4.0
specific types and members, use "#if NET_4_0" for all of them (you can
hide them by changing them as internal for !NET_4_0).
I have added several new internal calls to the MonoIO utility,
specifically for poll(3) and pipe handle manipulation. Does adding
internal calls require any versioning considerations?
Testing
-------------------------
In order to write a test suite for anonymous pipes i need to start new
processes and i will need to use some sort of IPC mechanism to
communicate certain errors back to the test process. This seems like a
rather unique challenge compared to most of the testing needed for mono.
Has any of this multi-process unit testing been done before in mono,
or are there any considerations that might be relevant?
Not everything has to be in our existing NUnit test style. We have a
couple of standalone tests (e.g. in System.Web.Services or System.Xml).
Probably you can create your own test runner to deal with multiple
processes.
Please do not hesitate to show us your ongoing changes. I'm sure that
almost no one cared this API (at least no one contributed to the
implementation), so you're most likely the one at best knowledge here :)
And with code people can talk better.
Atsushi Eno
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list