/* This is the header file of the Parma Watchdog Library.
   Copyright (C) 2002-2004 Roberto Bagnara <bagnara@cs.unipr.it>

This file is part of the Parma Watchdog Library (PWL).

The PWL is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.

The PWL is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.

For the most up-to-date information see the CS@Parma software
site: http://www.cs.unipr.it/Software/ . */

#ifndef PWL_pwl_hh
#define PWL_pwl_hh 1

#ifdef NDEBUG
#define PWL_SAVE_NDEBUG 1
#undef NDEBUG
#endif

/* config.h.  Generated by configure.  */
/* config.h.in.  Generated from configure.ac by autoheader.  */

/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1

/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1

/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1

/* Define to 1 if you have the `setitimer' function. */
#define HAVE_SETITIMER 1

/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1

/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1

/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1

/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1

/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1

/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1

/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1

/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1

/* Assertions are disabled when this is defined */
#define NDEBUG 1

/* Name of package */
#define PACKAGE "watchdog"

/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "bagnara@cs.unipr.it"

/* Define to the full name of this package. */
#define PACKAGE_NAME "the Parma Watchdog Library"

/* Define to the full name and version of this package. */
#define PACKAGE_STRING "the Parma Watchdog Library 0.3"

/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "watchdog"

/* Define to the version of this package. */
#define PACKAGE_VERSION "0.3"

/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1

/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1

/* Version number of package */
#define VERSION "0.3"

/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */

#if __GNUC__ < 3
// Get around a bug in GCC versions prior to 3 whereby the <cassert>
// header is wrongly guarded against multiple inclusion.
#ifdef __CASSERT__
#undef __CASSERT__
#endif

#endif

#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif

#include <cassert>
#include <stdexcept>
namespace Parma_Watchdog_Library {

class Doubly_Linked_Object;

}
namespace Parma_Watchdog_Library {

template <typename T>
class EList;

}
namespace Parma_Watchdog_Library {

template <typename T>
class EList_Iterator;

}
namespace Parma_Watchdog_Library {

class Handler;

template <typename Flag_Base, typename Flag>
class Handler_Flag;

class Handler_Function;

}
namespace Parma_Watchdog_Library {

class Pending_Element;

}
namespace Parma_Watchdog_Library {

class Pending_List;

}
namespace Parma_Watchdog_Library {

class Time;

}
namespace Parma_Watchdog_Library {
  class Watchdog;
  class Flag;
  class Init;
}
class Parma_Watchdog_Library::Handler {
public:

  virtual void act() const = 0;
};
template <typename Flag_Base, typename Flag>
class Parma_Watchdog_Library::Handler_Flag : virtual public Handler {
public:

  Handler_Flag(const Flag_Base* volatile& holder, Flag& flag);



  void act() const;

private:

  const Flag_Base* volatile& h;
  Flag& f;
};


class Parma_Watchdog_Library::Handler_Function : virtual public Handler {
public:

  Handler_Function(void (*function)());


  void act() const;

private:

  void (*f)();
};
namespace Parma_Watchdog_Library {

template <typename Flag_Base, typename Flag>
Handler_Flag<Flag_Base, Flag>::Handler_Flag(const Flag_Base* volatile& holder,
         Flag& flag)
  : h(holder), f(flag) {
}

template <typename Flag_Base, typename Flag>
void
Handler_Flag<Flag_Base, Flag>::act() const {
  if (h == 0 || static_cast<const Flag&>(*h).priority() < f.priority())
    h = &f;
}

inline
Handler_Function::Handler_Function(void (*function)())
  : f(function) {
}

inline void
Handler_Function::act() const {
  (*f)();
}

}
namespace Parma_Watchdog_Library {


bool operator==(const Time& x, const Time& y);


bool operator!=(const Time& x, const Time& y);


bool operator<(const Time& x, const Time& y);




bool operator<=(const Time& x, const Time& y);


bool operator>(const Time& x, const Time& y);




bool operator>=(const Time& x, const Time& y);


Time operator+(const Time& x, const Time& y);




Time operator-(const Time& x, const Time& y);

}


class Parma_Watchdog_Library::Time {
public:

  Time();


  explicit Time(unsigned long hundredths_of_a_second);


  Time(unsigned long s, unsigned long m);




  unsigned long seconds() const;




  unsigned long microseconds() const;


  Time& operator+=(const Time& y);




  Time& operator-=(const Time& y);


  bool OK() const;

private:

  unsigned long secs;


