You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@lucy.apache.org by Bruno Albuquerque <bg...@bug-br.org.br> on 2015/03/30 14:51:16 UTC

[lucy-dev] Trapping errors in C.

Hi.

I was checking the exceptions handling code for the C bindings and it looks
like it simply exists when an error happens.

For my purposes, it would be better if I could somehow trap errors and act
upon them instead of simply exiting. Is there a way to do it at all? I
noticed there is an Err_trap function in Clownfish but I am not sure if
that would do what I want or even how to use it.

Any pointers would be much appreciated.

Thanks in advance.

Re: [lucy-dev] Trapping errors in C.

Posted by Bruno Albuquerque <bg...@bug-br.org.br>.
Thanks Marvin.

Yes, I am playing with creating Go bindings for an internal project and I
did see your changes (congratulations, BTW, being able to use Lucy in an
idiomatic Go way will be amazing).

The reason i decided to go with 0.4.2 is mostly due to the fact that it is
the currently released version. Most of the bindings are trivial anyway
although your changes obviously would make things easier for anyone that
needs anything fancy.

Em seg, 30 de mar de 2015 às 16:41, Marvin Humphrey <ma...@rectangular.com>
escreveu:

> On Mon, Mar 30, 2015 at 11:06 AM, Bruno Albuquerque <bg...@bug-br.org.br>
> wrote:
> > Actually i think I was not clear about what I need. How would I apply
> > Err_trap for a higher level function? For example, a call to
> Indexer_new()
> > might fail under some circunstances and the program will exit due to
> that,
> > I would like for it not to exit immediately (for example, to be able to
> do
> > something else if the call fails). indexer_new() does nto accept a
> context
> > and it returns something when it works.
> >
> > Again, i might just be missing something obvious.
>
> FWIW, Indexer_new() is an example of a sub-optimal API that we should
> change.
> The existing approach is a legacy of Lucy's Lucene heritage.  In Java the
> idiom would be to wrap `new Indexer()` in a `try` block, which works fine
> and
> does not leak memory.  What we ought to have instead in Lucy is
> Indexer_open(), which returns NULL and sets an error variable on expected
> failure cases such as an unsuccessful attempt to acquire the write lock.
>
> By the way, I see you writing to the golang-nuts mailing list and it seems
> that you are wrapping the C bindings (presumably from the current 0.4.2
> public
> release).  You may be interested to know that current Git master has some
> proof-of-concept Go bindings, which include `lucy.OpenIndexer()` and an
> exception mechanism implemented using Go's `panic` -- including a
> `clownfish.TrapErr()` function which takes a Go closure rather than a C
> function pointer and context.
>
> But to get back to your question, what you would do is adapt Nick's sample
> code to wrap Indexer_new().
>
>     struct IndexerContext {
>         Schema       *schema;
>         Obj          *index;
>         IndexManager *manager;
>         int32_t       flags;
>         Indexer      *retval;
>     };
>
>     static void
>     try_indexer_new(void *vcontext) {
>         struct IndexerContext *context = (struct IndexerContext*)vcontext;
>         context->retval = Indexer_new(context->schema, context->index,
>                                       context->manager, context->flags);
>     }
>
>     Indexer*
>     open_indexer(Schema *schema, Obj *index, IndexManager *manager,
>                  int32_t flags) {
>         struct IndexerContext context;
>         context.schema  = schema;
>         context.index   = index;
>         context.manager = manager;
>         context.flags   = flags;
>         context.retval  = NULL;
>
>         Err *error = Err_trap(try_indexer_new, &context);
>         if (error != NULL) {
>             /* Handle error. */
>         }
>         return context.retval;
>     }
>
> The error trapping code in the lucy.OpenIndexer() Go wrapper is similar:
>
>     https://git1-us-west.apache.org/repos/asf?p=lucy.git;a=
> blob;f=go/lucy/index.go;h=f0e09797210253a0e4dde83280f9c49744667aa1;hb=
> 27842415a072d8b8a9f026df1d1f0b2a96b086e3#l76
>
> Marvin Humphrey
>

