Jérôme Belleman
Home  •  Tools  •  Posts  •  Talks  •  Travels  •  Graphics  •  About Me

Multitasking and Keeping Track of Tasks

31 May 2015

This is about the art of starting many tasks and keeping track of their progress and completion with on-the-fly notifications, with no special software.

Preemptive multitasking operating systems are a blessing, particularly for people like me who grew up with cooperative multitasking. It's tempting to start many tasks in the background and move on. But it's easy to lose track of them, especially if, like me, you typically have over 300 windows opened.

I'm not going to discuss here how having a good task manager allows you to keep track of things, make you multitasking and let you carry out many jobs at the same time, as would a scheduler. I'm more interested here about how to make sure you're notified when a task you started hours or days ago finishes. And it's not by mail.

1 Notifying Execution End

The idea is to ring the bell after a long-running command exits. All it takes it to write:

% long-running-command; echo -e \\a

If you already have long-running-command running and forgot to type echo -e \\a, you can still do so, even if there's a lot of output printed out. Make sure it's a command that doesn't expect anything from the TTY, e.g. prompts for information. Things will look like this:

% long-running-command
blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah
blah blah blah blah blah blah blah blah blah blah blah echo -e \\a blah blah
blah blah blah blah blah blah

Soon enough, you'll want to define an alias along the lines of:

alias beep='echo -e \\a'

All you have to type then is things like long-running-command; beep. Clearly, you could have your shell run this as a post command every time a command ends. You need to ask yourself then if you really want to be notified for each command ending – long-running as well as short-running ones.

In fact, shells such as zsh have the REPORTTIME parameter which can be set to a number of seconds after which a command is considered long-running and the time spent will automatically be printed out after its completion. Maybe a bell could be rung only then too.

2 Urgency Hints

Ringing a bell for long-running commands is not ideal or useful per se, and it will make your computer annoyingly beepy. To make this bell desirable, we need to:

By making the bell visual, I don't mean to turn on flashing of windows to replace beeping. I mean to set a so-called urgency hint for the window manager to do something with it. Urgency hinting is a useful X11 feature which allows windows to express urgency to the window manager. The window manager can in turn translate this urgency into e.g. turning a window red to draw attention, or raise a notification more centrally.

Urgency hint
In this example, while I have my focus in the lower left window. The urgency hint has been raised in the top right window and it turned red, telling me that something interesting happened in it, namely the sleep command ending. Note that my focus wasn't stolen.

Not all X11 programs, i.e. not all terminal emulators can emit urgency hints and not all window managers will receive them. But I've been using them flawlessly with rxvt-unicode and I know that window managers such as i3, xmonad and Notion understand them very well.

So you need to have your terminal set the urgency hint upon bell and with rxvt-unicode this is done by adding this line to your ~/.Xresources, before running xrdb ~/.Xresources and restarting any rxvt-unicode process:

URxvt.urgentOnBell: True

Notion, as well as some other window managers, will already do something useful with the window that set the urgency hint. Notion will turn it red. If it's not in view, it will light up a notification in the corner of the screen with the name of the window. And the window list will keep the urgent windows at its top so you can immediately jump to these.

3 Watching Output for Change

In some cases, you don't want to be notified when a command exits but when it prints out something or, more generally-speaking, when something changes in the terminal. This can apply to e.g. tail -fs, interactive ncurses programs or watch. In fact, watch can be used in a variety of cases, for instance when waiting for a particular program to print some specific output. Here I'm using watch to periodically run awk which will print the number of bytes written by process 24902 only if it's more than 100 MB:

% watch -t 'awk "/write_bytes/ && \$2 > 100000000" /proc/24902/io'

Another simpler example I'm going to discuss here is using watch to periodically run ls piped into grep when I expect file foo to appear soon:

% watch -t 'ls | grep foo'

I could have done this with a loop, but I need to take care of sleeping myself:

% while true; do ls | grep foo; sleep 1; done

Of course, there is nothing there that will draw my attention, nothing emitting the urgency hint, nothing ringing the bell. I could of course write a slightly more sophisticated command line and echo \a only if grep exits 0.

% watch -t 'ls | grep foo && echo -e \\a'

What I've chosen to do instead is to write a Perl extension to rxvt-unicode to ring the bell whenever a line is added (or otherwise changed):

sub add_lines { shift->scr_bell; () }

Of course, I don't want this all the time. In fact, I've got a keyboard shortcut to enable or disable this depending on whether or not I want to be notified of changes in a given window. I've even got a small, overlaid message telling me whether this is enabled on a per-window basis. More on this in my rxvt-unicode Perl Extensions story.

Monitored terminal
Any line added or otherwise changed in this window would trigger the add_lines Perl subroutine which will ring a bell, thus setting the urgency hint and having the window manager turning it red.

The advantage of this approach is that it also works with ncurses-like programs, e.g. mutt, and not just non-interactive programs where I can add echo -e \\as where I want.

4 Sleeping Before Starting

Not directly related to being notified, so much than it is about not forgetting to run commands later, I like to run sleep before running some command which I want to be run only later. I could use at, of course, but I want to keep the command line specific and attached to a terminal.

A typical example is when I've git pushed some Puppet manifest changes and want to try them out on a given node. Knowing that the Puppet master may not have received the changes yet, I programmatically sleep for some time before running Puppet. And then I want to be notified when the Puppet run finishes:

% sleep 30s; puppet agent -tv; echo -e \\a

During all the time this command line is executed, I can do something else but still be notified when it's all over.

5 References