From 30deef72c7b9444fdd38b2a832009b1cb7ed12e4 Mon Sep 17 00:00:00 2001 From: Jonathan Hollocombe Date: Wed, 15 Oct 2025 17:14:35 +0100 Subject: [PATCH 1/4] Adding process id check to cpyuda to detect forking issues. --- source/wrappers/python/pyuda/_client.py | 2 ++ source/wrappers/python/pyuda/cpyuda/client.pyx | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/source/wrappers/python/pyuda/_client.py b/source/wrappers/python/pyuda/_client.py index 718ebc628..399795471 100644 --- a/source/wrappers/python/pyuda/_client.py +++ b/source/wrappers/python/pyuda/_client.py @@ -55,6 +55,8 @@ class Client(with_metaclass(ClientMeta, object)): __metaclass__ = ClientMeta def __init__(self, debug_level=logging.ERROR): + cpyuda.set_pid() + self.version = __version__ assert self.version == cpyuda.get_build_version().decode(), "mismatching pyuda and c-library versions" diff --git a/source/wrappers/python/pyuda/cpyuda/client.pyx b/source/wrappers/python/pyuda/cpyuda/client.pyx index 122a706cf..127ee2381 100644 --- a/source/wrappers/python/pyuda/cpyuda/client.pyx +++ b/source/wrappers/python/pyuda/cpyuda/client.pyx @@ -1,5 +1,6 @@ #cython: language_level=3 +import os import numpy as np cimport uda cimport numpy as np @@ -9,6 +10,7 @@ from libc.stdlib cimport malloc, free from libc.string cimport strlen +_pid = None _properties = { "get_datadble": ("GET_DATA_DOUBLE", False), "get_dimdble": ("GET_DIM_DOUBLE", False), @@ -37,6 +39,11 @@ if PY_MAJOR_VERSION >= 3.0: else: Properties = type(b'Properties', (), {v[0]:k for k,v in _properties.items()}) + +def set_pid(): + _pid = os.getpid() + + def set_property(prop_name, value): if prop_name.lower() not in _properties: raise ValueError('invalid property ' + prop_name) @@ -88,6 +95,8 @@ def close_connection(): def get_data(signal, source): + if _pid != os.getpid(): + raise ClientException("Calling client from a process different to process in which library was initialised") handle = uda.idamGetAPI(signal.encode(), source.encode()) cdef const char* err_msg cdef int err_code @@ -104,6 +113,8 @@ def get_data(signal, source): def get_data_batch(signals, sources): + if _pid != os.getpid(): + raise ClientException("Calling client from a process different to process in which library was initialised") assert len(signals) == len(sources) cdef const char** signals_array = malloc(len(signals) * sizeof(char*)) cdef const char** sources_array = malloc(len(sources) * sizeof(char*)) @@ -140,6 +151,8 @@ def get_data_batch(signals, sources): cdef put_nothing(const char* instruction): + if _pid != os.getpid(): + raise ClientException("Calling client from a process different to process in which library was initialised") cdef int handle = uda.idamPutAPI(instruction, NULL) return Result(Handle(handle)) From 021851a96ea374af83ddd673c9540013d1f1bf0e Mon Sep 17 00:00:00 2001 From: stephen-dixon <68229525+stephen-dixon@users.noreply.github.com> Date: Wed, 15 Oct 2025 17:35:50 +0100 Subject: [PATCH 2/4] move fork check to main pudata method --- source/wrappers/python/pyuda/cpyuda/client.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/wrappers/python/pyuda/cpyuda/client.pyx b/source/wrappers/python/pyuda/cpyuda/client.pyx index 127ee2381..d922ae7e0 100644 --- a/source/wrappers/python/pyuda/cpyuda/client.pyx +++ b/source/wrappers/python/pyuda/cpyuda/client.pyx @@ -151,8 +151,6 @@ def get_data_batch(signals, sources): cdef put_nothing(const char* instruction): - if _pid != os.getpid(): - raise ClientException("Calling client from a process different to process in which library was initialised") cdef int handle = uda.idamPutAPI(instruction, NULL) return Result(Handle(handle)) @@ -304,6 +302,8 @@ cdef put_string(const char* instruction, const char* data): def put_data(instruction, data=None): + if _pid != os.getpid(): + raise ClientException("Calling client from a process different to process in which library was initialised") if isinstance(data, np.ndarray): if np.PyArray_TYPE(data) not in (np.NPY_STRING, np.NPY_UNICODE): return put_ndarray(instruction, data) From 2c18ad0fff4c9abfced761d981bf3dcbb810f695 Mon Sep 17 00:00:00 2001 From: sdixon Date: Thu, 27 Nov 2025 23:28:23 +0000 Subject: [PATCH 3/4] adding example script for mp pyuda clients --- source/wrappers/python/CMakeLists.txt | 5 +++ .../python/examples/parallel_client_demo.py | 39 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 source/wrappers/python/examples/parallel_client_demo.py diff --git a/source/wrappers/python/CMakeLists.txt b/source/wrappers/python/CMakeLists.txt index 29249195d..a202ad0f8 100755 --- a/source/wrappers/python/CMakeLists.txt +++ b/source/wrappers/python/CMakeLists.txt @@ -10,6 +10,11 @@ install( FILES ${CMAKE_CURRENT_LIST_DIR}/uda/__init__.py DESTINATION python_inst configure_file( ${CMAKE_CURRENT_LIST_DIR}/pyuda/_version.py.in ${CMAKE_CURRENT_BINARY_DIR}/pyuda/_version.py @ONLY ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/pyuda/_version.py DESTINATION python_installer/pyuda ) +file( GLOB EXAMPLE_SCRIPTS examples/*.py ) +install( FILES ${EXAMPLE_SCRIPTS} DESTINATION python_installer/examples ) +file( GLOB DEV_TEST_SCRIPTS devscripts/*.py ) +install( FILES ${DEV_TEST_SCRIPTS} DESTINATION python_installer/devscripts ) + find_package( OpenSSL REQUIRED ) find_package( LibXml2 REQUIRED ) find_package( LibMemcached QUIET ) diff --git a/source/wrappers/python/examples/parallel_client_demo.py b/source/wrappers/python/examples/parallel_client_demo.py new file mode 100644 index 000000000..fb9aac08a --- /dev/null +++ b/source/wrappers/python/examples/parallel_client_demo.py @@ -0,0 +1,39 @@ +import pyuda +import multiprocessing as mp +from enum import Enum, auto +from collections import namedtuple + +ConnectionDetails = namedtuple('ConnectionDetails', ['name', 'port']) +RequestData = namedtuple('RequestData', ['signal', 'source']) +# client = pyuda.Client() + +class Result(Enum): + SOME = auto() + ERROR = auto() + + +def task(queue: mp.Queue, server: ConnectionDetails, request: RequestData): + client = pyuda.Client() + pyuda.Client.port = server.port + pyuda.Client.server = server.name + + try: + result = client.get(*request) + queue.put((Result.SOME, result)) + except Exception as e: + queue.put((Result.ERROR, e)) + + +def main(): + server = ConnectionDetails("uda2.mast.l", 56565) + request = RequestData("help::help()", "") + results_q = mp.Queue() + p = mp.Process(target=task, args=(results_q, server, request)) + p.start() + item = results_q.get() + print(item) + p.join() + + +if __name__ == "__main__": + main() From c6b26512f104fcf3ca87d4ad6cbe94bb37935457 Mon Sep 17 00:00:00 2001 From: sdixon Date: Fri, 28 Nov 2025 00:02:45 +0000 Subject: [PATCH 4/4] fixing pyuda client fork guard and adding exmaple --- .../wrappers/python/examples/parallel_client_demo.py | 11 +++++++++-- source/wrappers/python/pyuda/cpyuda/client.pyx | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/source/wrappers/python/examples/parallel_client_demo.py b/source/wrappers/python/examples/parallel_client_demo.py index fb9aac08a..a486be216 100644 --- a/source/wrappers/python/examples/parallel_client_demo.py +++ b/source/wrappers/python/examples/parallel_client_demo.py @@ -5,7 +5,7 @@ ConnectionDetails = namedtuple('ConnectionDetails', ['name', 'port']) RequestData = namedtuple('RequestData', ['signal', 'source']) -# client = pyuda.Client() +client = pyuda.Client() class Result(Enum): SOME = auto() @@ -13,7 +13,8 @@ class Result(Enum): def task(queue: mp.Queue, server: ConnectionDetails, request: RequestData): - client = pyuda.Client() + # move client instantiation here to avoid errors + # client = pyuda.Client() pyuda.Client.port = server.port pyuda.Client.server = server.name @@ -27,6 +28,12 @@ def task(queue: mp.Queue, server: ConnectionDetails, request: RequestData): def main(): server = ConnectionDetails("uda2.mast.l", 56565) request = RequestData("help::help()", "") + + # No error if start method is "spawn" or if client is only + # instantiated in task (after fork) + mp.set_start_method('fork') + print("process start method is: " + mp.get_start_method()) + results_q = mp.Queue() p = mp.Process(target=task, args=(results_q, server, request)) p.start() diff --git a/source/wrappers/python/pyuda/cpyuda/client.pyx b/source/wrappers/python/pyuda/cpyuda/client.pyx index d922ae7e0..51b226d3f 100644 --- a/source/wrappers/python/pyuda/cpyuda/client.pyx +++ b/source/wrappers/python/pyuda/cpyuda/client.pyx @@ -41,6 +41,7 @@ else: def set_pid(): + global _pid _pid = os.getpid()