<?xml version="1.0" encoding="UTF-8"?>
<chapter id="sync">
  <?dbhtml filename="sync.html"?>

  <title>Mutual exclusion and synchronization</title>

  <section>
    <title>Introduction</title>

    <para>The HelenOS operating system is designed to make use of parallelism
    offered by hardware and to exploit concurrency of both the kernel and
    userspace tasks. This is achieved through multiprocessor support and
    several levels of multiprogramming (i.e. multitasking, multithreading and
    through userspace pseudo threads). However, such a highly concurrent
    environment needs safe and efficient ways to handle mutual exclusion and
    synchronization of many execution flows.</para>
  </section>

  <section>
    <title>Active kernel primitives</title>

    <section>
      <title>Spinlocks</title>

      <para>The basic mutual exclusion primitive is the spinlock. Spinlock
      implements busy waiting for an availability of a memory lock (i.e.
      simple variable) in a multiprocessor-safe manner. This safety is
      achieved through the use of a specialized, architecture-dependent,
      atomic test-and-set operation which either locks the spinlock (i.e. sets
      the variable) or, provided that it is already locked, leaves it
      unaltered. In any case, the test-and-set operation returns a value, thus
      signalling either success (i.e. zero return value) or failure (i.e.
      non-zero value) in acquiring the lock. Note that this makes the
      fundamental difference between the naive algorithm that doesn't use the
      atomic operation and the spinlock algortihm. While the naive algorithm
      is prone to race conditions on SMP configuratinos and thus is completely
      SMP-unsafe, the spinlock algorithm eliminates the possibility of race
      conditions and is suitable for mutual exclusion use.</para>

      <para></para>

      <para>The semantics of the test-and-set operation is that the spinlock
      remains unavailable until this operation called on the respective
      spinlock returns zero. HelenOS builds two functions on top of
      test-and-set operation. The first is the unconditional attempt to
      acquire the spinlock and is called <emphasis>spinlock_lock</emphasis>.
      It simply loops until test-and-set returns zero. The other operation,
      <emphasis>spinlock_trylock</emphasis>, is the conditional lock operation
      and calls the test-and-set only once to find out wheter it managed to
      acquire the spinlock or not. The conditional operation is useful in
      situations in which an algorithm cannot acquire more spinlocks in the
      proper order and a deadlock cannot be avoided. In such a case, the
      algorithm would detect the danger and instead of possibly deadlocking
      the system it would simply release some spinlocks it already holds and
      retry the whole operation with the hope that it will succeed next time.
      The unlock operation, <emphasis>spinlock_unlock</emphasis>, is quite
      easy - it merely clears the spinlock variable.</para>

      <para></para>

      <para>Nevertheless, there is a special issue related to hardware
      optimizations that modern processors implement. Particularily
      problematic is the out-of-order execution of instructions within the
      critical section protected by a spinlock. The processors are always
      self-consistent so that they can carry out speculatively executed
      instructions in the right order with regard to dependencies among those
      instructions. However, the dependency between instructions inside the
      critical section and those that implement locking and unlocking of the
      respective spinlock is not implicit on some processor architectures and
      the processor needs to be explicitly told about each occurrence of such
      a dependency. Therefore, HelenOS adds architecture-specific hooks to all
      <emphasis>spinlock_lock</emphasis>,
      <emphasis>spinlock_trylock</emphasis> and
      <emphasis>spinlock_unlock</emphasis> to prevent the instructions inside
      the critical section from bleeding out. On some architectures, these
      hooks can be a no-op because the dependencies are implicitly there
      because of the special properties of locking and unlocking instructions.
      However, other architectures need to instrument these hooks with
      different memory barriers, depending on what operations can bleed
      out.</para>

      <para></para>

      <para>Spinlocks have one significant drawback: when held for longer time
      periods, they harm both parallelism and concurrency. Processor executing
      <emphasis>spinlock_lock</emphasis> does not do any fruitful work and is
      effectively halted until it can grab the lock and proceed. Similarily,
      other threads cannot execute on the processor that holds the spinlock
      because the kernel disables preemption on that processor when a spinlock
      is held. The reason behind disabling preemption is priority inversion
      problem avoidance. For the same reason, threads are strongly discouraged
      from sleeping when they hold a spinlock.</para>

      <para></para>

      <para>To summarize, spinlocks represent very simple and essential mutual
      exclusion primitive for SMP systems. On the other hand, spinlocks scale
      poorly because of the active loop they are based on. Therefore,
      spinlocks are used in HelenOS only for a short-time mutual exclusion and
      in cases where the mutual exclusion is required out of thread context.
      Lastly, spinlocks are used in the construction of passive
      synchronization primitives.</para>
    </section>
  </section>

  <section>
    <title>Passive kernel synchronization</title>

    <section>
      <title>Mutexes</title>

      <para>Mutex explanations</para>
    </section>

    <section>
      <title>Semaphores</title>

      <para>Semaphore explanations</para>
    </section>

    <section>
      <title>Read/Write Locks</title>

      <para>RWLocks explanation</para>
    </section>

    <section>
      <title>Wait queues</title>

      <para>Wait queue explanation</para>
    </section>

    <section>
      <title>Condition variables</title>

      <para>Condvars explanation</para>
    </section>
  </section>

  <section>
    <title>Userspace synchronization</title>

    <section>
      <title>Futexes</title>

      <para></para>
    </section>
  </section>
</chapter>