[PATCH 3/7] winepulse: Move pulse main loop to unix lib.

Andrew Eikum aeikum at codeweavers.com
Wed May 12 11:36:26 CDT 2021


Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>

On Tue, May 11, 2021 at 06:30:17PM +0200, Jacek Caban wrote:
> Signed-off-by: Jacek Caban <jacek at codeweavers.com>
> ---
>  dlls/winepulse.drv/mmdevdrv.c | 170 ++++++----------------------------
>  dlls/winepulse.drv/pulse.c    |  81 ++++++++++++++++
>  dlls/winepulse.drv/unixlib.h  |   2 +
>  3 files changed, 109 insertions(+), 144 deletions(-)
> 
> 

> diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
> index 59806f28465..0bb3491a577 100644
> --- a/dlls/winepulse.drv/mmdevdrv.c
> +++ b/dlls/winepulse.drv/mmdevdrv.c
> @@ -23,7 +23,6 @@
>  #define _GNU_SOURCE
>  
>  #include "config.h"
> -#include <poll.h>
>  
>  #include <stdarg.h>
>  #include <unistd.h>
> @@ -74,9 +73,6 @@ enum DriverPriority {
>  
>  static struct pulse_config pulse_config;
>  
> -static pa_context *pulse_ctx;
> -static pa_mainloop *pulse_ml;
> -
>  static HANDLE pulse_thread;
>  static struct list g_sessions = LIST_INIT(g_sessions);
>  
> @@ -95,14 +91,7 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
>          if (__wine_init_unix_lib(dll, reason, NULL, &pulse))
>              return FALSE;
>      } else if (reason == DLL_PROCESS_DETACH) {
> -        if (pulse_thread)
> -           SetThreadPriority(pulse_thread, 0);
> -        if (pulse_ctx) {
> -            pa_context_disconnect(pulse_ctx);
> -            pa_context_unref(pulse_ctx);
> -        }
> -        if (pulse_ml)
> -            pa_mainloop_quit(pulse_ml, 0);
> +        __wine_init_unix_lib(dll, reason, NULL, NULL);
>          if (pulse_thread) {
>              WaitForSingleObject(pulse_thread, INFINITE);
>              CloseHandle(pulse_thread);
> @@ -244,65 +233,9 @@ static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
>      return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
>  }
>  
> -/* Following pulseaudio design here, mainloop has the lock taken whenever
> - * it is handling something for pulse, and the lock is required whenever
> - * doing any pa_* call that can affect the state in any way
> - *
> - * pa_cond_wait is used when waiting on results, because the mainloop needs
> - * the same lock taken to affect the state
> - *
> - * This is basically the same as the pa_threaded_mainloop implementation,
> - * but that cannot be used because it uses pthread_create directly
> - *
> - * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
> - * pa_threaded_mainloop_signal -> pthread_cond_broadcast
> - * pa_threaded_mainloop_wait -> pthread_cond_wait
> - */
> -
> -static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
> -    int r;
> -    pulse->unlock();
> -    r = poll(ufds, nfds, timeout);
> -    pulse->lock();
> -    return r;
> -}
> -
>  static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
> -    int ret;
> -    pulse_ml = pa_mainloop_new();
> -    pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
> -    pulse->lock();
> -    pulse->broadcast();
> -    pa_mainloop_run(pulse_ml, &ret);
> -    pulse->unlock();
> -    pa_mainloop_free(pulse_ml);
> -    return ret;
> -}
> -
> -static void pulse_contextcallback(pa_context *c, void *userdata)
> -{
> -    switch (pa_context_get_state(c)) {
> -        default:
> -            FIXME("Unhandled state: %i\n", pa_context_get_state(c));
> -            return;
> -
> -        case PA_CONTEXT_CONNECTING:
> -        case PA_CONTEXT_UNCONNECTED:
> -        case PA_CONTEXT_AUTHORIZING:
> -        case PA_CONTEXT_SETTING_NAME:
> -        case PA_CONTEXT_TERMINATED:
> -            TRACE("State change to %i\n", pa_context_get_state(c));
> -            return;
> -
> -        case PA_CONTEXT_READY:
> -            TRACE("Ready\n");
> -            break;
> -
> -        case PA_CONTEXT_FAILED:
> -            WARN("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
> -            break;
> -    }
> -    pulse->broadcast();
> +    pulse->main_loop();
> +    return 0;
>  }
>  
>  static void pulse_stream_state(pa_stream *s, void *user)
> @@ -352,73 +285,6 @@ static char *get_application_name(void)
>      return str;
>  }
>  
> -static HRESULT pulse_connect(void)
> -{
> -    int len;
> -    WCHAR path[MAX_PATH], *name;
> -    char *str;
> -
> -    if (!pulse_thread)
> -    {
> -        if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
> -        {
> -            ERR("Failed to create mainloop thread.\n");
> -            return E_FAIL;
> -        }
> -        SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
> -        pulse->cond_wait();
> -    }
> -
> -    if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx)))
> -        return S_OK;
> -    if (pulse_ctx)
> -        pa_context_unref(pulse_ctx);
> -
> -    GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
> -    name = strrchrW(path, '\\');
> -    if (!name)
> -        name = path;
> -    else
> -        name++;
> -    len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
> -    str = pa_xmalloc(len);
> -    WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
> -    TRACE("Name: %s\n", str);
> -    pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
> -    pa_xfree(str);
> -    if (!pulse_ctx) {
> -        ERR("Failed to create context\n");
> -        return E_FAIL;
> -    }
> -
> -    pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
> -
> -    TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
> -    if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
> -        goto fail;
> -
> -    /* Wait for connection */
> -    while (pulse->cond_wait()) {
> -        pa_context_state_t state = pa_context_get_state(pulse_ctx);
> -
> -        if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
> -            goto fail;
> -
> -        if (state == PA_CONTEXT_READY)
> -            break;
> -    }
> -
> -    TRACE("Connected to server %s with protocol version: %i.\n",
> -        pa_context_get_server(pulse_ctx),
> -        pa_context_get_server_protocol_version(pulse_ctx));
> -    return S_OK;
> -
> -fail:
> -    pa_context_unref(pulse_ctx);
> -    pulse_ctx = NULL;
> -    return E_FAIL;
> -}
> -
>  static HRESULT pulse_stream_valid(ACImpl *This) {
>      if (!This->stream)
>          return AUDCLNT_E_NOT_INITIALIZED;
> @@ -831,7 +697,7 @@ static DWORD WINAPI pulse_timer_cb(void *user)
>      return 0;
>  }
>  
> -static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
> +static HRESULT pulse_stream_connect(ACImpl *This, pa_context *pulse_ctx, UINT32 period_bytes) {
>      int ret;
>      char buffer[64];
>      static LONG number;
> @@ -1318,6 +1184,8 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
>          const GUID *sessionguid)
>  {
>      ACImpl *This = impl_from_IAudioClient3(iface);
> +    pa_context *pulse_ctx;
> +    char *name;
>      HRESULT hr = S_OK;
>      UINT32 bufsize_bytes;
>  
> @@ -1348,15 +1216,29 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
>  
>      pulse->lock();
>  
> -    hr = pulse_connect();
> -    if (FAILED(hr)) {
> +    if (This->stream) {
>          pulse->unlock();
> -        return hr;
> +        return AUDCLNT_E_ALREADY_INITIALIZED;
>      }
>  
> -    if (This->stream) {
> +    if (!pulse_thread)
> +    {
> +        if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
> +        {
> +            ERR("Failed to create mainloop thread.\n");
> +            pulse->unlock();
> +            return E_FAIL;
> +        }
> +        SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
> +        pulse->cond_wait();
> +    }
> +
> +    name = get_application_name();
> +    hr = pulse->connect(name, &pulse_ctx);
> +    free(name);
> +    if (FAILED(hr)) {
>          pulse->unlock();
> -        return AUDCLNT_E_ALREADY_INITIALIZED;
> +        return hr;
>      }
>  
>      hr = pulse_spec_from_waveformat(This, fmt);
> @@ -1378,7 +1260,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
>  
>      This->share = mode;
>      This->flags = flags;
> -    hr = pulse_stream_connect(This, This->period_bytes);
> +    hr = pulse_stream_connect(This, pulse_ctx, This->period_bytes);
>      if (SUCCEEDED(hr)) {
>          UINT32 unalign;
>          const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream);
> diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c
> index 4b4d2497314..5369d5d08a5 100644
> --- a/dlls/winepulse.drv/pulse.c
> +++ b/dlls/winepulse.drv/pulse.c
> @@ -76,6 +76,20 @@ static void WINAPI pulse_broadcast(void)
>      pthread_cond_broadcast(&pulse_cond);
>  }
>  
> +/* Following pulseaudio design here, mainloop has the lock taken whenever
> + * it is handling something for pulse, and the lock is required whenever
> + * doing any pa_* call that can affect the state in any way
> + *
> + * pa_cond_wait is used when waiting on results, because the mainloop needs
> + * the same lock taken to affect the state
> + *
> + * This is basically the same as the pa_threaded_mainloop implementation,
> + * but that cannot be used because it uses pthread_create directly
> + *
> + * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
> + * pa_threaded_mainloop_signal -> pthread_cond_broadcast
> + * pa_threaded_mainloop_wait -> pthread_cond_wait
> + */
>  static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata)
>  {
>      int r;
> @@ -85,6 +99,18 @@ static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout,
>      return r;
>  }
>  
> +static void WINAPI pulse_main_loop(void)
> +{
> +    int ret;
> +    pulse_ml = pa_mainloop_new();
> +    pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
> +    pulse_lock();
> +    pulse_broadcast();
> +    pa_mainloop_run(pulse_ml, &ret);
> +    pulse_unlock();
> +    pa_mainloop_free(pulse_ml);
> +}
> +
>  static void pulse_contextcallback(pa_context *c, void *userdata)
>  {
>      switch (pa_context_get_state(c)) {
> @@ -118,6 +144,50 @@ static void pulse_stream_state(pa_stream *s, void *user)
>      pulse_broadcast();
>  }
>  
> +static HRESULT WINAPI pulse_connect(const char *name, pa_context **ctx)
> +{
> +    if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx))) {
> +        *ctx = pulse_ctx;
> +        return S_OK;
> +    }
> +    if (pulse_ctx)
> +        pa_context_unref(pulse_ctx);
> +
> +    pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), name);
> +    if (!pulse_ctx) {
> +        ERR("Failed to create context\n");
> +        return E_FAIL;
> +    }
> +
> +    pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
> +
> +    TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
> +    if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
> +        goto fail;
> +
> +    /* Wait for connection */
> +    while (pulse_cond_wait()) {
> +        pa_context_state_t state = pa_context_get_state(pulse_ctx);
> +
> +        if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
> +            goto fail;
> +
> +        if (state == PA_CONTEXT_READY)
> +            break;
> +    }
> +
> +    TRACE("Connected to server %s with protocol version: %i.\n",
> +        pa_context_get_server(pulse_ctx),
> +        pa_context_get_server_protocol_version(pulse_ctx));
> +    *ctx = pulse_ctx;
> +    return S_OK;
> +
> +fail:
> +    pa_context_unref(pulse_ctx);
> +    pulse_ctx = NULL;
> +    return E_FAIL;
> +}
> +
>  static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map)
>  {
>      int i;
> @@ -412,6 +482,8 @@ static const struct unix_funcs unix_funcs =
>      pulse_unlock,
>      pulse_cond_wait,
>      pulse_broadcast,
> +    pulse_main_loop,
> +    pulse_connect,
>      pulse_test_connect,
>  };
>  
> @@ -430,6 +502,15 @@ NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *pt
>  
>          *(const struct unix_funcs **)ptr_out = &unix_funcs;
>          break;
> +    case DLL_PROCESS_DETACH:
> +        if (pulse_ctx)
> +        {
> +            pa_context_disconnect(pulse_ctx);
> +            pa_context_unref(pulse_ctx);
> +        }
> +        if (pulse_ml)
> +            pa_mainloop_quit(pulse_ml, 0);
> +
>      }
>  
>      return STATUS_SUCCESS;
> diff --git a/dlls/winepulse.drv/unixlib.h b/dlls/winepulse.drv/unixlib.h
> index 865a6f31ec6..f2bb7c78c82 100644
> --- a/dlls/winepulse.drv/unixlib.h
> +++ b/dlls/winepulse.drv/unixlib.h
> @@ -33,5 +33,7 @@ struct unix_funcs
>      void (WINAPI *unlock)(void);
>      int (WINAPI *cond_wait)(void);
>      void (WINAPI *broadcast)(void);
> +    void (WINAPI *main_loop)(void);
> +    HRESULT (WINAPI *connect)(const char *name, pa_context **ret);
>      HRESULT (WINAPI *test_connect)(const char *name, struct pulse_config *config);
>  };
> 




More information about the wine-devel mailing list