]> sigrok.org Git - libsigrokdecode.git/commitdiff
ir_irmp: wrapper lib, add locking and Python threading support
authorGerhard Sittig <redacted>
Sun, 26 Dec 2021 07:23:27 +0000 (08:23 +0100)
committerGerhard Sittig <redacted>
Sun, 26 Dec 2021 12:45:09 +0000 (13:45 +0100)
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.

Makefile.am
configure.ac
irmp/irmp-main-sharedlib.c
irmp/irmp-main-sharedlib.h

index 9e15c300395c861c95e246be25672f6771135ef8..32c6dd63f4cefa5703e2fdc43271f427bd7ee15b 100644 (file)
@@ -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
 
index c1314bb78bfceb4aff293db17933c1815b9863a8..7a2795f074b7a5215a67182d00e1c26c2c6c6685 100644 (file)
@@ -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`
index cbf239a3341eab68975eb4f419a1856fb6bcecdb..4d02460014baa4687429bc8cd7a79ad0261c8247 100644 (file)
@@ -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 <gerhard.sittig@gmx.net>
  *
  * 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 <errno.h>
+#include <glib.h>
+#include <Python.h>
 #include <stdlib.h>
 #include <string.h>
 
 #  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();
+}
index 94065f383c3ae41453e981b9836d9e1dd5b01f17..67e79973446d757abd3d7cc9c7d600521496b05d 100644 (file)
@@ -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 <gerhard.sittig@gmx.net>
  *
  * 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.
  */