Re: [lucy-dev] Trapping errors in C.

Posted by Marvin Humphrey <ma...@rectangular.com>.
On Mon, Mar 30, 2015 at 11:06 AM, Bruno Albuquerque <bg...@bug-br.org.br> wrote:
> Actually i think I was not clear about what I need. How would I apply
> Err_trap for a higher level function? For example, a call to Indexer_new()
> might fail under some circunstances and the program will exit due to that,
> I would like for it not to exit immediately (for example, to be able to do
> something else if the call fails). indexer_new() does nto accept a context
> and it returns something when it works.
>
> Again, i might just be missing something obvious.

FWIW, Indexer_new() is an example of a sub-optimal API that we should change.
The existing approach is a legacy of Lucy's Lucene heritage.  In Java the
idiom would be to wrap `new Indexer()` in a `try` block, which works fine and
does not leak memory.  What we ought to have instead in Lucy is
Indexer_open(), which returns NULL and sets an error variable on expected
failure cases such as an unsuccessful attempt to acquire the write lock.

By the way, I see you writing to the golang-nuts mailing list and it seems
that you are wrapping the C bindings (presumably from the current 0.4.2 public
release).  You may be interested to know that current Git master has some
proof-of-concept Go bindings, which include `lucy.OpenIndexer()` and an
exception mechanism implemented using Go's `panic` -- including a
`clownfish.TrapErr()` function which takes a Go closure rather than a C
function pointer and context.

But to get back to your question, what you would do is adapt Nick's sample
code to wrap Indexer_new().

    struct IndexerContext {
        Schema       *schema;
        Obj          *index;
        IndexManager *manager;
        int32_t       flags;
        Indexer      *retval;
    };

    static void
    try_indexer_new(void *vcontext) {
        struct IndexerContext *context = (struct IndexerContext*)vcontext;
        context->retval = Indexer_new(context->schema, context->index,
                                      context->manager, context->flags);
    }

    Indexer*
    open_indexer(Schema *schema, Obj *index, IndexManager *manager,
                 int32_t flags) {
        struct IndexerContext context;
        context.schema  = schema;
        context.index   = index;
        context.manager = manager;
        context.flags   = flags;
        context.retval  = NULL;

        Err *error = Err_trap(try_indexer_new, &context);
        if (error != NULL) {
            /* Handle error. */
        }
        return context.retval;
    }

The error trapping code in the lucy.OpenIndexer() Go wrapper is similar:

    https://git1-us-west.apache.org/repos/asf?p=lucy.git;a=blob;f=go/lucy/index.go;h=f0e09797210253a0e4dde83280f9c49744667aa1;hb=27842415a072d8b8a9f026df1d1f0b2a96b086e3#l76

Marvin Humphrey

Re: [lucy-dev] Trapping errors in C.

Posted by Bruno Albuquerque <bg...@bug-br.org.br>.
Thanks Nick, that is exactly what I need. I am still wrapping my mind
around all the Clownfish objects in C stuff. :)

Em seg, 30 de mar de 2015 às 16:41, Nick Wellnhofer <we...@aevum.de>
escreveu:

> On 30/03/2015 20:06, Bruno Albuquerque wrote:
> > Actually i think I was not clear about what I need. How would I apply
> > Err_trap for a higher level function? For example, a call to
> Indexer_new()
> > might fail under some circunstances and the program will exit due to
> that,
> > I would like for it not to exit immediately (for example, to be able to
> do
> > something else if the call fails). indexer_new() does nto accept a
> context
> > and it returns something when it works.
>
> Try something like:
>
>      typedef struct {
>          Indexer *indexer;
>      } my_context_t;
>
>      static void
>      try_create_indexer(void *arg) {
>          my_context_t *ctx = (my_context_t*)arg;
>          ctx->indexer = Indexer_new();
>      }
>
>      void
>      other_code() {
>          my_context_t ctx;
>          Err *error = Err_trap(try_create_indexer, &ctx);
>
>          if (error != NULL) {
>              /* Handle error. */
>          }
>          else {
>              /* Indexer created successfully. */
>              Indexer *indexer = ctx.indexer;
>          }
>      }
>
> C doesn't support closures, so such a verbose solution is necessary.
>
> Nick
>
>

