Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] recommended way of finalizing greenlets on SIGINT, SIGTERM, ... #1683

Open
woutdenolf opened this issue Sep 30, 2020 · 1 comment
Labels
Type: Question User support and/or waiting for responses

Comments

@woutdenolf
Copy link

woutdenolf commented Sep 30, 2020

I'm looking for the best way to execute some finalization in my greenlets upon SIGINT, SIGTERM, ...

It seems that gevent.signal allows me to register signal handlers but they are excuted directly in the hub so I suppose I can't do gevent stuff (like killing a greenlet and waiting until killed).

In the example below I'm using hub watchers instead. My questions:

  1. Is this the best way to making sure greenlet finalization is called when the greenlet is killed or upon SIGINT, SIGTERM, ...
  2. It seems that I need to call os.kill in the watcher handler because, for example, sometimes I need a second SIGINT to close the process

Example (run and press CTRL-C before it ends):

import os
from contextlib import contextmanager
import gevent
from gevent import monkey
from gevent import signal
from gevent import hub

monkey.patch_all(thread=False)


def register_signal_handler(signalnum, handler, *args, **kw):
    def wrapper():
        try:
            handler(*args, **kw)
        finally:
            watcher.cancel()
            os.kill(os.getpid(), signalnum)

    watcher = hub.signal(signalnum, wrapper)
    return watcher


@contextmanager
def kill_on_exit(timeout=3):
    handler = gevent.getcurrent().kill
    watchers = [
        register_signal_handler(signalnum, handler, timeout=timeout)
        for signalnum in (signal.SIGQUIT, signal.SIGINT, signal.SIGTERM)
    ]
    try:
        yield
    finally:
        for w in watchers:
            try:
                w.cancel()
            except Exception as e:
                print(f"Exception during kill_on_exit cleanup: {e}")


def main(filename):
    # This is just a random example to illustrate proper finalization
    print("Start writing to ", filename)
    with open(filename, mode="w") as f:
        try:
            for i in range(100):
                print(f"Writing {i} to ", filename)
                print(i, file=f)
                gevent.sleep(1)
        finally:
            print("Finalize ", filename)
            f.write("END")


def safe_main(filename):
    with kill_on_exit():
        main(filename)


glts = [gevent.spawn(safe_main, f"/tmp/file{i}.txt") for i in range(10)]
gevent.joinall(glts)
@jamadden
Copy link
Member

gevent makes the same basic guarantee that Python does with regards to signals: They are delivered to the main greenlet (or main thread). Other than that, how you handle them will be application specific, depending on exactly what you're trying to do and why.

I can't say I've had a need to do anything like what you're showing here.

@jamadden jamadden added the Type: Question User support and/or waiting for responses label Dec 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Question User support and/or waiting for responses
Projects
None yet
Development

No branches or pull requests

2 participants