Re: Design question: asynchronous API in C++
Adam Skutt wrote:
But it requires a callback function to be passed and returns a request
object pointer. However these two things are not related directly which
is very error prone.
How are they not related directly?
To wait for the command to complete the application has to wait for the
callback. This implies providing a callback function and dealing with an
event semaphore. Very inconvenient.
To poll the completion state the application has to query the
If the callback calls a member function of some command tracking object
of the application, and if the command has to be canceled for some
reason it is up to the application to ensure to cleanup it's own
tracking object together with the pa_operation object. This is error
prone and can't be easily supported by a smart C++ object. In fact the
applications request object and the pa_operation object belong to the
same thing and therefore should be intrinsically tied to each other.
E.g. one cannot call a wait function on the request
object pointer, because this object does not receive the callback.
Huh? You can't call a wait function on a pa_operation because it's nonsensical:
> depending on the mainloop structure you'd never return from the wait.
AFAIK all requests should either complete or fail sooner or later. In
case of need they should fail by a time out.
But I don't see what "because this object does not receive the callback" has to do with anything.
> To be honest, I'm not even entirely sure what it means, but I fail to
see the relevance.
> wait() is no difference from cancel() from an encapsulation perspective.
> pa_operation doesn't support wait() not because it lacks the
information to do so,
In fact the operations state is not sufficient, because there is a need
for some kind of thread synchronization.
but because doing so would be _dangerous and nonsensical_.
From the applications point of view it would be helpful to have e
receive function that waits for completion and returns the result. And
it would be even more helpful if the function also returns (e.g. with an
exception) if another thread cancels the outstanding operation. It is a
lot of work to implement this semantics for each request.
I really think you need to go back and figure out exactly what you're trying to do,
> because what you've explained to us sounds dangerous, though perhaps
only out of inadequate explanation.
I am currently writing a plug-in that acts as pulseaudio client. This
plug-in is written in C++. And because I want the compiler to detect as
many bugs as possible (debugging plug-ins is a pain) I want to make the
API as safe as possible. I expect from a C++ API that it is impossible
or at least not that easy to run into undefined behavior. That means:
- All objects should manage the pulseaudio reference counters
automatically and exception safe (no big deal).
- All methods should belong to class types that really support this kind
of call. E.g. only a sink input stream could be written to, not any
stream, like the API suggests.
- The C++ objects should be thread safe and manage the synchronization
with the mainloop as far as it makes sense.
- The state of asynchronous calls should be tracked safely. If the C++
request object goes out of scope the callback should no longer be
called. If the callback arrives the results should be stored at a
predefined location, handled synchronously or discarded, its the
- And last but not least, using the C++ API should result in small and
The pulseaudio developers were also be happy to have some
libpulseaudio++, since there are more C++ pulseaudio applications
around. So I thought it would be a good idea to look for a sophisticated
solution. And because the asynchronous nature does not fit well into the
C++ language, I want to do some brainstorming.