noxdafox/pebble

Cancel future returned from run_in_executor caused SIGTERM signal to main process

yli02 opened this issue · 1 comments

Hi, thanks for your effort in developing and maintaining this very good library. It is very helpful for me to use it with asyncio.

I found one strange behavior but not sure whether it could be considered as a bug. If you run the following code in a python file, the main process where the event loop run get SIGTERM signal after the cancellation of a running future. This is fine if the main process just ignore it, however, for my real code running in K8S, the SIGTERM signal is handled in main process for graceful shutdown in case of pod scaling down. Therefore, the cancellation of a future causing graceful shutdown is not really what I want.

Could you have a look at the code and see whether this is a bug? If it is not, do you have any suggestion about how could I avoid receiving SIGTERM in main process in case of cancelling running future. Thanks a lot.

import asyncio
import time
from functools import partial
import signal

from pebble import ProcessPool


def cpu_bound_task():
    print(f"CPU task started")
    time.sleep(30)
    print(f"CPU task completed")


async def cancel_future(future):
    print('cancel future')
    # to give the cpu task a chance to start
    await asyncio.sleep(5)
    cancel_result = future.cancel(msg='cancel future due to OOM')
    print(f"done cancel: {cancel_result}")


async def main():
    pool = ProcessPool(max_workers=2)
    loop = asyncio.get_running_loop()

    loop.add_signal_handler(
        signal.SIGTERM,
        lambda: print("SIGTERM received")
    )

    func_in_executor = partial(cpu_bound_task)
    result_future = loop.run_in_executor(pool, func_in_executor, 0)

    try:
        results = await asyncio.gather(
            cancel_future(result_future),
            result_future,
            # return_exceptions=True
        )

        for res in results:
            print(res, type(res))

    except asyncio.CancelledError:
        print('gather is cancelled')

    await asyncio.sleep(30)

    print("done sleep in main")


if __name__ == "__main__":
    asyncio.run(main())

Hi, I found that this seems to be related to issue #66, I will close this one.