  unsigned long microsecs;
};
namespace Parma_Watchdog_Library {

inline
Time::Time()
  : secs(0), microsecs(0) {
  assert(OK());
}

inline
Time::Time(unsigned long hundredths_of_a_second)
  : secs(hundredths_of_a_second / 100),
    microsecs((hundredths_of_a_second % 100) * 10000) {
  assert(OK());
}

inline
Time::Time(unsigned long s, unsigned long m)
  : secs(s),
    microsecs(m) {
  if (microsecs >= 1000000) {
    secs += microsecs / 1000000;
    microsecs %= 1000000;
  }
  assert(OK());
}

inline unsigned long
Time::seconds() const {
  return secs;
}

inline unsigned long
Time::microseconds() const {
  return microsecs;
}

inline Time&
Time::operator+=(const Time& y) {
  unsigned long r_secs = secs + y.secs;
  unsigned long r_microsecs = microsecs + y.microsecs;
  if (r_microsecs >= 1000000) {
    ++r_secs;
    r_microsecs %= 1000000;
  }
  secs = r_secs;
  microsecs = r_secs;
  assert(OK());
  return *this;
}

inline Time&
Time::operator-=(const Time& y) {
  long r_secs = secs - y.secs;
  long r_microsecs = microsecs - y.microsecs;
  if (r_microsecs < 0) {
    --r_secs;
    r_microsecs += 1000000;
  }
  if (r_secs < 0)
    r_secs = r_microsecs = 0;
  secs = r_secs;
  microsecs = r_microsecs;
  assert(OK());
  return *this;
}

inline Time
operator+(const Time& x, const Time& y) {
  Time z = x;
  z += y;
  return z;
}

inline Time
operator-(const Time& x, const Time& y) {
  Time z = x;
  z -= y;
  return z;
}

inline bool
operator==(const Time& x, const Time& y) {
  assert(x.OK() && y.OK());
  return x.seconds() == y.seconds() && y.microseconds() == y.microseconds();
}

inline bool
operator!=(const Time& x, const Time& y) {
  assert(x.OK() && y.OK());
  return !(x == y);
}

inline bool
operator<(const Time& x, const Time& y) {
  assert(x.OK() && y.OK());
  return x.seconds() < y.seconds()
    || (x.seconds() == y.seconds() && x.microseconds() < y.microseconds());
}

inline bool
operator<=(const Time& x, const Time& y) {
  return x < y || x == y;
}

inline bool
operator>(const Time& x, const Time& y) {
  return y < x;
}

inline bool
operator>=(const Time& x, const Time& y) {
  return y <= x;
}

}
class Parma_Watchdog_Library::Doubly_Linked_Object {
public:

  Doubly_Linked_Object();


  Doubly_Linked_Object(Doubly_Linked_Object* f, Doubly_Linked_Object* b);


  void insert_before(Doubly_Linked_Object& y);


  void insert_after(Doubly_Linked_Object& y);


  Doubly_Linked_Object* erase();


  ~Doubly_Linked_Object();

private:

  Doubly_Linked_Object* next;


  Doubly_Linked_Object* prev;

  template <typename T> friend class EList;
  template <typename T> friend class EList_Iterator;
};
namespace Parma_Watchdog_Library {

inline
Doubly_Linked_Object::Doubly_Linked_Object() {
}

inline
Doubly_Linked_Object::Doubly_Linked_Object(Doubly_Linked_Object* f,
        Doubly_Linked_Object* b)
  : next(f),
    prev(b) {
}

inline void
Doubly_Linked_Object::insert_before(Doubly_Linked_Object& y) {
  y.next = this;
  y.prev = prev;
  prev->next = &y;
  prev = &y;
}

inline void
Doubly_Linked_Object::insert_after(Doubly_Linked_Object& y) {
  y.next = next;
  y.prev = this;
  next->prev = &y;
  next = &y;
}

inline Doubly_Linked_Object*
Doubly_Linked_Object::erase() {
  next->prev = prev;
  prev->next = next;
  return next;
}

inline
Doubly_Linked_Object::~Doubly_Linked_Object() {
  erase();
}

}
namespace Parma_Watchdog_Library {


template <typename T>
bool operator==(const EList_Iterator<T>& x, const EList_Iterator<T>& y);


template <typename T>
bool operator!=(const EList_Iterator<T>& x, const EList_Iterator<T>& y);

}


template <typename T>
class Parma_Watchdog_Library::EList_Iterator {
public:

  EList_Iterator();


  explicit EList_Iterator(Doubly_Linked_Object* p);


  EList_Iterator& operator=(Doubly_Linked_Object* p);


  T* operator->();


  T& operator*();


  EList_Iterator& operator++();


  EList_Iterator operator++(int);


  EList_Iterator& operator--();


  EList_Iterator operator--(int);

private:

