0

I'm trying to detect system suspend using the following algorithm:

while True:
    lastchecked = now()
    if now() - lastchecked > 1s: print "suspend detected!"

But I ran into a problem: If suspend happens between 2nd and 3rd line, then the loop catches it. But if suspend happens between 1st and 2nd line, then the algorithm fails.

Is there some commonly used method for this type of situation? Preferably OS-independent please, I don't want to hook into OS events and suchlike.

user2108462
  • 855
  • 7
  • 23
  • Looks like an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Please add details of what you're trying to achieve -- there must be a vastly better way than polling. – ivan_pozdeev Jun 10 '18 at 16:18
  • In the vast majority of cases, you don't _need_ to check for system suspend -- a well-designed algorithm would simply continue from where it left off. That's kind of the whole purpose of system suspend. – ivan_pozdeev Jun 10 '18 at 16:21
  • 1
    My program is kind of a system monitor type of application - it needs to know if the system has went to sleep and for how long. – user2108462 Jun 10 '18 at 16:28

2 Answers2

1

First of all, polling is inferiour to notifications because it wastes system resources that could instead be spent on useful work (and your current loop is a busy loop, too). Naturally, power management event systems are OS-specific (see Power Management Notifications in Linux and how to hook to events / messages in windows using python) but if you're writing a system monitor app, you can't hide from OS differences anyway.


Now, the key here is to always have two timestamps in memory and overwrite the older one:

T1
  \
   T2
   <- compare
  / 
T3
 <- compare
  \
   T4
   etc
  /

Then, at whichever moment a suspend happens, the next timestamp will be set later than it should, and the comparison will see the difference.

This way, you don't even need to poll every second or so! Your poll interval needs to be only as short as is the shortest suspend period that you'd like to detect. E.g. if you'd like to detect at least a 30s suspend period, you only have to poll every 30s: if the system sleeps for longer, it will be guaranteed to "miss a beat".

i=0
poll_period=30
t=[time.time()]*2
while True:
    # actually, poll period will be slightly longer due to code overhead:
    # https://stackoverflow.com/questions/26774186/looping-at-a-constant-rate-with-high-precision-for-signal-sampling
    # but that doesn't make a difference in this case
    time.sleep(poll_period)
    t[i]=time.time()
    if t[i] - t[(i+1)%2] > poll_period + 2: print "suspend detected"
    i = (i+1)%2

Note that you will get false positives if your process gets preempted by others. That's yet another reason why using system notifications is a vastly superior way.

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • If I want to put in some more function calls in the loop, does it matter where I put it? – user2108462 Jun 10 '18 at 17:13
  • @user2108462 Not really, but it'd probably be easier if it's all on the same side of `time.sleep()` -- then you'll be sure which code's overhead is a part of which measured interval. – ivan_pozdeev Jun 10 '18 at 17:19
0

I think this algorithm works:

last1 = now()
last2 = now()
while True:
    last1 = now()
    if now() - last1 > 1s or now() - last2 > 1s: print "suspend detected!"
    last2 = now()
    if now() - last1 > 1s or now() - last2 > 1s: print "suspend detected!"

This will detect a suspend immediately after any line in the while loop:

  • Suspend after the first line will be detected by the second (since last1 will be old).
  • Suspend after the second line will be detected by the 4th (since last1 will be old).
  • Suspend after the 3rd line will be detected by the 4th (since both last1 and last2 will be old).
  • Suspend after the 4th line will be detected by the 2nd (since last2 will be old).

I believe this is the minimal amount of code necessary to detect system suspend. Please correct me if I'm wrong.

user2108462
  • 855
  • 7
  • 23