1  /*
   2   * \brief  Semaphore
   3   * \author Norman Feske
   4   * \author Christian Prochaska
   5   * \date   2006-09-22
   6   */

   7  
   8  /*
   9   * Copyright (C) 2006-2013 Genode Labs GmbH
  10   *
  11   * This file is part of the Genode OS framework, which is distributed
  12   * under the terms of the GNU General Public License version 2.
  13   */

  14  
  15  #ifndef _INCLUDE__BASE__SEMAPHORE_H_
  16  #define _INCLUDE__BASE__SEMAPHORE_H_
  17  
  18  #include <base/lock.h>
  19  #include <util/fifo.h>
  20  
  21  namespace Genode { class Semaphore; }
  22  
  23  
  24  class Genode::Semaphore
  25  {
  26     protected:
  27  
  28        int  _cnt;
  29        Lock _meta_lock;
  30  
  31        struct Element : Fifo<Element>::Element
  32        {
  33           Lock lock { Lock::LOCKED };
  34  
  35           void block()   { lock.lock();   }
  36           void wake_up() { lock.unlock(); }

  37        }
;

  38  
  39        Fifo<Element> _queue;

  40  
  41     public:
  42  
  43        /**
  44         * Constructor
  45         *
  46         * \param n  initial counter value of the semphore
  47         */

  48        Semaphore(int = 0) : _cnt(n) { }

  49  
  50        ~Semaphore()
  51        {
  52           /* synchronize destruction with unfinished `up()` */
  53           try { _meta_lock.lock(); } catch (...) { }

  54        }

  55  
  56        /**
  57         * Increment semphore counter
  58         *
  59         * This method may wake up another thread that currently blocks on
  60         * a `down` call at the same semaphore.
  61         */

  62        void up()
  63        {
  64           Element * element = nullptr;
  65  
  66           {
  67              Lock::Guard lock_guard(_meta_lock);
  68  
  69              if (++_cnt > 0)
  70                 return;

  71  
  72              /*
  73               * Remove element from queue and wake up the corresponding
  74               * blocking thread
  75               */

  76              element = _queue.dequeue();

  77           }

  78  
  79           /* do not hold the lock while unblocking a waiting thread */
  80           if (element) element->wake_up();

  81        }

  82  
  83        /**
  84         * Decrement semaphore counter, block if the counter reaches zero
  85         */

  86        void down()
  87        {
  88           _meta_lock.lock();
  89  
  90           if (--_cnt < 0) {
  91  
  92              /*
  93               * Create semaphore queue element representing the thread
  94               * in the wait queue.
  95               */

  96              Element queue_element;
  97              _queue.enqueue(&queue_element);
  98              _meta_lock.unlock();
  99  
 100              /*
 101               * The thread is going to block on a local lock now,
 102               * waiting for getting waked from another thread
 103               * calling `up()`
 104               * */

 105              queue_element.block();

 106  
 107           }
 else {
 108              _meta_lock.unlock();
 109           }

 110        }

 111  
 112        /**
 113         * Return current semaphore counter
 114         */

 115        int cnt() { return _cnt; }

 116  }
;

 117  
 118  #endif /* _INCLUDE__BASE__SEMAPHORE_H_ */