// <condition_variable> -*- C++ -*-

// Copyright (C) 2008 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library 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, or (at your option)
// any later version.

// This library 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 library; see the file COPYING.  If not, write to
// the Free Software Foundation, 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301, USA.

// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.

/** @file condition_variable
 *  This is a Standard C++ Library header.
 */

#ifndef _GLIBCXX_CONDITION_VARIABLE
#define _GLIBCXX_CONDITION_VARIABLE 1

#pragma GCC system_header

#ifndef __GXX_EXPERIMENTAL_CXX0X__
# include <c++0x_warning.h>
#else

#include <chrono>
#include <mutex> // unique_lock

#if defined(_GLIBCXX_HAS_GTHREADS) && defined(_GLIBCXX_USE_C99_STDINT_TR1)

namespace std 
{
  /// condition_variable
  class condition_variable
  {
  public:
    typedef __gthread_cond_t* native_handle_type;

    condition_variable();
    ~condition_variable();

    condition_variable(const condition_variable&) = delete;
    condition_variable& operator=(const condition_variable&) = delete;

    void 
    notify_one();

    void 
    notify_all();

    void 
    wait(unique_lock<mutex>& __lock);

    template<typename _Predicate>
      void 
      wait(unique_lock<mutex>& __lock, _Predicate __p)
      {
	while (!__p())
	  wait(__lock);
      }
  
    template<typename _Clock, typename _Duration>
      bool 
      wait_until(unique_lock<mutex>& __lock, 
		 const chrono::time_point<_Clock, _Duration>& __atime)
      {
	chrono::time_point<_Clock, chrono::seconds>  __s =
          chrono::time_point_cast<chrono::seconds>(__atime);

	chrono::nanoseconds __ns =
          chrono::duration_cast<chrono::nanoseconds>(__atime - __s);

        __gthread_time_t __ts = {
          static_cast<std::time_t>(__s.time_since_epoch().count()),
          static_cast<long>(__ns.count())
        };
	
	__gthread_cond_timedwait(&_M_cond, __lock.mutex()->native_handle(), &__ts);
	
	return __clock_t::now() < __atime;
      }

    template<typename _Clock, typename _Duration, typename _Predicate>
      bool
      wait_until(unique_lock<mutex>& __lock,
		 const chrono::time_point<_Clock, _Duration>& __atime,
		 _Predicate __p);

    template<typename _Rep, typename _Period>
      bool
      wait_for(unique_lock<mutex>& __lock,
	       const chrono::duration<_Rep, _Period>& __rtime)
      { return __wait_for_impl(__rtime); }

    template<typename _Rep, typename _Period, typename _Predicate>
      bool
      wait_for(unique_lock<mutex>& __lock,
	       const chrono::duration<_Rep, _Period>& __rtime,
	       _Predicate __p);

    native_handle_type 
    native_handle() 
    { return &_M_cond; }

  private:
    __gthread_cond_t _M_cond;
    mutex _M_internal_mutex;

#ifdef _GLIBCXX_USE_CLOCK_MONOTONIC
    typedef chrono::monotonic_clock __clock_t;
#else
    typedef chrono::high_resolution_clock __clock_t;
#endif

    template<typename _Rep, typename _Period>
      typename enable_if<
        ratio_less_equal<__clock_t::period, _Period>::value, bool>::type
      __wait_for_impl(unique_lock<mutex>& __lock,
		      const chrono::duration<_Rep, _Period>& __rtime)
      { 
	__clock_t::time_point __atime = __clock_t::now()
	  + chrono::duration_cast<__clock_t::duration>(__rtime);
	
	return wait_until(__lock, __atime);
      }
    
    template<typename _Rep, typename _Period>
      typename enable_if<
        !ratio_less_equal<__clock_t::period, _Period>::value, bool>::type
      __wait_for_impl(unique_lock<mutex>& __lock,
		      const chrono::duration<_Rep, _Period>& __rtime)
      { 
	__clock_t::time_point __atime = __clock_t::now()
	  + ++chrono::duration_cast<__clock_t::duration>(__rtime);
	
	return wait_until(__lock, __atime);
      }    
  };

  /// condition_variable_any
  // Like above, only mutex may not have try_lock.
  class condition_variable_any
  {
  public:
    typedef __gthread_cond_t* native_handle_type;

    condition_variable_any();
    ~condition_variable_any();
    
    condition_variable_any(const condition_variable_any&) = delete;
    condition_variable_any& operator=(const condition_variable_any&) = delete;

    void 
    notify_one();

    void 
    notify_all();

    template<typename _Lock>
      void 
      wait(_Lock& __lock);

    template<typename _Lock, typename _Predicate>
      void 
      wait(_Lock& __lock, _Predicate __p);

    template<typename _Lock, typename _Clock, typename _Duration>
      bool 
      wait_until(_Lock& __lock,
		 const chrono::time_point<_Clock, _Duration>& __atime);

    template<typename _Lock, typename _Clock, 
	     typename _Duration, typename _Predicate>
      bool 
      wait_until(_Lock& __lock,
		 const chrono::time_point<_Clock, _Duration>& __atime,
		 _Predicate __p);
    
    template<typename _Lock, typename _Rep, typename _Period>
      bool
      wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __rtime);

    template<typename _Lock, typename _Rep,
	     typename _Period, typename _Predicate>
      bool
      wait_for(_Lock& __lock, 
	       const chrono::duration<_Rep, _Period>& __rtime, _Predicate __p);

    native_handle_type 
    native_handle()
    { return &_M_cond; }

  private:
    __gthread_cond_t _M_cond;
  };
}

#endif // _GLIBCXX_HAS_GTHREADS && _GLIBCXX_USE_C99_STDINT_TR1

#endif // __GXX_EXPERIMENTAL_CXX0X__

#endif // _GLIBCXX_CONDITION_VARIABLE
