Home About

Running applications only when you are idle

The problem: Bad responsiveness caused by high I/O load

On a typical desktop PC, there are several long-running tasks with a high I/O load which usually need to be executed regularly — like backups, indexing files for search, checking the backups, etc. Tasks with high I/O load however can cause bad responsiveness of the system under Linux, which results in applications being frozen, the mouse cursor not moving, etc.

With the most systems this can usually be fixed by selecting the CFQ scheduler in the kernel. If you are running particular new hardware however, this might not work since e.g. the kernel does not support setting an I/O scheduler for those new fancy m.2 SSDs. Running an application like recollindex or borgbackup on one of those disks can completely fubar the desktop until they are done.

The solution

To work around this problem, I've created the following script:

#!/bin/bash
# execute-idle.sh
# Copyright (C) 2018 Philipp Ludwig, <idle (at) philippludwig.net>
# License: GPLv3
export DISPLAY=$(cat /proc/$(pidof dwm)/environ | tr '\0' '\n' | \grep '^DISPLAY=' | sed 's/DISPLAY=//')
set -e

if [ "$1" == "" ]; then
	echo "Error: No command given"
	exit 1
fi

[ -z $DISPLAY ] && exec "$@"

$@ &

PID=$!

if [ "$PID" == "" ]; then
	echo "Could not determine PID of $1!"
	exit 1
fi

kill -SIGSTOP $PID
for p in $(ps --ppid $PID -ho '%p'); do kill -SIGSTOP $p; done

while kill -0 $PID &> /dev/null; do
	sleep 5
	if [ "$(xprintidle)" -gt 30000 ]; then # 30 seconds
		signal="-SIGCONT"
	else
		signal="-SIGSTOP"
	fi

	kill $signal $PID &> /dev/null
	for p in $(ps --ppid $PID -ho '%p'); do kill $signal $p &> /dev/null; done
done
	

It is called execute-idle.sh and designed to run these tasks only while you are not using the computer (e.g. when you are in the kitchen for 10 minutes, waiting for a cup of coffee).

Dependencies: xprintidle, bash

How it works

execute-idle.sh first starts the task specified as an argument, then pauses it immediately by sending a SIGSTOP. Then it calls xprintidle every 5 seconds to check if you are still using the computer — if not, it sends a SIGCONT signal to the process and all of its children, letting the job continue. As soon as you return and type on your keyboard or move your mouse, execute-idle.sh again sends SIGSTOP to the process and its children, pausing the task until you are idle again.

How to use it

Just put it in your crontab like this:

0 12 * * * ~/tools/execute-idle.sh ~/my-backup-script.sh

Remarks

You might think that this causes some jobs to never finish, but after using this script for several weeks I didn't encounter any problems like this. Due to the rather short 5 second interval, execute-idle.sh can utilize even small breaks of e.g. 30 seconds.

Try running your backup with it — your regular backup check job should tell you if something went wrong.