#include <linux/printk.h>
#include <linux/version.h>
-static struct {
- struct completion crank_comp;
- struct completion flywheel_comp;
-} machine;
+static struct completion crank_comp;
+static struct completion flywheel_comp;
static int machine_crank_thread(void *arg)
{
pr_info("Turn the crank\n");
- complete_all(&machine.crank_comp);
+ complete_all(&crank_comp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
- kthread_complete_and_exit(&machine.crank_comp, 0);
+ kthread_complete_and_exit(&crank_comp, 0);
#else
- complete_and_exit(&machine.crank_comp, 0);
+ complete_and_exit(&crank_comp, 0);
#endif
}
static int machine_flywheel_spinup_thread(void *arg)
{
- wait_for_completion(&machine.crank_comp);
+ wait_for_completion(&crank_comp);
pr_info("Flywheel spins up\n");
- complete_all(&machine.flywheel_comp);
+ complete_all(&flywheel_comp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
- kthread_complete_and_exit(&machine.flywheel_comp, 0);
+ kthread_complete_and_exit(&flywheel_comp, 0);
#else
- complete_and_exit(&machine.flywheel_comp, 0);
+ complete_and_exit(&flywheel_comp, 0);
#endif
}
pr_info("completions example\n");
- init_completion(&machine.crank_comp);
- init_completion(&machine.flywheel_comp);
+ init_completion(&crank_comp);
+ init_completion(&flywheel_comp);
crank_thread = kthread_create(machine_crank_thread, NULL, "KThread Crank");
if (IS_ERR(crank_thread))
static void __exit completions_exit(void)
{
- wait_for_completion(&machine.crank_comp);
- wait_for_completion(&machine.flywheel_comp);
+ wait_for_completion(&crank_comp);
+ wait_for_completion(&flywheel_comp);
pr_info("completions exit\n");
}
Sometimes one thing should happen before another within a module having multiple threads.
Rather than using \sh|/bin/sleep| commands, the kernel has another way to do this which allows timeouts or interrupts to also happen.
-In the following example two threads are started, but one needs to start before another.
+Completions as code synchronization mechanism have three main parts, initialization of struct completion synchronization object, the waiting or barrier part through \cpp|wait_for_completion()|, and the signalling side through a call to \cpp|complete()|.
-\samplec{examples/completions.c}
-
-The \cpp|machine| structure stores the completion states for the two threads.
+In the subsequent example, two threads are initiated: crank and flywheel.
+It is imperative that the crank thread starts before the flywheel thread.
+A completion state is established for each of these threads, with a distinct completion defined for both the crank and flywheel threads.
At the exit point of each thread the respective completion state is updated, and \cpp|wait_for_completion| is used by the flywheel thread to ensure that it does not begin prematurely.
+The crank thread uses the \cpp|complete_all()| function to update the completion, which lets the flywheel thread continue.
-So even though \cpp|flywheel_thread| is started first you should notice if you load this module and run \sh|dmesg| that turning the crank always happens first because the flywheel thread waits for it to complete.
+So even though \cpp|flywheel_thread| is started first you should notice when you load this module and run \sh|dmesg|, that turning the crank always happens first because the flywheel thread waits for the crank thread to complete.
-There are other variations upon the \cpp|wait_for_completion| function, which include timeouts or being interrupted, but this basic mechanism is enough for many common situations without adding a lot of complexity.
+There are other variations of the \cpp|wait_for_completion| function, which include timeouts or being interrupted, but this basic mechanism is enough for many common situations without adding a lot of complexity.
+
+\samplec{examples/completions.c}
\section{Avoiding Collisions and Deadlocks}
\label{sec:synchronization}