On Mon, 2015-01-19 at 13:57 -0500, Rafael Schloming wrote:
> On Mon, Jan 19, 2015 at 11:52 AM, Alan Conway <acon...@redhat.com> wrote:
> 
> > On Wed, 2015-01-14 at 08:28 -0500, Rafael Schloming wrote:
> > > Hi Everyone,
> > >
> > > I've been doing some work on a C reactor API for proton that is
> > > intended to fit both alongside and underneath what gordon has been
> > > doing in pure python. I have several goals with this work.
> > >
> > >  - Simplify/enable a reactive style of programming in C that is
> > > similar to what gordon has built out in pure python.
> > >
> > >  - Provide a C API that translates well into the python/ruby/etc
> > > bindings, so that sophisticated handlers can be written once in C
> > > rather than being duplicated in each scripting language.
> > >
> > >
> > >  - Preserve the extensibility/flexibility that comes with being able
> > > to define custom handlers in the bindings, so python/ruby/etc handlers
> > > can intermix well with C handlers.
> > >
> > >
> > >  - Provide a C API that translates well into javascript via
> > > emscripten. In some ways this is similar to the above goals with the
> > > other language bindings, however I mention it separately because there
> > > are additional memory management constraints for javascript since it
> > > has no finalizers.
> > >
> > >
> > >
> > > I believe I've made significant progress towards most of these goals,
> > > although there is still plenty of work left to do. I'd like to share a
> > > few examples both to illustrate where I am with this and to solicit
> > > feedback and/or help.
> >
> > The goals are spot on, C API looks good at first glance, I'll try to
> > give it more careful scrutiny. The C handler functions need a void
> > *context so we can parameterize the callbacks. e.g.
> >
> > void task_dispatch(pn_handler_t *handler, pn_event_t *event, void
> > *context) {
> >     ...
> >     pn_reactor_schedule(reactor, (int)context, handler);
> > }
> > ...
> >       pn_handler_t *handler = pn_handler(task_dispatch, (void*)1000);
> >
> > I'm not sure if you intended a 1-1 relationship between handlers and
> > callback functions. If you do then you could put the context on the
> > handler rather than as a parameter to the callback - but that would
> > force the 1-1 relationship in the implementation from the API.
> >
> 
> I've had trouble in general using just simple void pointers for contexts in
> more complex scenarios. The memory management semantics quickly get pretty
> cumbersome. My intention was that any state the callback needs should be
> accessible from either the event itself or from the handler, and there are
> really two ways to add your own state. One being adding "attachments" into
> the object model, and two being extending the handler itself with
> additional state.
> 
> The attachments work like this. Most of the core python objects now support
> doing something like this:
> 
>   #define MY_STATE <any-unique-id>
> 
>   pn_record_t *attachments = pn_connection_attachments(conn);
>   pn_record_def(attachments, MY_STATE, PN_VOID);
>   ...
>   pn_record_set(attachments, MY_STATE, foo);
>   ...
>   void *foo = pn_record_get(attachments, MY_STATE);
> 
> Two key things to note here is that first, when you define the attachment,
> you are explicitly telling proton how to treat the pointer with respect to
> memory management. You can define PN_OBJECT, or PN_WEAKREF attachments
> also, and even define your own kind of pointer with your own finalization
> semantics. This is how the python bindings work, the python binding defines
> a PN_PYREF attachment for tracking associations between proton objects in C
> and python objects.
> 
> Second, there can be multiple independent attachments that won't conflict
> with each other so long as they use different keys. (I've just been using
> globally defined memory addresses to give myself guaranteed unique numeric
> ids with no need to have a shared bootstrap process.)
> 
> In the case of handlers, I've actually supplied another way to add extend
> the handler with your own state. When you create a handler you can request
> an additional chunk of memory associated with the handler that you can use
> in any way you like, and when you do this you supply a finalizer along with
> it. Your example above could work like this:
> 
>   pn_handler_t *handler = pn_handler_new(task_dispatch, sizeof(int), NULL
> /*if I were using pointers, I might want a finalizer here*/);
> 
> You can now access that from the handler via pn_handler_mem:
> 
>   int *my_state = pn_handler_mem(handler);
> 
> You can check out the pn_flowcontroller handler if you want to see a full
> working example of this.

Nice! You're way ahead of me :) Agreed on the mem mgmt problems with
void*, this seems like a nice solution. Attachments do imply an extra
malloc, but not your second option which I like more. IMO you don't need
attchments with the second option - you can use the second option to
store a pointer to anything (or a struct full of pointers to manythings)
with a proper finalizer and no void* at all. Very neat.



Reply via email to