Using threading lock in ctypes callback function

徘徊边缘 提交于 2020-01-02 08:05:23

问题


I want to use a ctypes dll from a twisted application. Minimal example concocted here:

from ctypes import *
from threading import Lock

lock = Lock()

dll = windll.LoadLibrary('mydll.dll')

l = [1,2,3]

def callback():
    lock.acquire()
    l.pop()
    lock.release()
    return 0

C_CALLBACK = CFUNCTYPE(c_int)
c_callback = C_CALLBACK(callback)

# this is a non blocking function call starts a hardware task that fires a callback upon completion
dll.registerCallback(c_callback)

while(True):
    # in reality this block gets called from a twisted server application
    lock.acquire()
    l.append(l.pop() + 1)
    lock.release()

The dll has a function (dll.registerCallback) that takes in a ctypes callback function, starts a hardware event, and fires a callback when the hardware indicates the hardware task is complete.

From the API documentation:

The callback function is called in a DAQmx thread.

Somewhere on the web they attempt to explain what a "DAQmx thread" is:

...your callback will be called and run in a DAQmx driver thread and will be run asynchronously (not in the same thread) in relation to your program.

The full documentation cound be found here. I changed the function signature in my example for simplicity.

So I suppose we can safely assume that the dll is spawning a thread.

Will the locks I have in place ensure that the callback function does not attempt to access the list l when it's in the middle of the pop operation in the mainloop, and vice versa? Or does this scheme only work when you use threads created using the threading library? What is the recommended practice here?


回答1:


The first thing that ctypes _CallPythonObject does is call PyGILState_Ensure(), which will call PyThreadState_New to create a new thread state if necessary. Beyond that it's vanilla Python threaded code, so your lock should work fine. It worked for me in the example below (Linux, Python 2.7.3):

from ctypes import *
import threading

lock = threading.Lock()
lib = CDLL('./tmp.so')
callback_t = CFUNCTYPE(c_int)

def f():
  with lock:
    print threading.current_thread()
  return 21

callback = callback_t(f)
lib.registerCallback(callback)
>>> lock.acquire()
True
>>> t = threading.Thread(target=lib.event)
>>> t.start()
>>> lock.locked()
True
>>> lock.release()
>>> <_DummyThread(Dummy-2, started daemon -1230402704)>
res: 21

The DummyThread output is from printing the current thread in the callback. Threads created outside of Python get a 'dummy' name.

tmp.c:

#include <stdio.h>
#include <pthread.h>

typedef int (*callback_t)(void);
callback_t callback = NULL;

void *doCallback(void *arg)
{
    int res = callback();
    printf("res: %d\n", res);
    pthread_exit(0);
}

int event(void)
{
    pthread_t callback_thread;
    pthread_create(&callback_thread, NULL, doCallback, NULL);
    pthread_join(callback_thread, NULL);
    return 0;
}

int registerCallback(callback_t foo)
{
    callback = foo;
    return 0;
}


来源:https://stackoverflow.com/questions/15952086/using-threading-lock-in-ctypes-callback-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!