[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Channel sessions

From: Carl Fredrik Hammar
Subject: Re: Channel sessions
Date: Mon, 13 Aug 2007 15:08:27 +0200
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/22.1.50 (gnu/linux)


<olafBuddenhagen@gmx.net> writes:
> Well, I think we generally failed to define a clear terminology. Or
> maybe you actually did, and I only fail to stick to it. In any case, to
> make things clear, I'll explain what terms seem most logical to me. Let
> me know what you think of that. I have so far (mostly) been using all
> terms in the way I'm defining them here, and will also do so for the
> rest of this mail.

I've been struggling to get a solid terminology with this and although
we've been talking past each-other a bit, I'm glad you're helping me
with this.

> Channel is the general term for the whole concept, and thus can be used
> in various ways. The most strict meaning proably would be an entity that
> can be accessed through the channel interface, i.e. a filesystem node
> served by a channel translator. (To avoid ambiguity, one could also call
> that a channel endpoint or so.) However, depending on context, channel
> might also refer to other things.

I will use channel for the object that a client interacts with,
i.e. one is created whenever the translator is opened.

> A channel module is a single unit implementing the channel interface,
> running within libchannel or in a channel translator. It has a client
> side, which can be access through a filesystem node or through
> libchannel; and a device side, which can access either a Mach device, or
> the client side of another channel module. A channel stack is a layering
> of several channel translators/modules.

This object I have opted to call a hub, ``module'' would perhaps be a
bit better.  However, I don't want to stray to much from libstore's
terminology.  And in libstore module is the shared object containing a
class that can be loaded dynamically.  This is how I will use the

I might revise this later.

> A channel class for me sounds like a specific kind of channel with
> certain properties, e.g. an audio channel or a network channel. However,
> as you are using this term in a totally different meaning, I try to
> avoid using it this way. Channel type also describes it quite well I
> think.

A type is simply the identifier of a class.  A class is the entity
implementing a type, similar to its use in OOP languages, with methods
and other constant data.  Type and class is sometimes used

Instead of client and device side, I find front and back-end more
appropriate.  Keeping device for Mach devices and the channel type
that wraps them.

> An output channel is a channel that passed data from the client side to
> the device side; an input channel is the opposite.

Okay, lets keep it in that direction.

> A channel instance is an individual client connection to a channel. Most
> modules will pass through every instance individually, but some will
> take several instances on the client side and open only one instance on
> the device side.
> A stream is similar to an instance, except that a bidirectional channel
> usually has one input stream and one output stream per instance. This
> term is somewhat blurry, as I'm not sure how this should actually be
> handled in practice. As I pointed out, one possible variant could be
> creating modules that will pass on the output streams of all instances
> individually, but only a joined input stream, or the other way round...
> Which would make the relation of streams and channel instances pretty
> confusing. (Other variants are more clear.)

I agree that a stream is a unidirectional concept, but I will use it
in a more abstract fashion.  Namely the data passing through a
channel, e.g. all clients of a tee will receive the same input stream,
while a fifo will splice it into one stream per client.

For your usage, I will simply say input or output channel.

> Henceforth, I will use "junction" for any module that merges or splits
> channels or channel instances.

Yes, ``junction'' fits nicely.

>> The unwieldy part is that instead of giving a --buffer or --no-buffer
>> flag you now have to use channel layering syntax, e.g.
>> ``buffer:dev:dsp'' (or something like that, I haven't given much
>> thought to the syntax.)
> I don't understand the reason for that. Why can't --buffer or
> --no-buffer be used? In storeio, you also have a number of optional
> flags, and if you give several of them, you get implicit layering,
> although you only set a single translator -- without using the
> "unwieldy" layering syntax...

Yes, that is an option in many cases.  But for buffering, position in
the stack may be crucial.  Consider a tee, buffer, device stack, where
the tee will only open a single channel to the buffer.  If the buffer
were on top, there would have to be one buffer per channel, which is
redundant since the same stream is passing through all of them.

However, it might be the case that buffering only makes sense over
devices and file channels in which case implicit layering might be

>> >> Multiplexors are primarily used to gain multiple sessions to
>> >> exclusive access devices and also balances io over sub-channels.
>> >> E.g. they multiplex in both directions.
>> >
>> > Why do you want to merge these functionalities? They seem totally
>> > independant -- I can't think of any situation where one would want
>> > to use both together.
>> >
>> > Maybe there *are* cases where you need multiple inputs and multiple
>> > outputs in a single component, so it's probably reasonable to leave
>> > this option open; but I don't think it should be used in any of the
>> > standard modules.
>> I agree that they are functionally independent and that performing
>> both is uncommon.  But they are conceptually similar, they do the same
>> thing only in different directions.  This way we don't need an
>> `in-tee' and an `out-tee'.
>> The good news is that since they are functionally independent, they
>> don't get in the way of each-other.  Normally you would only layer it
>> over a single channel, in which case input would simply be forwarded
>> to the back-end in a fifo manner.
> Well, I don't see the point in a translator/module that -- depending on
> context -- will either do one thing, or another totally unrelated thing.
> (And in very seldom cases both.) Even moreso as I can see multiple
> variants for both functionalities -- any combination would be totally
> arbitrary.
> Also, I thing there still might be some confusion here. It seems very
> important that we get a common understanding of the topology.
> There are basically two kinds of junctions regarding the topology:
> Multi-client junctions and multi-device junctions. Those are two totally
> different things, serving very different purposes, and usually not used
> together.

In not sure if this is what you were getting at, but your talk of
topology got me thinking.  When faced with a back-end junction I was
assuming that one only wants one channel per back-end, leaving only
one channel to be shared by clients.  This forced me to choose
front-end junction as well, an arbitrary choice, with in-tee being the
closest at hand.

However, it is now clear to me that when a back-end junction channel
is opened, new channels to all back-ends should be opened.  By
layering a front-end junction over the back-end junction one can still
have only one channel per back-end.

I've tried to capture the situation in this diagram, where the
rectangles are hubs and each `o' is an open channel to it and the
lines show which underlying channel they use.

                                               ---------+-o |
                                              / --------+-o |
     +------+                                / /        | . |
     |in-tee|                               / /         | . |
     +------+     +---+                    / /          | . |
     |   o--+--+  |dev|                   / /     ------+-o |
     |   o--++ |  +---+       +-------+--/-/-----/+     +---+
     |   .  |+-+--+-o |       |out-tee| o o ... o |          
     |   .  |  |  +---+       +-------+--\-\-----\+     +---+
     |   .  |  |                          \ \     \     |dev|
     |   o--+--+                           \ \     \    +---+
     +------+                               \ \     ----+-o |
                                             \ ---------+-o |
                                              \         | . |
                                               \        | . |
                                                \       | . |
                                                 -------+-o |

> For each of these base kinds, there are two diretions (input our
> output); for bi-directional channels, usually a pair is necessary.
> However, the pairing is not always obvious. In your example, we had two
> clients that read from the same audio device. What we need here in the
> tee I desribed, which is a muliti-client input junction. What is the
> complementary multi-client output junction? In the audio case, probably
> an arithmetic mixing makes most sense -- the tee allows multiple clients
> to simultaneously read from the same device, and the mixer allows
> multiple clients to simultaneously output to the same device. In other
> cases however, like networking for example, we would obviouly need
> something else to provide complementary functionality...

A mixer would be the most appropriate way to funnel output streams
into a single one.  But that obviously only works for audio.  For a
general purpose front-end junction, the simplest and most neutral way
would be used, which would be a fifo (or perhaps it should be called a
fofi. ;-))

> Now what about the out-tee? This is somewhat confusing: While the in-tee
> is a multi-client input junction, the out-tee is a multi-device output
> junction -- something totally unrelated with very different use cases.
> Also, althouh the basic idea of duplicating streams is the same,
> forwarding from one client to multiple devices is totally different
> implementation-wise than forwarding from one device to multiple
> clients...

Yes, I am well aware of the differences.  The out-tee is much, much
simpler.  Handling the case when some channels of an in-tee aren't so
that other channels don't stall is tricky.

> The situation is even more confusing for fifo. There are two distinct
> kinds: A split-fifo (with a topology like the tee), and a merge-fifo;
> each having in- and out-variants. (Maybe better leave out the "fifo"
> alltogether and give them totally distinct names instead, to reduce
> confusion...)

What would a merge-fifo be?  I don't think we've brought that one up
before.  I have always been referring to split-fifo (I think.)

> Furthermore, for every variant, multiple policies are possible: If there
> are outstanding requests from several connections, how to shedule them?
> Always prefer the first one and fallback to others only if the first is
> busy? Use round-robin? Or yet something else?

I would go with round-robin, skipping already busy channels.  For a
generic fifo that is, I agree that it is domain specific problem.

> All this only convinces me more and more, that there are very few -- if
> any -- generic modules useful for various types of channels; it's more
> like every channel type will have a distinct set of modules useful for
> this particular type. This is what I felt from when I first learned
> about the channel concept, and in fact this made me very sceptical about
> the whole concept for a long time. Only recently I realized that even if
> every channel type uses totally different modules, it still makes sense
> to use a common framework...

Yes, it seems to be going in that direction.  However there are still
many modules that can be shared, especially if we break down
functionality of each module even further, e.g. making out-tee
write-only, forcing one to split channels.

To handle the complex syntax this would lead to, one can introduce
scripts that only understands how to sets up a specific system,
e.g. audioio.

>> > This brings up another problem: The fact that for some kinds of
>> > channels, input and output are rather independant, and may be
>> > handled differently.
>> >
>> > [...]
>> >
>> > I can think of several approaches for that. One possibility is, if
>> > you have a module that merges outputs for example, i.e. from all the
>> > client outputs generates only a single output stream to the device,
>> > to simply pass through all client inputs as distinct streams
>> > (channel instances). You could then put another translator below
>> > that, which for example duplicates all the inputs from a single
>> > device stream, and directly passes through the output stream.
>> >
>> > Another possibility would be making the input/output split explicit,
>> > using a dedicated pair of modules for that: First you have a module
>> > that splits each client session into an output stream and an input
>> > stream, forwarded to different backends. The output and input
>> > backend then could be implemented by totally distinct translator
>> > paths (sitting on different FS nodes). Finally, the other dedicated
>> > module would merge output and input stream again and forward them to
>> > a common device backend.
>> >
>> > The above may be the most elegant and transparent solution, but
>> > could be a bit cumbersome due to the many translators one needs to
>> > set explicitely. Yet another variant could be using a single special
>> > translator that internally splits output and input, and passes them
>> > to two extra modules specified as command line parameters.
>> Yes this would be the right way to go.  I have already thought about
>> the whole in/out splitting and reached the same conclusions.
>> A third would be to have two simplex channels instead of a duplex one,
>> all the way through.  While it's probably the most generall solution,
>> it seems to much to cumbersome.
> This is in fact my second suggestion (as opposed to the third which you
> commented on above): You could split the directions immediately before
> the direction-dependant modules, and join them again directly
> afterwards; but you could also do the split at the very bottom of the
> stack, and join only at the very top... The mechanism is the same.
> Every of the variants I suggested has a specific appeal: The first
> variant is elegant in the sense that you never explicitely split the
> directions -- instead, just stack various modules, some handling the
> input case and some the output case. The third variant is elegant in the
> sense that every translator handles both directions, the split happening
> only internally. The second variant is elegant in that it doesn't really
> require any special handling: It just uses generic direction
> splitting/merging modules, which are implemented just the same as other
> modules (i.e. the framework doesn't need any extra facilities to handle
> bidirectionality), and can be useful also in other scenarios anyways.
> Indeed the fact that it doesn't require special handling, strongly
> suggests that this variant is probably the most reasonable to use for
> the beginning -- if it turns out too cumbersome in practice, something
> more involved still can be devised...
> -antrik-

Sorry for the confusion, I didn't realize that between your second and
third options were different.

One more thing to think about is making sure that when a in and out
channel is rejoined that they belong to the same client.  This might
very well happen in a naive implementation of a joiner, that simply
pairs the last in channel with the next out channel or vice versa.
Considering that it is possible to open read or write only channels.

In fact I think this rules out option one and three.  A modified
version of three might work though, where the split and join is in a
single module that maintains two separate stacks.  Lets call this
option four.

Another way to tackle this would be to supply a client id along with
the channel when it is opened, allowing the relevant party to pair up
channels.  This would allow option one and three to be implemented.
But the client id can't be forwarded across translators (unless we
expand the IPC protocol,) so channel would have to be paired up before
this point.

libchannel also handles control interfaces, analogous to ioctls.  This
just means that channels can handle any IPC messages not recognized by
the translator.  It just occurred to me that one might want to split
them into separate paths as well.  But this can be handled similarly
to in/out splitting.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]