[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