Re: [lucy-dev] Trapping errors in C.

Posted by Nick Wellnhofer <we...@aevum.de>.
On 30/03/2015 20:06, Bruno Albuquerque wrote:
> Actually i think I was not clear about what I need. How would I apply
> Err_trap for a higher level function? For example, a call to Indexer_new()
> might fail under some circunstances and the program will exit due to that,
> I would like for it not to exit immediately (for example, to be able to do
> something else if the call fails). indexer_new() does nto accept a context
> and it returns something when it works.

Try something like:

     typedef struct {
         Indexer *indexer;
     } my_context_t;

     static void
     try_create_indexer(void *arg) {
         my_context_t *ctx = (my_context_t*)arg;
         ctx->indexer = Indexer_new();
     }

     void
     other_code() {
         my_context_t ctx;
         Err *error = Err_trap(try_create_indexer, &ctx);

         if (error != NULL) {
             /* Handle error. */
         }
         else {
             /* Indexer created successfully. */
             Indexer *indexer = ctx.indexer;
         }
     }

C doesn't support closures, so such a verbose solution is necessary.

Nick


Re: [lucy-dev] Trapping errors in C.

Posted by Bruno Albuquerque <bg...@bug-br.org.br>.
Actually i think I was not clear about what I need. How would I apply
Err_trap for a higher level function? For example, a call to Indexer_new()
might fail under some circunstances and the program will exit due to that,
I would like for it not to exit immediately (for example, to be able to do
something else if the call fails). indexer_new() does nto accept a context
and it returns something when it works.

Again, i might just be missing something obvious.


Em seg, 30 de mar de 2015 às 14:30, Nick Wellnhofer <we...@aevum.de>
escreveu:

> On 30/03/2015 14:51, Bruno Albuquerque wrote:
> > I was checking the exceptions handling code for the C bindings and it
> looks
> > like it simply exists when an error happens.
> >
> > For my purposes, it would be better if I could somehow trap errors and
> act
> > upon them instead of simply exiting. Is there a way to do it at all? I
> > noticed there is an Err_trap function in Clownfish but I am not sure if
> > that would do what I want or even how to use it.
>
> Yes, the Err_trap function is what you want to use. It takes a function
> pointer and a void pointer to a user context and executes the function. If
> a
> Clownfish error is thrown, it is returned. Example:
>
>      static void
>      try_something(void *context) {
>          /* Execute code that might throw. */
>      }
>
>      void
>      other_code() {
>          Err *error = Err_trap(try_something, context);
>          if (error != NULL) {
>              /* Handle error. */
>          }
>      }
>
> The problem with this approach is that it's based on setjmp/longjmp and can
> leak memory, so our goal is to throw errors only in fatal situations.
> What's
> your use case? If it's something common, we might consider an API change.
>
> Nick
>

Re: [lucy-dev] Trapping errors in C.

Posted by Nick Wellnhofer <we...@aevum.de>.
On 30/03/2015 14:51, Bruno Albuquerque wrote:
> I was checking the exceptions handling code for the C bindings and it looks
> like it simply exists when an error happens.
>
> For my purposes, it would be better if I could somehow trap errors and act
> upon them instead of simply exiting. Is there a way to do it at all? I
> noticed there is an Err_trap function in Clownfish but I am not sure if
> that would do what I want or even how to use it.

Yes, the Err_trap function is what you want to use. It takes a function 
pointer and a void pointer to a user context and executes the function. If a 
Clownfish error is thrown, it is returned. Example:

     static void
     try_something(void *context) {
         /* Execute code that might throw. */
     }

     void
     other_code() {
         Err *error = Err_trap(try_something, context);
         if (error != NULL) {
             /* Handle error. */
         }
     }

The problem with this approach is that it's based on setjmp/longjmp and can 
leak memory, so our goal is to throw errors only in fatal situations. What's 
your use case? If it's something common, we might consider an API change.

Nick