Python3: segfault c-типа из многопоточной общей библиотеки CPython

Программы на Python
Ответить
Anonymous
 Python3: segfault c-типа из многопоточной общей библиотеки C

Сообщение Anonymous »

Я просмотрел другие вопросы и ответы, связанные со StackOverflow, и следил за предоставленной информацией. У меня все еще возникают проблемы.
Запуск Python3 в Linux, который обращается к общей библиотеке C (.so). Библиотека C будет запускать потоки по мере необходимости. У меня возникла проблема с ошибками сегментации при доступе к API C во время работы потоков. Проблема возникает только в том случае, если поток C вызывает функцию обратного вызова в Python. Если поток C выполняется без вызова обратного вызова, проблем нет. В приведенном ниже коде строки Python print(GetData(128)) получат строку и выведут ее на консоль, прежде чем вызвать ошибку сегмента. Опять же, я получаю ошибку сегментации GetData только в том случае, если поток работает И вызывает событие или обратный вызов. Другие API C, которые возвращают символ char*, но не принимают параметров, прекрасно работают при работающем потоке.
Python:

Код: Выделить всё

# Within a class, but not shown for simplicity.
EVENT = CFUNCTYPE(c_void_p, c_int)
lib = CDLL("library.so")
lib.GetData.restype = c_uint
lib.GetData.argtypes = [c_char_p, c_uint]
lib.SetEvent.argtypes = [EVENT]

def GetData(count):
buf = create_string_buffer(count)
lib.GetData(buf, count)
return buf.value.decode("utf-8")

def SetEvent(ev):
lib.SetEvent(ev)

# Code outside of class.
def Event(e):
print("Event " + str(e))
if e == 2:
print(GetData(128)) # Event works fine until I call this.

SetEvent(EVENT(Event))

cnt = 0
while cnt < 10:
print(GetData(128)) # Works if I do not call SetEvent.
cnt = cnt + 1
sleep(1)
Код C/C++:

Код: Выделить всё

typedef void(*Event)(int i);
Event efunc = nullptr;

static void ThreadFunc()
{
int i = 0;

while(true)
{
if (efunc != nullptr) { efunc(++i); } // Raise event.

if (i > 10) { i = 0; }

std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

extern "C" unsigned int GetData(char* data, unsigned int count)
{
const char* str = "Random data for testing";
std::strcpy(data, str);
return std::strlen(str);
}

extern "C" void SetEvent(Event e)
{
efunc = e;

if (efunc != nullptr) { myThread = std::thread(ThreadFunc); }
}
Я привел простые версии приведенного выше кода, чтобы облегчить понимание проблемы. Я знаю, что отсутствует проверка ошибок, и поток работает вечно. В реальном коде это не так, но этот пост был бы слишком длинным, если бы я добавил все. Кроме того, библиотеке C необходимо создавать потоки, это невозможно изменить. Та же библиотека C работает при динамической загрузке и запуске с C++ и C#.
Обновить
Я добавил библиотеку Python в общую библиотеку C/C++ и внес следующие изменения в код (см. выше).
C/C++:

Код: Выделить всё

static void ThreadFunc()
{
int i = 0;

while(true)
{
if (efunc != nullptr)
{
PyGILState_STATE state = PyGILState_Ensure();
// Py_BEGIN_ALLOW_THREADS
efunc(++i); // Raise event.
// Py_END_ALLOW_THREADS
PyGILState_Release(state);
}

if (i > 10) { i = 0; }

std::this_thread::sleep_for(std::chrono::seconds(1));
}
}

extern "C" void SetEvent(Event e)
{
efunc = e;

if (efunc != nullptr)
{
if (!PyEval_ThreadsInitialized())
{
PyEval_InitThreads();
}

myThread = std::thread(ThreadFunc)
}
}
Все работает до тех пор, пока обратный вызов Python не запустит print(GetData(128)), после этого каждый обратный вызов кода Python выводит на консоль SystemError: null аргумент для внутренней процедуры.
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»