  Doubly_Linked_Object* ptr;

  friend bool operator==<T>(const EList_Iterator& x, const EList_Iterator& y);

  friend bool operator!=<T>(const EList_Iterator& x, const EList_Iterator& y);
};
namespace Parma_Watchdog_Library {

template <typename T>
inline
EList_Iterator<T>::EList_Iterator() {
}

template <typename T>
inline
EList_Iterator<T>::EList_Iterator(Doubly_Linked_Object* p)
  : ptr(p) {
}

template <typename T>
inline EList_Iterator<T>&
EList_Iterator<T>::operator=(Doubly_Linked_Object* p) {
  ptr = p;
  return *this;
}

template <typename T>
inline T*
EList_Iterator<T>::operator->() {
  return static_cast<T*>(ptr);
}

template <typename T>
inline T&
EList_Iterator<T>::operator*() {
  return *operator->();
}

template <typename T>
inline EList_Iterator<T>&
EList_Iterator<T>::operator++() {
  ptr = ptr->next;
  return *this;
}

template <typename T>
inline EList_Iterator<T>
EList_Iterator<T>::operator++(int) {
  EList_Iterator tmp = *this;
  ++*this;
  return tmp;
}

template <typename T>
inline EList_Iterator<T>&
EList_Iterator<T>::operator--() {
  ptr = ptr->prev;
  return *this;
}

template <typename T>
inline EList_Iterator<T>
EList_Iterator<T>::operator--(int) {
  EList_Iterator tmp = *this;
  --*this;
  return tmp;
}

template <typename T>
inline bool
operator==(const EList_Iterator<T>& x, const EList_Iterator<T>& y) {
  return x.ptr == y.ptr;
}

template <typename T>
inline bool
operator!=(const EList_Iterator<T>& x, const EList_Iterator<T>& y) {
  return x.ptr != y.ptr;
}

}
template <typename T>
class Parma_Watchdog_Library::EList : private Doubly_Linked_Object {
public:

  typedef EList_Iterator<const T> Const_Iterator;


  typedef EList_Iterator<T> Iterator;


  EList();


  ~EList();


  void push_front(T& obj);


  void push_back(T& obj);




  Iterator insert(Iterator position, T& obj);




  Iterator erase(Iterator position);


  bool empty() const;


  Iterator begin();


  Iterator end();


  Const_Iterator begin() const;


  Const_Iterator end() const;


  bool OK() const;
};
namespace Parma_Watchdog_Library {

template <typename T>
inline
EList<T>::EList()
  : Doubly_Linked_Object(this, this) {
}

template <typename T>
inline void
EList<T>::push_front(T& obj) {
  next->insert_before(obj);
}

template <typename T>
inline void
EList<T>::push_back(T& obj) {
  prev->insert_after(obj);
}

template <typename T>
inline typename EList<T>::Iterator
EList<T>::insert(Iterator position, T& obj) {
  position->insert_before(obj);
  return Iterator(&obj);
}

template <typename T>
inline typename EList<T>::Iterator
EList<T>::begin() {
  return Iterator(next);
}

template <typename T>
inline typename EList<T>::Iterator
EList<T>::end() {
  return Iterator(this);
}

template <typename T>
inline typename EList<T>::Const_Iterator
EList<T>::begin() const {
  return Const_Iterator(next);
}

template <typename T>
inline typename EList<T>::Const_Iterator
EList<T>::end() const {
  return Const_Iterator(const_cast<EList<T>*>(this));
}

template <typename T>
inline bool
EList<T>::empty() const {
  return begin() == end();
}

template <typename T>
inline typename EList<T>::Iterator
EList<T>::erase(Iterator position) {
  assert(!empty());
  return Iterator(position->erase());
}

template <typename T>
inline
EList<T>::~EList() {

  for (Iterator i = begin(), lend = end(), next; i != lend; i = next) {
    next = erase(i);
    delete &*i;
  }
}

template <typename T>
inline bool
EList<T>::OK() const {
  for (Const_Iterator i = begin(), lend = end(); i != lend; ++i)
    if (!i->OK())
      return false;

  return true;
}

}
class Parma_Watchdog_Library::Pending_Element : public Doubly_Linked_Object {
public:

  Pending_Element(const Time& deadline,
    const Handler& handler,
    bool& expired_flag);


  void assign(const Time& deadline,
       const Handler& handler,
       bool& expired_flag);


  const Time& deadline() const;


  const Handler& handler() const;


  bool& expired_flag() const;


  bool OK() const;

private:

  Time d;


  const Handler* p_h;


