wednesday, 13 june 2007

posted at 08:51

Named pipes are like normal files in that they can be created and deleted. The difference is that when you read from it, nothing happens until something else writes to it. Then the reader gets a copy of whatever the writer wrote. Usually there can be multiple readers and writers.

We need to create an "unnamed" pipe. This is different from a named pipe in the following ways:

  • When the last reader/writer closes it, it disappears.
  • Because it has no name, there's no way to open it again onces its created.

AROS implements it by opening PIPEFS:__UNNAMED__ for read, then using the "duplicate with new mode" misfeature of Open() on this handle to get the write half. Internally, pipefs.handler recognises __UNNAMED__ and sets a flag to tell it to automatically destroy the pipe when the last opener drops off.

This is currently not working because I changed Open("", mode) to be implemented as (sans error checking):

    lock = LockFromFH(pr->pr_CurrentDir);
    newfile = OpenFromLock(lock);

As you can see, it entirely ignores the new mode. The way it used to work before I broke it was to dig the device and unit out of the "lock" (actually a filehandle on AROS), then call FSA_OPEN_FILE with the new mode. I could simply revert to this behaviour, but longer term this won't work becasue locks and filehandles won't be equivalent any more, which means the handle can't be assigned to pr_CurrentDir, and so Open("", mode) won't know what its supposed to be duplicating.

OpenFromLock() (or its 1.3 Open("") counterpart mentioned previously) also can't be used because not all filesystems use locks - there's no guarantee that a lock can be obtained from a filehandle. Of course we could just make sure that the pipe handler does use locks, but that places a fairly big restriction on its internals, making it harder to be replaced.

I've done some research it seems that there's no standard interface for unnamed pipes (where they've even been available). The usual way seems to be to generate a unique filename (based on a timestamp) and use that. It works well enough, but it does require that the pipe be deleted afterwards. There was also something called ConMan that had a ACTION_DOUBLE packet that would return a pair of handles (like the POSIX pipe() system call). I really like that approach, but would prefer to not have to extend the API.

On the other hand I can't see a way to do it without extending the API. For the pipe to be truly unnamed, you need to be able to return two handles from the same call (like ACTION_DOUBLE). Its not a terrible approach.

Do we really need unnamed pipes? The only place its currently used is in the shell (to implement '|'), so could the shell just have it built in? Of course it could, but the implementation would be almost as complex as a handler anyway, and it makes sense to have the function available to other things, like POSIX emulation (pipe(), popen(), etc). Obviously they'd be a good thing to have.

I can think of other ways to do it where the handler could infer the requirement for a private, auto-closing pipe (like two open calls on the same name immediately after each other, one for read and one for write, followed by some other call, but that kind of thing is too easy for a programmer to get wrong, and doesn't read well. I think a direct call is what we want.

I'm going to look at two approaches: An AROS-specific Pipe() call in DOS (LONG Pipe(BPTR *reader, BPTR *writer)) and if that can't work (eg not enough space in the table), a new "mode" like ACTION_DOUBLE. I'll start experimenting tomorrow, most likely.