< up >
2021-07-09

bad practice: busy-waiting

Busy-waiting is one of the easiest ways of exhausting a cpu1:

package main

func main() {
  for true {
    /* complex logic */
  }
}

What looks like a stupid construct one would never do by accident is often introduced on waiting a resource or state change. I mostly see this construct on retrying connecting to a service a couple of times.

Analyze

Use top:

PID     USER       PR   NI VIRT      RES    SHR S %CPU    %MEM  TIME+   COMMAND
1237306 evilcookie 20   0  702424    936    644 R 100.3   0.0   0:59.39 busywaiting      

The process has a load of one or higher.

Solutions

A general solution for busy-waiting is telling the CPU to do something else for some time. This can be done by using the syscall sleep2:

package main

import "time"

func main() {
  for true {
    /* complex logic */
    time.Sleep(1 * time.Millisecond)
  }
}

Sleeping one millisecond is enough to make the process barely remarkable:

PID     USER      PR   NI VIRT      RES    SHR S %CPU  %MEM  TIME+   COMMAND
1242442 rpour     20   0  702476   1340    840 S 8.6   0.0   0:01.05 busywaiting

  1. all examples are written in golang
  2. sleep (3): “On Linux, sleep() is implemented via nanosleep(2).”