views:

162

answers:

7

I'm writing an open source C library. This library is quite complex, and some operations can take a long time. I therefore created a background thread which manages the long-running tasks.

My problem is that I have not yet found an elegant way to return errors from the background thread. Suppose the background thread reorganizes a file or does periodic maintenance, and it fails – what to do?

I currently see two options:

1) if the user is interested in seeing these errors, he can register a callback function.

I don't like this option – the user doesn't even know that there's a background thread, so he will most likely forget about setting the callback function. From usability point of view, this option is bad.

2) the background thread stores the error in a global variable and the next API function returns this error.

That's what I'm currently doing, but I'm also not 100% happy with it, because it means that users have to expect EVERY possible error code being returned from every API function. I.e. if the background thread sets an IO Error, and the user just wants to know the library version, he will get an IO error although the get_version() API call doesn't access the disk at all. Again, bad usability…

Any other suggestions/ideas?

+3  A: 

Perhaps for the "long running operations" (the ones you'd like to use a thread for) give users two options:

  • a blocking DoAction(...) that returns status
  • a non-blocking DoActionAsync(..., <callback>) that gives the status to a user provided callback function

This gives the user the choice in how they want to handle the long operation (instead of you deciding for them), and it is clear how the status will be returned.

Note: I suppose that if they call DoActionAsync, and the user doesn't specify a callback (e.g. they pass null) then the call wouldn't block, but the user wouldn't have/need to handle the status.

Daniel LeCheminant
I have 20 or 30 API functions, and if i would need two versions for each of them, it would be a bit much...
It might be more work on your part, but if you used common naming conventions, I expect users would understand it. It also gives the user flexibility to decide how they want the code to run...
Daniel LeCheminant
@cruppstahl: Also, you would only need to do this on the long operations (the ones that you would thread). Other operations could just block.
Daniel LeCheminant
A: 

API users can check against SUCCESS or !SUCCESS and deal with specific error codes only when they are expecting them, like errno.

ssg
A: 

If you're providing a library and try to hide expensive work via a thread I'd suggest to not do it that way. If something is expensive it should be visible to the caller and if it bugs him, he should take care of backgrounding/threading himself. That way he also has full control over the error. It also takes the control over his process away from the developer who uses your library.

If you still want to use threads I'd suggest to really follow the callback-route but make it clearly visible in the API and documentation that there will be a background thread running on this task and therefore the callback is necessary.

Best way would be if you offered both ways, synchronous and asynchronous, to the users of the library so they can choose what fits best for them in their specific situation.

Kosi2801
+1  A: 

I am interested in knowing how the completion status informed to the caller of API.

Since the background thread carries out all the execution. Either the foreground thread chooses to wait till the completion, like synchronous. Or the foreground thread can do other tasks, registering for a callback.

Now, since the first method is synchronous, like your usage of a global variable. You can use a message queue with 1 member, instead of your global variable. Now, - Caller can either poll the message queue for the status - Caller can block wait on the message queue for status

What I can think of,

But if I am the caller, I would like to know the progress status, if the time taken is very ... very long. So better to give some kind of percentage completion or something to enable the end user to develop much better application with progress bar and all.

Alphaneo
A: 

You should keep a thread-safe list (or queue) of error events and warnings. The worker thread can post events to the list, then the main thread can read events from the list, one at a time, or in a batch to prevent race conditions. Ideally, the main thread should fetch a copy of the event queue and flush it so there is no change of duplicating events in the case of multiple main or worker threads. Events on the list would have a type and details.

sean riley
A: 

Thanks for all the good answers. You provided me with a lot of material to think about.

Some of you suggested callbacks. Initially, i thought a callback is a good idea. But it just moves the problem to the user. If a user gets an asynchronous error notification, how will he deal with it? he will have to interrupt and/or notify his synchronous program flow, and that's usually tricky and often breaks a design.

The solution i'm doing now: if the background thread generates an error, the next API call will return an error BACKGROUND_ERROR_PENDING. With a separate API function (get_background_error()) the user can look at this error code, if he's interested in it.

Also, i added documentation so users don't be too surprised if this error is returned.

A: 

You might take a look at java's Future API for an alternate mechanism for dealing with asynchronous calls and errors. You could easily substitute the checked exceptions with some isError() or getError() methods if you preferred.

Bill Michell