5

I have one module which uses python "threading" for concurrency, and "signal" for shutdown hook:

signal.signal(signal.SIGINT, self.shutdownhook)

I have another module which uses dbus and gobject

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
....
GObject.threads_init()
mainloop = GObject.MainLoop()
mainloop.run()

When I run them seperately, they both operate as expected and ctrl+c causes termination via "KeyboardInterrupt".

However, when I run them together the mainloop terminates but the shutdown hook is never called - the process does not terminate without kill -9 pid.

Can someone please explain why this occurs, and how best to integrate the two models

Here is a working example which highlights my problem. I cannot exit the program with just a CTRL+C and the shutdown hook is not called in this case either.

import threading
import signal
import sys
from gi.repository import GObject


def runMainloop():
        print('running mainloop')
        mainloop.run()

def shutdown():
        print('shutdown')

def readInput():
        print('readInput')
        print(sys.stdin.readline())

if __name__ == '__main__':
        signal.signal(signal.SIGINT, shutdown)
        signal.signal(signal.SIGTERM, shutdown)
        GObject.threads_init()
        mainloop = GObject.MainLoop()

        mainloopThread = threading.Thread(name='mainloop', target=runMainloop)
        mainloopThread.setDaemon(True)
        mainloopThread.start()
        print('started')

        inputThread = threading.Thread(name='input', target=readInput)
        inputThread.start()
        print('started input')
pstanton
  • 35,033
  • 24
  • 126
  • 168

2 Answers2

6

No one is interested, so let me try.

Just to be on the same page:

import signal
from gi.repository import GObject

GObject.threads_init()
mainloop = GObject.MainLoop()

signal.signal(signal.SIGINT, lambda n, f: mainloop.quit())

mainloop.run()

This code works:

import signal
from gi.repository import GObject

signal.signal(signal.SIGINT, lambda n, f: print("kill"))

GObject.threads_init()
mainloop = GObject.MainLoop()
mainloop.run()

I've first registered signal handler, and then initiated the loop. Strange is that it is not called. However result is - as expected...

As a side note - according to their doc... mainloop is deprecated. That is first thing.

Edit

Here is example with reading from stdin inside MainLoop:

import signal
import sys
from gi.repository import GObject, GLib

GObject.threads_init()

def readInput():
    print('readInput\n')
    while True:
        input = sys.stdin.readline()
        print(input)
        if input.strip() == 'exit':
            print('closing main loop')
            mainloop.quit()
            print('terminating thread')
            return

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    mainloop = GObject.MainLoop.new(None, False)
    GObject.timeout_add(1000, readInput)

    # inputThread = threading.Thread(name='input', target=readInput)
    # inputThread.start()
    # print('started input')

    print('running mainloop\n')
    try:
        mainloop.run()
    except KeyboardInterrupt:
        mainloop.quit()

Adding .new(None, False) allows CTRL-C working normally. Took it from here, also here is another thread about integrating pulse audio controller with GLib/GObject loop. There are samples about integration dbus with the loop, but I'm not sure which way you wish to go...

Community
  • 1
  • 1
Michał Zaborowski
  • 3,911
  • 2
  • 19
  • 39
  • thanks, but that doesn't seem to help the situation. i've updated the question with a better working example. – pstanton Dec 28 '17 at 05:28
  • Yes, problem is that `MainLoop` takes all the resources, and since Python goes single threaded since GIL... The only way to integrate with DBus is putting your "stuff" into `MainLoop` event system. There are examples on the net, I'll try to update answer to deal with it. – Michał Zaborowski Dec 28 '17 at 12:41
  • ok .. i'm confused because my other threads seem to work just fine, just the signal / shutdown is never triggered. so MainLoop gobbles the signal? maybe i can just add a shutdown hook to MainLoop? – pstanton Dec 29 '17 at 04:32
  • According to their docs `MainLoop` takes that signals. I don't get that either - signals can be controlled by main thread... Maybe that is because of doing it outside Python? I mean in C/C++/SharedObject... not sure. – Michał Zaborowski Dec 29 '17 at 09:49
2

Adding a timer makes the loop receive Unix signals.

import gi
from gi.repository import GLib
import signal

GLib.threads_init()

mainloop = GLib.MainLoop()

signal.signal(signal.SIGTERM, lambda signum, frame: mainloop.quit())

GLib.timeout_add(1000, lambda *args: (print("tick") or True))

try:
    mainloop.run()
except KeyboardInterrupt:
    print()
haael
  • 972
  • 2
  • 10
  • 22