Over the years, developers seem to have forgotten that Unix is a Programming Environment and not just something you must install to access your web server. Unix is a powerful multi-user system with numerous tools that have stood the test of time and, with the advent of systemd, we have a new tool that can easily model state machines. Now, we can address a new class of problems using Unix alone.
In this blog post series, we’ll solve the classic “Dining Philosophers” concurrency problem with some of the forgotten mechanisms and tools within the Unix Programming Environment, some advanced capabilities of systemd and a sprinkling of shell scripts to glue it together - all without a single deadlock.
Let’s start by building a simple state machine that moves between the Thinking
,
Hungry
, and Eating
states using the user-level systemd facilities.
In these examples, we’ll be using Ubuntu on Brightbox, but any modern distro running systemd running anywhere should work.
Create a server and log in with the default ubuntu
user:
$ ssh ubuntu@public.srv-hgy08.gb1.brightbox.com
Welcome to Ubuntu 23.10 (GNU/Linux 6.5.0-10-generic x86_64)
...
When you log in, systemd automatically starts a user instance to manage user-level units. You can view this via systemctl:
ubuntu@srv-hgy08:~$ systemctl --user status
â—Ź srv-hgy08
State: running
Units: 171 loaded (incl. loaded aliases)
Jobs: 0 queued
Failed: 0 units
Since: Mon 2023-11-20 09:41:56 UTC; 1min 19s ago
systemd: 253.5-1ubuntu6
CGroup: /user.slice/user-1000.slice/user@1000.service
└─init.scope
├─857 /lib/systemd/systemd --user
└─858 "(sd-pam)"
We can model the three states of the dining philosophers problem using systemd target units. We can make the targets mutually exclusive by stating that they conflict with each other:
# thinking.target
[Unit]
Description=Thinking...
Conflicts=eating.target hungry.target
This means only one of the targets can remain active at any point in time. For a target to transition to another, we need a timer unit.
# thinking.timer
[Unit]
Description=Thinking Period
PartOf=thinking.target
[Timer]
Unit=hungry.target
RandomizedDelaySec=60
OnActiveSec=0
AccuracySec=1us
RemainAfterElapse=false
We make the timer unit part of the target so that it stops at the same
time as the target. This combination of RandomizedDelay
, OnActive
,
and Accuracy
distributes the transition time from 0 to 60 seconds.
We want to know what the philosopher is thinking about, which we can simulate by running a service to call a script.
# contemplation.service
[Unit]
Description=Contemplation
PartOf=thinking.target
Before=hungry.target
[Service]
Type=oneshot
SyslogFacility=local0
ExecStart=/usr/local/bin/contemplation
We log the script’s output to a different syslog facility, which allows us
to find them easily in the log later. A Before
restriction ensures that the
philosopher logs their thoughts before dealing with their hunger.
Create the contemplation script to output an appropriate thought:
#!/bin/sh
# /usr/local/bin/contemplation
set -e
default_thought="the enigmatic nature of reality:
the existential grasp for an elusive fork, the categorical imperative
of simulated existence, and the irony in questioning the true essence
of the held object."
{
printf '%s' 'Currently contemplating '
printf '%s\n' "${default_thought}"
} | fmt
exit 0
Now, we update the target to pull in these new units:
# thinking.target
[Unit]
Description=Thinking...
Conflicts=eating.target hungry.target
Wants=thinking.timer contemplation.service
And repeat the process for the eating and hungry targets.
To start the simulation, put the files in $HOME/.config/systemd/user
and activate the thinking
target:
$ systemctl --user start thinking.target
Look in the journal to see the simulation in progress:
$ journalctl -f
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Started thinking.timer - Thinking Period.
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Stopped target eating.target - Eating....
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Stopped eating.timer - Eating Period.
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Starting contemplation.service - Contemplation...
Nov 20 10:49:15 srv-hgy08 contemplation[2163]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:49:15 srv-hgy08 contemplation[2163]: grasp for an elusive fork, the categorical imperative of simulated
Nov 20 10:49:15 srv-hgy08 contemplation[2163]: existence, and the irony in questioning the true essence of the held
Nov 20 10:49:15 srv-hgy08 contemplation[2163]: object.
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Finished contemplation.service - Contemplation.
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Reached target thinking.target - Thinking....
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Started hungry.timer - Hungry Period.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Stopped target thinking.target - Thinking....
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Stopped thinking.timer - Thinking Period.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Starting hunger.service - Hunger...
Nov 20 10:49:17 srv-hgy08 hunger[2165]: Getting hungry...
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Finished hunger.service - Hunger.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Reached target hungry.target - Hungry....
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Started eating.timer - Eating Period.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Stopped target hungry.target - Hungry....
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Stopped hungry.timer - Hungry Period.
Nov 20 10:49:17 srv-hgy08 consumption[2166]: Eating...
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Starting eating-food.service - Eating Food...
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Finished eating-food.service - Eating Food.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Reached target eating.target - Eating....
We can filter out the state noise by looking just for the output from our scripts:
$ journalctl -f --facility local0
Nov 20 10:50:44 srv-hgy08 contemplation[2176]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:50:44 srv-hgy08 contemplation[2176]: grasp for an elusive fork, the categorical imperative of simulated
Nov 20 10:50:44 srv-hgy08 contemplation[2176]: existence, and the irony in questioning the true essence of the held
Nov 20 10:50:44 srv-hgy08 contemplation[2176]: object.
Nov 20 10:51:14 srv-hgy08 hunger[2177]: Getting hungry...
Nov 20 10:51:15 srv-hgy08 consumption[2178]: Eating...
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: grasp for an elusive fork, the categorical imperative of simulated
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: existence, and the irony in questioning the true essence of the held
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: object.
Or, if we want to see just one particular script, we can filter by identifier:
$ journalctl -f --identifier=contemplation
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: existence, and the irony in questioning the true essence of the held
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: object.
Nov 20 10:52:11 srv-hgy08 contemplation[2192]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:52:11 srv-hgy08 contemplation[2192]: grasp for an elusive fork, the categorical imperative of simulated
Nov 20 10:52:11 srv-hgy08 contemplation[2192]: existence, and the irony in questioning the true essence of the held
Nov 20 10:52:11 srv-hgy08 contemplation[2192]: object.
Nov 20 10:52:37 srv-hgy08 contemplation[2197]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:52:37 srv-hgy08 contemplation[2197]: grasp for an elusive fork, the categorical imperative of simulated
Nov 20 10:52:37 srv-hgy08 contemplation[2197]: existence, and the irony in questioning the true essence of the held
Nov 20 10:52:37 srv-hgy08 contemplation[2197]: object.
To stop the simulation, stop the targets:
$ systemctl --user stop thinking.target eating.target hungry.target
or log out.
In this blog, we’ve created a simple state machine with systemd that moves neatly between states over time and logs its activity in the journal. It’s hardly a fully modelled philosopher, but it’s a good start.
In the next part, we’ll take this simple model and expand it to cover multiple philosophers, introducing the shared dining room where their contemplations take place.