]> sigrok.org Git - libsigrokdecode.git/commitdiff
ir_irmp: Python binding, support instance locking and context manager
authorGerhard Sittig <redacted>
Sun, 26 Dec 2021 07:31:02 +0000 (08:31 +0100)
committerGerhard Sittig <redacted>
Sun, 26 Dec 2021 12:45:09 +0000 (13:45 +0100)
Extend the ctypes wrapper for the IRMP decoder core. Add routines for
the instance state creation and lock management. Implement metamethods
for Python context managers which lock the instance to protect the C
library's internal state from changing unexpectedly. Add my copyright
for the non-trivial changes.

This commit eliminates the limitation to a single IRMP decoder core for
the sigrok process to use. Multiple Python callers can synchronize their
library use, and see a consistent library state across the scope of the
context manager. It's essential though that callers leave the context
to not block other callers for extended periods of time.

decoders/ir_irmp/irmp_library.py

index 5ec652224752b49e1209aac9e47706a855ce0e17..a1bc2583df82fb6b8b4a4437350732b91b1d0604 100644 (file)
@@ -2,6 +2,7 @@
 ## This file is part of the libsigrokdecode project.
 ##
 ## Copyright (C) 2019 Rene Staffen
+## 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
@@ -61,12 +62,24 @@ class IrmpLibrary:
         Lookup the C library's API routines. Declare their prototypes.
         '''
 
-        if not self._lib:
-            return False
-
         self._lib.irmp_get_sample_rate.restype = ctypes.c_uint32
         self._lib.irmp_get_sample_rate.argtypes = []
 
+        self._lib.irmp_instance_alloc.restype = ctypes.c_void_p
+        self._lib.irmp_instance_alloc.argtypes = []
+
+        self._lib.irmp_instance_free.restype = None
+        self._lib.irmp_instance_free.argtypes = [ ctypes.c_void_p, ]
+
+        self._lib.irmp_instance_id.restype = ctypes.c_size_t
+        self._lib.irmp_instance_id.argtypes = [ ctypes.c_void_p, ]
+
+        self._lib.irmp_instance_lock.restype = ctypes.c_int
+        self._lib.irmp_instance_lock.argtypes = [ ctypes.c_void_p, ctypes.c_int, ]
+
+        self._lib.irmp_instance_unlock.restype = None
+        self._lib.irmp_instance_unlock.argtypes = [ ctypes.c_void_p, ]
+
         self._lib.irmp_reset_state.restype = None
         self._lib.irmp_reset_state.argtypes = []
 
@@ -85,6 +98,7 @@ class IrmpLibrary:
 
         # Create a result buffer that's local to the library instance.
         self._data = self.ResultData()
+        self._inst = None
 
         return True
 
@@ -93,30 +107,47 @@ class IrmpLibrary:
         Create a library instance.
         '''
 
-        # Only create a working instance for the first invocation.
-        # Degrade all other instances, make them fail "late" during
-        # execution, so that users will see the errors.
-        self._lib = None
-        self._data = None
-        if IrmpLibrary.__usable_instance is None:
-            filename = self._library_filename()
-            self._lib = ctypes.cdll.LoadLibrary(filename)
-            self._library_setup_api()
-            IrmpLibrary.__usable_instance = self
+        filename = self._library_filename()
+        self._lib = ctypes.cdll.LoadLibrary(filename)
+        self._library_setup_api()
+
+    def __del__(self):
+        '''
+        Release a disposed library instance.
+        '''
+
+        if self._inst:
+            self._lib.irmp_instance_free(self._inst)
+        self._inst = None
+
+    def __enter__(self):
+        '''
+        Enter a context (lock management).
+        '''
+
+        if self._inst is None:
+            self._inst = self._lib.irmp_instance_alloc()
+        self._lib.irmp_instance_lock(self._inst, 1)
+        return self
+
+    def __exit__(self, extype, exvalue, trace):
+        '''
+        Leave a context (lock management).
+        '''
+
+        self._lib.irmp_instance_unlock(self._inst)
+        return False
+
+    def client_id(self):
+        return self._lib.irmp_instance_id(self._inst)
 
     def get_sample_rate(self):
-        if not self._lib:
-            return None
         return self._lib.irmp_get_sample_rate()
 
     def reset_state(self):
-        if not self._lib:
-            return None
         self._lib.irmp_reset_state()
 
     def add_one_sample(self, level):
-        if not self._lib:
-            raise Exception("IRMP library limited to a single instance.")
         if not self._lib.irmp_add_one_sample(int(level)):
             return False
         self._lib.irmp_get_result_data(ctypes.byref(self._data))