From: Gerhard Sittig Date: Sun, 26 Dec 2021 07:23:27 +0000 (+0100) Subject: ir_irmp: wrapper lib, add locking and Python threading support X-Git-Url: https://sigrok.org/gitweb/?p=libsigrokdecode.git;a=commitdiff_plain;h=7cfa9f7aa2426c617990f3ecfcabed8765aff46c ir_irmp: wrapper lib, add locking and Python threading support The IRMP core library is not thread safe (known limitation, heritage of the AVR firmware origin). Add a mutex so that calling applications can lock IR decoder core instances. Allow Python threading while waiting for the locks, we can safely assume that this IRMP wrapper is used in the sigrok context which does require Python for decoders. Add my copyright for the non-trivial changes. This implementation uses glib for locking to improve portability, which already is a dependency of the libsigrokdecode component. This version uses belt and suspenders by implementing a constructor as well as adding auto init calls to each of the public API code paths. The client ID is not an essential requirement, but useful during application maintenance. --- diff --git a/Makefile.am b/Makefile.am index 9e15c30..32c6dd6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -64,6 +64,8 @@ libirmp_la_SOURCES = \ irmp/irmpsystem.h \ irmp/irmpprotocols.h noinst_HEADERS += irmp/irmp.c +libirmp_la_CFLAGS = $(LIBIRMP_CFLAGS) +libirmp_la_LIBADD = $(LIBIRMP_LIBS) libirmp_la_LDFLAGS = -no-undefined -version-info 0:0:0 endif diff --git a/configure.ac b/configure.ac index c1314bb..7a2795f 100644 --- a/configure.ac +++ b/configure.ac @@ -159,6 +159,7 @@ AC_SUBST([SRD_PKGLIBS]) # Retrieve the compile and link flags for all modules combined. # Also, bail out at this point if any module dependency is not met. PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.34 $SRD_PKGLIBS]) +PKG_CHECK_MODULES([LIBIRMP], [glib-2.0 >= 2.34 $SRD_PKGLIBS]) PKG_CHECK_MODULES([TESTS], [$SRD_PKGLIBS_TESTS glib-2.0 $SRD_PKGLIBS]) srd_glib_version=`$PKG_CONFIG --modversion glib-2.0 2>&AS_MESSAGE_LOG_FD` diff --git a/irmp/irmp-main-sharedlib.c b/irmp/irmp-main-sharedlib.c index cbf239a..4d02460 100644 --- a/irmp/irmp-main-sharedlib.c +++ b/irmp/irmp-main-sharedlib.c @@ -3,6 +3,7 @@ * * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de * Copyright (c) 2009-2019 René Staffen - r.staffen(at)gmx.de + * Copyright (c) 2020-2021 Gerhard Sittig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +17,9 @@ */ #include "irmp-main-sharedlib.h" +#include +#include +#include #include #include @@ -98,6 +102,109 @@ # define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #endif +static int irmp_lib_initialized; +static size_t irmp_lib_client_id; +static GMutex irmp_lib_mutex; + +struct irmp_instance { + size_t client_id; + GMutex *mutex; +}; + +static void irmp_lib_autoinit(void) +{ + if (irmp_lib_initialized) + return; + + irmp_lib_client_id = 0; + g_mutex_init(&irmp_lib_mutex); + + irmp_lib_initialized = 1; +} + +static size_t irmp_next_client_id(void) +{ + size_t id; + + do { + id = ++irmp_lib_client_id; + } while (!id); + + return id; +} + +IRMP_DLLEXPORT struct irmp_instance *irmp_instance_alloc(void) +{ + struct irmp_instance *inst; + + irmp_lib_autoinit(); + + inst = g_malloc0(sizeof(*inst)); + if (!inst) + return NULL; + + inst->client_id = irmp_next_client_id(); + inst->mutex = &irmp_lib_mutex; + + return inst; +} + +IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state) +{ + + irmp_lib_autoinit(); + + if (!state) + return; + + g_free(state); +} + +IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state) +{ + + irmp_lib_autoinit(); + + return state ? state->client_id : 0; +} + +IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait) +{ + int rc; + PyGILState_STATE pyst; + + irmp_lib_autoinit(); + + if (!state || !state->mutex) + return -EINVAL; + + pyst = PyGILState_Ensure(); + Py_BEGIN_ALLOW_THREADS + if (wait) { + g_mutex_lock(state->mutex); + rc = 0; + } else { + rc = g_mutex_trylock(state->mutex); + } + Py_END_ALLOW_THREADS + PyGILState_Release(pyst); + if (rc != 0) + return rc; + + return 0; +} + +IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state) +{ + + irmp_lib_autoinit(); + + if (!state || !state->mutex) + return; + + g_mutex_unlock(state->mutex); +} + static uint32_t s_end_sample; IRMP_DLLEXPORT uint32_t irmp_get_sample_rate(void) @@ -195,3 +302,8 @@ IRMP_DLLEXPORT const char *irmp_get_protocol_name(uint32_t protocol) return "unknown"; return name; } + +static __attribute__((constructor)) void init(void) +{ + irmp_lib_autoinit(); +} diff --git a/irmp/irmp-main-sharedlib.h b/irmp/irmp-main-sharedlib.h index 94065f3..67e7997 100644 --- a/irmp/irmp-main-sharedlib.h +++ b/irmp/irmp-main-sharedlib.h @@ -3,6 +3,7 @@ * * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de * Copyright (c) 2009-2019 René Staffen - r.staffen(at)gmx.de + * Copyright (c) 2020-2021 Gerhard Sittig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,6 +33,49 @@ extern "C" { /* Part of the library API is optional. */ #define WITH_IRMP_DETECT_BUFFER 0 +/** + * @brief State container for a decoder core instance. Opaque to clients. + */ +struct irmp_instance; + +/** + * @brief Allocate a decoder instance. + * + * @returns Reference to the allocated instance state. + */ +IRMP_DLLEXPORT struct irmp_instance *irmp_instance_alloc(void); + +/** + * @brief Release a decoder instance. + * + * @param[in] state Reference to the instance's state. + */ +IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state); + +/** + * @brief Get the client ID of an IRMP decoder core instance. + */ +IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state); + +/** + * @brief Acquire a decoder instance's lock. + * + * @param[in] state Reference to the instance's state. + * @param[in] wait Whether to block until the lock is acquired. + * + * @returns 0 upon success, non-zero upon failure + */ +IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait); + +/** + * @brief Release a decoder instance's lock. + * + * @param[in] state Reference to the instance's state. + * + * @returns 0 upon success, non-zero upon failure + */ +IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state); + /** * @brief IR decoder result data at the library's public API. */