catubc
catubc

Reputation: 508

Python variable dynamically accessible in C using ctypes

I'm trying to get python to pass a variable to a C module (called on 2nd thread) that is updated in python and the updates are detected in C. For now I'm only interested in one direction of data change: Python->C.

My python code is from an example I found online. It simply creates an array (_sum.numbers) and then passes the pointers to the array to the C module on a 2nd thread:

import ctypes
import numpy as np
import time as time
from concurrent.futures import ThreadPoolExecutor

_sum = ctypes.CDLL('/home/pi/murphylab_picam/temp/libsum.so')
_sum.c_function.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_int))

#Function to eventually strobe LEDs; for now just print an array;
def strobe(_sum):
    num_numbers = len(_sum.numbers)
    array_type = ctypes.c_int * num_numbers
    result = _sum.c_function(ctypes.c_int(num_numbers), array_type(*_sum.numbers))

#Initialize array of values
_sum.numbers = np.arange(5)

#Call on another thread
t = ThreadPoolExecutor(max_workers=1)
t.map(strobe, [_sum])
time.sleep(0.25)

#Change array values and compare Python and C printouts
for i in range(10):
    _sum.numbers[0] = np.random.randint(10) #-----------------> These changes are not detected in C
    print ("Python: ", _sum.numbers)
    time.sleep(.25)

And my C code just prints out the array over and over (4 x second) hoping that the changes in array values made in python will eventually show up in C:

#include <stdio.h>
#include <time.h> 

// compile instructions: cc -fPIC -shared -o libsum.so sum.c

int c_function(int num_numbers, volatile int *numbers) {
    int i,j, useconds = 250000;

    for (j=0; j<10;j++){
        printf("C:        ");
        for (i = 0; i < num_numbers; i++) {
            //sum += numbers[i];
            printf("%i ", numbers[i]);
        }
        usleep(useconds);
        printf("\n");
    }
    return 0;
}

The results are that the array is only changing in python and C only ever prints out the original array (0,1,2,3,4):

C:        0 1 2 3 4 
Python:  [6 1 2 3 4]
C:        0 1 2 3 4 
Python:  [8 1 2 3 4]
C:        0 1 2 3 4 
Python:  [3 1 2 3 4]
C:        0 1 2 3 4 
Python:  [6 1 2 3 4]

I thought that volatile pointers would take care of this dynamic information passing back and forth, but clearly this is not happening. I've even tried using double volatiles for the array. It seems to me that either in Python the "=" command destroys the original array; or possibly that my use of the variable in C isn't correct. I saw some solutions using struct and passing an object back from C, but that's way too complex for my purpose - not to mention that I need to initialise the structure from the Python side - if possible.

Thanks.

Upvotes: 1

Views: 583

Answers (1)

ShadowRanger
ShadowRanger

Reputation: 155704

When you do: array_type(*_sum.numbers) to pass the second argument to _sum.c_function, you're creating a brand new ctypes array from the values in _sum.numbers (it couldn't even use the buffer protocol to make a view of the underlying data if it wanted to; using * unpacks the iterable into discrete values with no tie to the original iterable).

In this case, you're not even using numpy stuff, so you could just do:

_sum.numbers = (ctypes.c_int * 5)(*range(5))

then use _sum.numbers everywhere, without further wrapping, e.g.

result = _sum.c_function(num_numbers, _sum.numbers)

It doesn't have a friendly print form, so you would want to change your print to print out _sum.numbers[:] (which creates a list of the current values in the ctypes array).

If you need a numpy array backed by the same storage, you can use numpy.frombuffer to create such a view backed by the ctypes array:

npview = np.frombuffer(_sum.numbers, np.int32)

then use npview when you need a numpy types, and _sum.numbers for ctypes purposes.

Upvotes: 3

Related Questions