Colin Guthrie wrote:
As someone who has written/adapted a couple of C++ related PA apps, I
can say that a good, generic, libpulse++ would be quite desirable. I'd
even be tempted to help out here if possible to help unify some of the
other stuff I've done.
>
One thing I would suggest doing (if you really are looking at a generic
lib) is to have a synchronous "test" method, to check if PA support is
available (e.g. try to connect to the server with a standard mainloop
that you run manually and then disconnect and reconnect with a proper
mainloop if PA support is available.

Well, this test should not be related to C++.


But also I have to stop the main loop when it is no longer needed. This
is not trivial, because the pa_context might only be referenced
internally by other PA objects. So an instance counter of the C++
context wrappers is not sufficient, because they might have gone out of
scope while the mainloop is still needed for processing.

However, if I could track the total number of existing pa_context
objects, I could safely stop the main loop after the last pa_context
died. Without any pa_context object it is most likely no longer needed.

At what point do you need to cache the context objects?

It is up to the application to keep the wrapper classes alive as long as it likes. Destroying might in fact be mainly like calling unref. OK, in case of a context, disconnect could be more intuitive on destruction.

Most (all?)
callbacks include the context in them and as such you don't really need
to keep the context around as a variable in it's own right.

This won't help for C++ since in general the callbacks have to be wrapped with some oberservable classes. First of all this allows multiple subscribers, secondly a reasonable observer implementation would disconnect from dieing observers automatically to avoid undefined behavior.


The only available observable is the stat callback. But tracking changes
to PA_CONTEXT_FAILED and PA_CONTEXT_TERMINATED is not sufficient. First
of all a context might not have been used at all, Secondly a terminated
connection might become connected later.

Contexts can only be used once, so if your context is TERMINATED or
FAILED, then it should be discarded and a new one used.

Ah! I wasn't aware of that. In this case the state transition of the last context to TERMINATED or FAILED should be sufficient.


Any ideas how to terminate the mainloop?

If you really do want to terminate the mainloop, then you should tear
down all contexts using it also I think.

I'd like to do it the other way around. Once all contexts are disconnected the main loop should terminate itself.


Not sure if anything I've said helps specifically but perhaps seeing
some code would help to illustrate the problem better.

Well, I just have started. And since I am not on an officially unsupported platform (eComStation) it might not become as generic as you like.

class PAContext
{private:
  static volatile unsigned              InstCount;
  static pa_threaded_mainloop* volatile Mainloop;
  pa_context*        Context;
  pa_context_state_t State;

 public:
  PAContext(const char* appname);
  ~PAContext();
  operator pa_context*()                        { return Context; }

int Connect(const char* server, pa_context_flags_t flags = PA_CONTEXT_NOFLAGS) { return pa_context_connect(Context, server, flags, NULL); } void Disconnect() { pa_context_disconnect(Context); }

 private: // non-copyable
  PAContext(const PAContext& r);
  void operator=(const PAContext& r);
 private:
  bool Init();
  void Uninit();
  static void StateCB(pa_context *c, void *userdata);
};


PAContext::PAContext(const char* appname)
: State(PA_CONTEXT_UNCONNECTED)
{ if (!Init())
    return;

Context = pa_context_new(pa_threaded_mainloop_get_api(Mainloop), appname);
  if (!Context)
  { Uninit();
    return;
  }

  pa_context_set_state_callback(Context, &PAContext::StateCB, this);
}

PAContext::~PAContext()
{ if (Context)
    pa_context_unref(Context);
// TODO: defer termination of mainloop until the context is really destroyed.
  Uninit();
}

bool PAContext::Init()
{ if (InterlockedXadd(&InstCount, 1) == 0)
  { // First instance => start main loop.
    Mainloop = pa_threaded_mainloop_new();
    if (!Mainloop)
      return false;
    pa_threaded_mainloop_start(Mainloop);
  } else
  { // Initialized by another instance
    // Dirty spin-lock to synchronize the other thread.
    // TODO: deadlock in case of error.
    while (Mainloop == NULL)
      DosSleep(1);
  }
}

void PAContext::Uninit()
{ pa_threaded_mainloop* mainloop = Mainloop;
  if (InterlockedDec(&InstCount))
    return;
  // Do not detach a mainloop that might be instantiated by another thread.
InterlockedCmpxch((volatile unsigned*)&Mainloop, (unsigned)mainloop, NULL);
  // destroy mainloop
  pa_threaded_mainloop_stop(mainloop);
  pa_threaded_mainloop_free(mainloop);
}

_______________________________________________
pulseaudio-discuss mailing list
pulseaudio-discuss@mail.0pointer.de
https://tango.0pointer.de/mailman/listinfo/pulseaudio-discuss

Reply via email to