Остановите запущенное расширение C /C в Django

#python #c #django #python-c-api

Вопрос:

Краткое изложение проблемы

Я внедряю давно работающее расширение C/C в рамках платформы Django (вызывается через сообщение с запросом). В некоторых случаях пользователь может захотеть прервать вычисление, просто оставив/выгрузив конкретную веб-страницу, на которой вызывается расширение C. Можно ли удалить расширение из Python?

Минимальный Воспроизводимый Пример

Чтобы воспроизвести проблему, мы можем создать наше собственное расширение Фибоначчи C. Вызов в Django выглядит следующим образом:

 import fibonacci
from django.views.generic.base import TemplateView

# Computation
class ComputationView(TemplateView):
    template_name = 'Generic/computation_page.html'

    @staticmethod
    def get_results(request):
        N=1000
        if request.method == "POST":  # When button is pressed
            # Long computations
            cs_min = fibonacci.fib(N)
        else:
            cs_min = None
        return render(request, ComputationView.template_name, {'cs_min': cs_min})
 

В то время как код на C является:

 #include <Python.h>

// Compute the Minimal cutsets
int Cfib(int n)
{
    int fib1;
    int fib2;
    if (n < 2)
        return n;
    else{
        Py_BEGIN_ALLOW_THREADS // Release GIL
        // Some stric operations in C
        Py_END_ALLOW_THREADS // Reacquire GIL
        return Cfib(n-1) Cfib(n-2);
    }
}

// Our Python binding to our C function
// This will take one and only one non-keyword argument
static PyObject* fib(PyObject* self, PyObject* args)
{
    // instantiate our 'n' value
    int n; // Order of cutset

    // if our `n` value
    if(!PyArg_ParseTuple(args, "i", amp;n))
        return NULL;
    // return our computed fib number
    return Py_BuildValue("i", Cfib(n));
}

// Our Module's Function Definition struct
// We require this `NULL` to signal the end of our method
// definition
static PyMethodDef myMethods[] = {
    { "fib", fib, METH_VARARGS | METH_KEYWORDS, "Computes Fibonacci" },
    { NULL, NULL, 0, NULL }
};

// Our Module Definition struct
static struct PyModuleDef fibonacci = {
    PyModuleDef_HEAD_INIT,
    "fibonacci",
    "Test Module",
    -1,
    myMethods
};

// Initializes our module using our above struct
PyMODINIT_FUNC PyInit_fibonacci(void)
{
    return PyModule_Create(amp;fibonacci);
}
 

As you can see, the user can do other actions thanks to the GIL management ( ALLOW_THREADS ) while the C function is running.

Alternative approach I have tried

Since I have been unable to kill the running function, I have tried directly killing the thread in charge of the C function by doing:

 import psutil

# Get the process
current_process = psutil.Process()
# Iterate the threads and find the one which has spent more time
max_time = 0
ind_max = 0
for ind, thread in enumerate(current_process.threads()):
    if thread.user_time > max_time:
        max_time = thread.user_time
        ind_max = ind
                            
# Kill the thread with more time
# This only works in Linux !!!!!
if platform.system()!= "Windows" and max_time > 10:
    signal.pthread_kill(current_process.threads()[ind_max].id, signal.SIGINT)
 

However, this approach kills not only the responsible thread, but the entire Django process.

Possible alternative approach

I am aware that you can arise exceptions inside the C extension. For instance:

 PyErr_SetString(PyExc_TypeError, "Custom error message");
 

Если бы я мог прочитать значение, хранящееся в базе данных, или передать каким-либо образом аргумент (по ссылке?) чтобы функция могла непрерывно считываться во время работы, я мог бы достичь той же цели-остановить функцию. Однако я понятия не имею, как это сделать.