Re: [linux-audio-dev] back to the API

New Message Reply About this list Date view Thread view Subject view Author view Other groups

Subject: Re: [linux-audio-dev] back to the API
From: David Olofson (audiality_AT_swipnet.se)
Date: ma loka   18 1999 - 23:53:33 EDT


On Tue, 19 Oct 1999, Paul Barton-Davis wrote:
> i see it oppositely: the API has to define what gets passed to various
> plugin function calls. By making it the plugin itself, the API becomes
> completely amenable to source-code compatible alteration. If you
> specify anything else, any time you want to change the argument type
> or number, everything needs to be recoded, not just recompiled.

How does it make a difference WRT binary level compatibility whether
the API specifies what the events should look like, or what a struct
should look like?

BTW, we need both kinds of specifications anyway, and I'd like to
get as close to keeping all important stuff in one of them as
possible. As we're dealing with a sample accurate system, the event
system seem to be the logical "main API", IMO.

> before I get going, some suggested terminology changes:
>
> destination "ports" are not at all similar to source "ports".

Agreed. That becomes obvious when studying the routing more closely.

> destinations contain LISP-like linked lists of ptrs to events in
> sources' "typical-C-like" linked lists.
>
> so ... i am using the term "event source" for a "source port" - a
> place where events appear from the engine's POV - and "event
> destination" for places where a pointer to the event gets posted
> (i.e. where they appear from a plugin/client POV).

Ok...
 
> > at the moment, i am preferring an implementation that uses fixed event
> >> sizes, since events are just "differences that make a difference".
> >
> >How big?
>
> sizeof (event_timestamp_field) +
> sizeof (event_type_field) +
> sizeof (event_extra_size_field) +
> sizeof (event_target_id_field) +
> sizeof (union {
> .... various data types ...
> })
>
> either way, i figure there should be two OOB mechanisms for events
> requiring more space than fits into the union: one would use a void *
> member of the union, and would point to space managed entirely by the
> plugin (via some allocation/deallocation API that we provide).

Major problem; this still has to be implemented. And it has to work
without shared memory...

> The
> other would be via an second such member, but would point to space
> allocated by the engine, and released by the engine after the event is
> processed.

Ok. Looks a lot like my qm_heap_t, except that you can allocate space
from there from outside the engine. Same problem with memory
fragmentation and/or allocation size restrictions.

