Search code examples
pythoncgnuradio

Python C API and gnuradio, Python starts threads that don't terminate after Py_Finalize()


When I launch a python code from C code, there are many threads that do not stop at the end of the execution of the Python code:

    ps -T 20402
    PID    SPID TTY      STAT   TIME COMMAND
  20402   20402 pts/0    Rl     0:11 ./test
  20402   20427 pts/0    Sl     0:00 ./test
  20402   20428 pts/0    Sl     0:00 ./test
  20402   20443 pts/0    Sl     0:00 ./test
  20402   20458 pts/0    Sl     0:00 ./test
  20402   20461 pts/0    Sl     0:00 ./test
  20402   20464 pts/0    Sl     0:00 ./test
  20402   20471 pts/0    Sl     0:00 ./test

These threads seem to be related to the use of libraries related to gnuradio. Here is my code in Python:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#
# SPDX-License-Identifier: GPL-3.0
#
# GNU Radio Python Flow Graph
# Title: Weather satellite
# Author: Norian
# GNU Radio version: 3.8.1.0

from gnuradio import analog
from gnuradio import blocks
from gnuradio import filter
from gnuradio.filter import firdes
from gnuradio import gr
import sys
import signal
from argparse import ArgumentParser
from gnuradio.eng_arg import eng_float, intx
from gnuradio import eng_notation
import osmosdr
import time

class Weather_sat(gr.top_block):

    def __init__(self):
        gr.top_block.__init__(self, "Weather satellite")

        ##################################################
        # Variables
        ##################################################
        self.samp_rate = samp_rate = 1411200

        ##################################################
        # Blocks
        ##################################################
        self.rational_resampler_xxx_0 = filter.rational_resampler_fff(
                interpolation=208,
                decimation=441,
                taps=None,
                fractional_bw=None)
        self.osmosdr_source_0 = osmosdr.source(
            args="numchan=" + str(1) + " " + ""
        )
        self.osmosdr_source_0.set_time_now(osmosdr.time_spec_t(time.time()), osmosdr.ALL_MBOARDS)
        self.osmosdr_source_0.set_sample_rate(samp_rate)
        self.osmosdr_source_0.set_center_freq(137100000, 0)
        self.osmosdr_source_0.set_freq_corr(0, 0)
        self.osmosdr_source_0.set_gain(40, 0)
        self.osmosdr_source_0.set_if_gain(20, 0)
        self.osmosdr_source_0.set_bb_gain(20, 0)
        self.osmosdr_source_0.set_antenna('', 0)
        self.osmosdr_source_0.set_bandwidth(0, 0)
        self.low_pass_filter_0 = filter.fir_filter_ccf(
            16,
            firdes.low_pass(
                1,
                samp_rate,
                110000,
                10000,
                firdes.WIN_HAMMING,
                6.76))
        self.blocks_wavfile_sink_0 = blocks.wavfile_sink('/home/norian/Documents/GNURADIO/noaadeu.wav', 1, 20800, 16)
        self.analog_wfm_rcv_0 = analog.wfm_rcv(
            quad_rate=88200,
            audio_decimation=2,
        )



        ##################################################
        # Connections
        ##################################################
        self.connect((self.analog_wfm_rcv_0, 0), (self.rational_resampler_xxx_0, 0))
        self.connect((self.low_pass_filter_0, 0), (self.analog_wfm_rcv_0, 0))
        self.connect((self.osmosdr_source_0, 0), (self.low_pass_filter_0, 0))
        self.connect((self.rational_resampler_xxx_0, 0), (self.blocks_wavfile_sink_0, 0))

    def get_samp_rate(self):
        return self.samp_rate

    def set_samp_rate(self, samp_rate):
        self.samp_rate = samp_rate
        self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.samp_rate, 110000, 10000, firdes.WIN_HAMMING, 6.76))
        self.osmosdr_source_0.set_sample_rate(self.samp_rate)



def main(freq):
    top_block_cls=Weather_sat
    tb = top_block_cls()

    def sig_handler(sig=None, frame=None):
        tb.stop()
        tb.wait()
        sys.exit(0)

    signal.signal(signal.SIGINT, sig_handler)
    signal.signal(signal.SIGTERM, sig_handler)

    tb.start()
    try:
        input('Press Enter to quit: ')
    except EOFError:
        pass
    tb.stop()
    tb.wait()

And here is my C code:

//prgm.c
#include <stdio.h>
#include "Python.h"

int main(void) {
    Py_Initialize();

    /* add . to the path */
    PyObject* sys = PyImport_ImportModule("sys");
    PyObject* path = PyObject_GetAttrString(sys, "path");
    PyList_Insert(path, 0, PyUnicode_FromString("."));

    /* import desired function */
    PyObject* pModule = PyImport_ImportModule("main");
    PyObject* pFunc = PyObject_GetAttrString(pModule, "main");

    /* call it */
    PyObject* pArgs = Py_BuildValue("(s)", "137912500");
    PyObject_CallObject(pFunc, pArgs);
    Py_Finalize();
    fprintf(stderr,"End of python software");
    while(1);
    return 0;
}

Can you explain to me how to detect threads created by python and kill them?

I am working on Ubuntu 20.4. I am using gnuradio version 3.8 and I installed it from apt.

Thank you very much for your help.


Solution

  • Perhaps gnuradio creates threads from its own C API? – user23952

    Exactly. GNU Radio is a heavily multithreaded application.

    Can you explain to me how to detect threads created by python and kill them?

    You can "kill" processes, you can't "kill" threads - you can interrupt/join them (which is akin in the sense that it means they don't get scheduled anymore, but different in the sense that the resources allocated to them don't disappear – because threads don't hold own resources (aside from a stack), it's the process that does.

    Note that existing threads in themselves are not inherently a problem - it's perfectly normal for a program to have dormant threads that get cleaned up on exit by the operating system and not earlier; threads serve various purposes, including things like handling signals, waiting for RPC connections, managing other resources... which might not even occur in your case.

    Do you know how to exit the gr-osmosdr block properly?

    This might very well be a problem of the specific driver source encapsulated in the osmosdr source.

    Probably this all exits "properly", you've not actually described anything that goes wrong!