Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does time.sleep() stop immediately when using signal handler?

While doing some messing around with making a WSGI server, I noticed some test calls to time.sleep() inside a loop were only working the first iteration. Here's a reproduction:

import os
import signal 
import time


def run_loop():
    i = 0
    while True:
        print('PID: {}, i = {}'.format(os.getpid(), i))
        time.sleep(5) # Only works the first time
        i += 1

        pid = os.fork() . # May or may not be related?
        if pid == 0:
            print('PID: {}, CHILD'.format(os.getpid(), i))
            os._exit(0)


if __name__ == '__main__':
    # If I uncomment this line it works fine.
    # If I leave it as is, the time.sleep in run_loop() only waits once. 
    signal.signal(signal.SIGCHLD, lambda a, b: os.wait())

    run_loop()

I am aware of the following part in the time.sleep() documentation.

The actual suspension time may be less than that requested because any caught signal will terminate the sleep() following execution of that signal’s catching routine.

Is this because the SIGCHLD signal keeps triggering while time.sleep() is running? What would cause the signal to cancel every single call to time.sleep() consistently?

like image 296
SamuelN Avatar asked Dec 31 '25 08:12

SamuelN


1 Answers

from the linux sleep documentation

sleep() causes the calling thread to sleep either until the number of real-time seconds specified in seconds have elapsed or until a signal arrives which is not ignored.

there is a linux c function that allows you to continue sleeping after being interrupted by a signal called 'nanosleep'. it either sleeps a given amount of nanoseconds or sleeps until a signal arrives and returns the number of nanoseconds that remain to be slept but python doesn't expose it in any module

you could simulate the same behavior you would want from nanosleep yourself, something like:

import datetime
import time
def busy_sleep(seconds):
    start_time = datetime.datetime.now()
    seconds_slept = 0
    while seconds_slept < seconds:
        time.sleep(seconds - seconds_slept)
        seconds_slept = (datetime.datetime.now() - start_time).total_seconds()
like image 169
AntiMatterDynamite Avatar answered Jan 02 '26 21:01

AntiMatterDynamite



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!