[...good lookin' event struct...]
> if we believe that a common case is to pass around, say, new absolute
> values of structures rather than basic C types, then we can have a
> field in the union along the lines:
>
> unsigned char structure[EV_MAX_DATA_SIZE];
>
> and use it with casts. But I'd expect EV_MAX_DATA_SIZE to be small
> (say 16-32 bytes).
>
> note that events take essentially 3 arguments:
>
> - what changed (ev_target_id) - this may refer (from a plugin's
> point of view) to some C parameter in memory, or it
> may not - the engine doesn't care
> - how it changed (delta from old value, absolute new value,
> increment or decrement from old value)

Uhm, this seems to be asking for trouble with automation and editing
of automation data... Why deltas and inc/dec? What are plugins
allowed to send?

> - value (interpreted based on the kind of change)

Whether this is enough depends on what the event system will be used
for - and IMO, the design we're working on has too much potential to
be restricted to audio for the simple reason that you can't send more
than 32 bytes (or whatever) as a single event without extra trouble
and overhead. I believe things like a 3D sound FX system for games or
movie sound track editing would benefit from larger events... Should
we extend the "standard" when such needs become important?

(Not that we should design for the unknown future - that's a waste of
time and may make things worse - but 32 bytes seems little to me
already.)

> increments and decrements do not take a value argument. they are a
> special case that could arguably be done away with if they turn out to
> be infrequently used. it just struck me that the frequent use of C's
> ++ and -- operators might make it worth representing such events in a
> way that avoided having to do an extra assignment when setting up the
> event.

Don't know if that matters enough performance wise to support it, as
it's hardly the common case... BTW, many CPUs don't have special
INC/DEC instructions nowodays, and those that have often execute them
slower than ADD/SUB. (Not that it matters much - it's the memory
variable in the event that causes the overhead.)

> >> >As soon as the global heap of buffers is turned into a freelist, we
> >> >get search and splitt overhead, risk of complicated memory leak bugs,
> >> >deallocation overhead, and most importantly; _memory fragmentation_.
> >>
> >> nope. not if the objects are all the same size.
> >
> >Well, I was thinking about allocation of data buffers, not the
> >events themselves. Event memory management is not a problem, not even
> >with dynamic size with a sane size limit.
>
> i don't see why data buffers are such a problem. they don't get
> allocated and deallocated very often - its not even clear to me why
> malloc(3) is unacceptable. a plugin would establish its buffers at
> init time, might occasionally allocate some afterwards, and would
> destroy them when removed.

Yes, hopefully that will be enough... Have to think more about that.

> > Is it safe to split too big events into multiple
> >events, with respect to ordering of events with the same timestamp?
> >(I think they should come in the order they're sent, but what if
> >someone else is sending the same kind of events to the same place?)
>
> can't happen. events appear at an event source. only one "object" has
> access to the event source. the event can go to many event
> destinations but thats different: these are just LISP-like lists of
> pointers to events. the object that owns the event source posts events
> to it in the order received, so that when the engine inspects all
> active event sources, it finds them already sorted in time order on
> any given event source. its job is to build the event cell lists for
> each destination, which requires mux-ing each relevant source and
> sorting by timeorder while so doing. thats all.

So, you have to force every event source to use a unique kind of
event? Ok, it's rather silly to connect two sources of the same kind
of events to the same input - it should probably just not be allowed.

> i should have the prototype code working tomorrow.
>
> >> and of course, note that since only the engine allocates and
> >> deallocates from both pools, no locks are necessary.
> >
> >How do you send events to the engine from other threads in that case?
>
> the engine is subscribed to all event sources. before building the
> event lists for each subscriber to all active event sources, it has to
> make a pass through the current events to see if there is anything
> earmarked for it. it does this by inspecting the ev_target_id field,
> to see if any of the message concern any of its "parameters".
>
> but either way, the quote from me above is misleading. each
> event_source has its own event pool. its only the event_cell pool
> (used to build event lists for destinations) that is owned by the
> engine.

Ok.

> >real time system. And unless the lookup really finds a string most of
> >the time, you might as well copy the data right away.
>
> fair point. probably quite a reasonable pragmatic approach. there are
> certainly some pathological cases where its the wrong thing to do, but
> they would reflect a combination of bad plugin GUI programming and a
> bad user interface.
>
> BTW: can we *please, Please, PLEASE* avoid the pseudo-OOP code ala GTK
> with "simple public header structs" etc. ? C simply cannot provide
> "private" and "public" compile-time safety, so trying to provide
> it only makes the code harder to read, and the macros more numerous. I
> am sick of writing stuff in GTK like:
>
> gc = GTK_WIDGET(t)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(t))];
>
> all of which reflects this use of "header structs" that became so
> prevalent as many people wanted the benefits of OOP without the cost.
>
> Lets just make the structs open and clear, and mark areas that are
> off-limits to plugins etc. with clear comments. It will make our life
> much easier down the road, I think.

Good point, but that's not exactly what I meant. How could you
possibly give the engine anything but a void * to you closure, if
it's not 100% defined in the API, or 100% private to the plugin?
Further, how do you optimize engines without breaking plugin binary
compatibility, if you have "private" implementation stuff in the API?

It's going to be some uggly code somewhere, but I'd rather have that
in the engine, so there will be nothing of the above in plugin code.
*Possibly*, there will be special instantiation calls that allow the
engine to add internal stuff to the same memory blocks as some
structs, but that would have to be *strongly* motivated.

> The only exception I have ever come across for this has been generic
> lists, which allow manipulation of any object in a list as long as its
> initial structure shares the same declarations as the "generic list
> element". I haven't yet found a place in this system where that might
> be useful.

Neither have I. Most of the structures will be handled by inline code
in plugins anyway, for performance reasons. The things that are
implementation specific shouldn't show up in the API spec at all,
not even as macros containing things like 'char _private_stuff[64]' or
whatever. That might not be enough anyway, and results in *really*
messy engine source code still without really isolating the
implementation from the interface - the hardcoded struct size is
still there... It would probably be nicer to have the engine
allocate the memory, or in the case of the closure/instance struct, A
void * to something "engine private".

//David

 ·A·U·D·I·A·L·I·T·Y· P r o f e s s i o n a l L i n u x A u d i o
- - ------------------------------------------------------------- - -
    ·Rock Solid David Olofson:
    ·Low Latency www.angelfire.com/or/audiality ·Audio Hacker
    ·Plug-Ins audiality_AT_swipnet.se ·Linux Advocate
    ·Open Source ·Singer/Composer


New Message Reply About this list Date view Thread view Subject view Author view Other groups

This archive was generated by hypermail 2b28 : pe maalis 10 2000 - 07:27:59 EST