  bool* p_f;
};
namespace Parma_Watchdog_Library {

inline
Pending_Element::Pending_Element(const Time& deadline,
     const Handler& handler,
     bool& expired_flag)
  : d(deadline), p_h(&handler), p_f(&expired_flag) {
  assert(OK());
}

inline void
Pending_Element::assign(const Time& deadline,
   const Handler& handler,
   bool& expired_flag) {
  d = deadline;
  p_h = &handler;
  p_f = &expired_flag;
  assert(OK());
}

inline const Time&
Pending_Element::deadline() const {
  return d;
}

inline const Handler&
Pending_Element::handler() const {
  return *p_h;
}

inline bool&
Pending_Element::expired_flag() const {
  return *p_f;
}

}
class Parma_Watchdog_Library::Pending_List {
public:

  typedef EList<Pending_Element>::Iterator Iterator;


  Pending_List();


  ~Pending_List();


  Iterator insert(const Time& deadline,
    const Handler& handler,
    bool& expired_flag);




  Iterator erase(Iterator position);


  bool empty() const;


  Iterator begin();


  Iterator end();


  bool OK() const;

private:
  EList<Pending_Element> active_list;
  EList<Pending_Element> free_list;
};
namespace Parma_Watchdog_Library {

inline
Pending_List::Pending_List()
  : active_list(),
    free_list() {
  assert(OK());
}

inline
Pending_List::~Pending_List() {
}

inline Pending_List::Iterator
Pending_List::begin() {
  return active_list.begin();
}

inline Pending_List::Iterator
Pending_List::end() {
  return active_list.end();
}

inline bool
Pending_List::empty() const {
  return active_list.empty();
}

inline Pending_List::Iterator
Pending_List::erase(Iterator position) {
  assert(!empty());
  Iterator next = active_list.erase(position);
  free_list.push_back(*position);
  assert(OK());
  return next;
}

}
namespace Parma_Watchdog_Library {


extern "C" void PWL_handle_timeout(int signum);


class Watchdog {
public:
  template <typename Flag_Base, typename Flag>
  Watchdog(int units, const Flag_Base* volatile& holder, Flag& flag);

  Watchdog(int units, void (*function)());
  ~Watchdog();


private:
  friend class Init;
  static void initialize();
  static void finalize();

  bool expired;
  const Handler& handler;
  Pending_List::Iterator pending_position;

private:

  Watchdog(const Watchdog&);
  Watchdog& operator=(const Watchdog&);


  static itimerval current_timer_status;


  static void get_timer(Time& time);


  static itimerval signal_once;


  static Time last_time_requested;


  static void set_timer(const Time& time);


  static void stop_timer();


  static void reschedule();


  static Time reschedule_time;


  static Time time_so_far;


  static Pending_List pending;


  static void handle_timeout(int);


  static Pending_List::Iterator new_watchdog_event(int units,
         const Handler& handler,
         bool& expired_flag);


  void remove_watchdog_event(Pending_List::Iterator position);


  static volatile bool alarm_clock_running;


  static volatile bool in_critical_section;

  friend void Parma_Watchdog_Library::PWL_handle_timeout(int signum);
};

class Init {
private:

  static unsigned int count;

public:

  Init();


  ~Init();
};

}


namespace {

Parma_Watchdog_Library::Init Parma_Watchdog_Library_initializer;

}
namespace Parma_Watchdog_Library {

inline void
Watchdog::reschedule() {
  set_timer(reschedule_time);
}

template <typename Flag_Base, typename Flag>
Watchdog::Watchdog(int units, const Flag_Base* volatile& holder, Flag& flag)
  : expired(false),
    handler(*new Handler_Flag<Flag_Base, Flag>(holder, flag)) {
  if (units <= 0)
    throw std::invalid_argument("Watchdog constructor called with a"
    " non-positive number of time units");
  in_critical_section = true;
  pending_position = new_watchdog_event(units, handler, expired);
  in_critical_section = false;
}

inline
Watchdog::Watchdog(int units, void (*function)())
  : expired(false), handler(*new Handler_Function(function)) {
  if (units <= 0)
    throw std::invalid_argument("Watchdog constructor called with a"
    " non-positive number of time units");
  in_critical_section = true;
  pending_position = new_watchdog_event(units, handler, expired);
  in_critical_section = false;
}

inline
Init::Init() {

  if (count++ == 0) {

    Watchdog::initialize();
  }
}

inline
Init::~Init() {

  if (--count == 0) {

    Watchdog::finalize();
  }
}

}

#ifdef PWL_SAVE_NDEBUG
#define NDEBUG 1
#undef PWL_SAVE_NDEBUG
#endif

#undef PACKAGE
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#undef VERSION

#endif
