//
//  Copyright (C) 2009-2012 Artyom Beilis (Tonkikh)
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOSTER_UTIL_THREAD_H
#define BOOSTER_UTIL_THREAD_H

#include <booster/hold_ptr.h>
#include <booster/noncopyable.h>
#include <booster/refcounted.h>
#include <booster/intrusive_ptr.h>
#include <booster/function.h>
#include <booster/config.h>

namespace booster {

	extern "C" void *booster_thread_func(void *);
	
	///
	/// \brief the class that allows to start an execution thread
	///
	class BOOSTER_API thread : public noncopyable {
	public:
		///
		/// Run a function \a cb in separate thread
		///
		thread(function<void()> const &cb);
		~thread();

		///
		/// Join existing thread
		///
		void join();

		///
		/// Detach from the thread
		///
		void detach();

		///
		/// Get number of CPUS, returns 0 if the number is unknown
		///
		static unsigned hardware_concurrency();
	private:
		friend void *booster_thread_func(void *);
		struct data;
		hold_ptr<data> d;
	};


	class condition_variable;

	///
	/// \brief Mutex object
	///
	class BOOSTER_API mutex : public noncopyable {
	public:
		mutex();
		~mutex();
		///
		/// Lock the mutex. If the same thread tries to lock the mutex it holds, the behavior is undefined
		/// and would likely lead to deadlock
		///
		/// \see unique_lock
		///
		void lock();
		///
		/// Unlock the mutex. If called for unlocked mutex, the behavior is undefined
		///
		/// \see unique_lock
		///
		void unlock();
		friend class condition_variable;
	private:
		struct data;
		hold_ptr<data> d;
	};

	///
	/// \brief Recursive mutex object.
	///
	/// Unlike \ref mutex, when the same thread tries to lock the mutex that is already locked it would
	/// succeed and would require to unlock it same times it was locked.
	///
	/// Useful for handling objects locks when the order of functions calls is not known in advice. For example
	/// \code
	/// void foo()
	/// {
	///    unique_lock<recursive_mutex> guard(this->lock);
	///    bar();
	///    baz();
	/// }
	/// void bar()
	/// {
	///    unique_lock<recursive_mutex> guard(this->lock);
	///    ...
	/// }
	/// \endcode
	///
	/// \see unique_lock
	/// \see shared_lock 
	///
	class BOOSTER_API recursive_mutex : public noncopyable {
	public:
		recursive_mutex();
		~recursive_mutex();
		///
		/// Lock the mutex. \see unique_lock
		///
		void lock();
		///
		/// Unlock the mutex, \see unique_lock
		/// 
		void unlock();
	private:
		struct data;
		hold_ptr<data> d;
	};

	///
	/// \brief Recursuve Shared mutex or a.k.a. Read-Write Lock that can be recursively locked by \b readers
	/// 
	/// This class provides two options of locking unique - nobody but me can use the object
	/// shared anybody with shared lock can use the object.
	///
	class BOOSTER_API recursive_shared_mutex : public noncopyable {
	public:
		recursive_shared_mutex();
		~recursive_shared_mutex();
		///
		/// Same as unique_lock()
		///
		/// \note this function is not recursive
		///
		void lock() { unique_lock(); }
		///
		/// Acquire a unique lock on the object. \see booster::unique_lock
		///
		/// \note this function is not recursive
		///
		void unique_lock();
		///
		/// Acquire a shared lock on the object.  \see booster::shared_lock
		///
		/// \note the shared_lock() member function is recursive, that means that same thread may acquire
		/// it multiple times.
		///
		void shared_lock();
		///
		/// Release the lock
		///
		void unlock();
	private:
		struct data;
		hold_ptr<data> d;
	};
	///
	/// \brief Shared mutex or a.k.a. Read-Write Lock
	/// 
	/// This class provides two options of locking unique - nobody but me can use the object
	/// shared anybody with shared lock can use the object.
	///
	class BOOSTER_API shared_mutex : public noncopyable {
	public:
		shared_mutex();
		~shared_mutex();
		///
		/// Same as unique_lock()
		///
		void lock() { unique_lock(); }
		///
		/// Acquire a unique lock on the object. \see booster::unique_lock
		///
		void unique_lock();
		///
		/// Acquire a shared lock on the object.  \see booster::shared_lock
		///
		/// Note this function is not recursive
		///
		void shared_lock();
		///
		/// Release the lock
		///
		void unlock();
	private:
		struct data;
		hold_ptr<data> d;
	};

	template<typename Mutex>
	class unique_lock;

	///
	/// \brief This is conditional variable object
	///
	/// For detailed description of the concept read http://en.wikipedia.org/wiki/Monitor_(synchronization)
	///
	class BOOSTER_API condition_variable {
	public:
		condition_variable();
		~condition_variable();
		
		///
		/// Wait for the condition atomically unlocking the mutex referenced by m.
		///
		/// When the condition occurs the lock on the mutex would be acquired again.
		///
		/// Note it is unspecified whether suspicious wakes can occur. It is good idea to check
		/// whether the condition hold after this function returns.
		///
		void wait(unique_lock<mutex> &m);

		///
		/// Notify exactly one waiting process on the condition. If no process waits then the notification
		/// would be ignored
		/// 
		void notify_one();
		///
		/// Notify all waiting process on the condition. If no process waits then the notification
		/// would be ignored
		/// 
		void notify_all();
	private:
		struct data;
		hold_ptr<data> d;
	};

	/// \cond INTERNAL
	namespace details {
		struct tls_object;

		class key : public refcounted {
		public:
			key(void (*d)(void *)) : 
				dtor_(d)
			{
			}
			virtual ~key()
			{
			}
			void *get();
			void set(void *);

			void destroy(void *p)
			{
				dtor_(p);
			}
			virtual tls_object *get_object() = 0;
		private:
			void (*dtor_)(void *);
		};

		BOOSTER_API intrusive_ptr<key> make_key(void (*dtor)(void *));

		struct tls_object {
			tls_object(intrusive_ptr<key> p) :
				the_key(p),
				obj(0)
			{
			}
			~tls_object()
			{
				the_key->destroy(obj);
				obj = 0;
			}
			intrusive_ptr<key> the_key;
			void *obj;
		};

		inline void key::set(void *p)
		{
			get_object()->obj = p;
		}
		inline void *key::get()
		{
			return get_object()->obj;
		}

	} // details

	/// \endcond

	///
	/// \brief Thread specific pointer
	///
	/// Thread specific pointer is NULL for all threads. So on first use it is expected
	/// to be initialized.
	///
	/// The object would be destroyed when the thread exists. If thread_specific_ptr was destroyed
	/// before the thread that uses it exited the object would still exist and would be cleaned up
	/// on thread exit.
	///
	/// Of course an access to thread_specific_ptr object would lead to undefined behavior and would
	/// likely is going to crash your program. 
	///
	/// \note
	///
	/// On Windows platform, when CppCMS and Booster are compiled \b statically (not used as DLL) then
	/// TLS cleanup for the threads that where not created with booster::thread would not be executed.
	///
	/// So when using thread_specific_ptr on Windows platform make sure that you do one of the following:
	///
	/// - Use booster::thread_specific_ptr only from threads created by booster::thread (or main thread)
	/// - Link dynamically with booster library (use it as DLL)
	///
	template<typename T>
	class thread_specific_ptr {
	public:
		///
		/// Create a new thread specific pointer
		///
		thread_specific_ptr() : key_(details::make_key(destructor))
		{
		}

		///
		/// Destroy the thread specific pointer
		///
		~thread_specific_ptr()
		{
		}
		///
		/// Get thread specific value, if not initialized returns NULL
		///
		T *get() const
		{
			return static_cast<T*>(key_->get());
		}
		///
		/// Dereference pointer, if it is NULL the behavior is undefined
		///
		T* operator->() const
		{
			return get();
		}
		///
		/// Dereference pointer, if it is NULL the behavior is undefined
		///
		T& operator*() const
		{
			return *get();
		}
		///
		/// Reset the thread specific pointer and set its value to \a new_val. 
		///
		/// If previous object was not NULL it would be destroyed
		///
		void reset(T *new_val = 0)
		{
			T *p = get();
			if(p)
				destructor(p);
			key_->set(static_cast<void *>(new_val));
		}
		///
		/// Release exiting pointer and set thread specific pointer to 0.
		///
		/// The caller is responsible to destroy the returned object
		///
		T *release()
		{
			T *p = get();
			key_->set(0);
			return p;
		}
	private:
		static void destructor(void *ptr)
		{
			delete static_cast<T*>(ptr);
		}
		intrusive_ptr<details::key> key_;
	};


	///
	/// \brief a Unique lock guard.
	///
	/// Acquire the unique lock in the constructor and release it in the destructor
	///
	template<typename Mutex>
	class unique_lock : public noncopyable {
	public:
		/// Acquire the lock
		unique_lock(Mutex &m) : m_(&m)
		{
			m_->lock();
		}
		/// Release the lock
		~unique_lock()
		{
			m_->unlock();
		}
		/// Get the reference to the mutex object
		Mutex *mutex() const
		{
			return m_;
		}
	private:
		Mutex *m_;
	};

	///
	/// \brief a Shared lock guard.
	///
	/// Acquire the shared lock in the constructor and release it in the destructor
	///
	template<typename Mutex>
	class shared_lock : public noncopyable {
	public:
		/// Acquire the lock
		shared_lock(Mutex &m) : m_(&m)
		{
			m_->shared_lock();
		}
		/// Release the lock
		~shared_lock()
		{
			m_->unlock();
		}
		/// Get the reference to the mutex object
		Mutex *mutex() const
		{
			return m_;
		}
	private:
		Mutex *m_;
	};
#ifdef BOOSTER_POSIX
	///
	/// \brief The mutex that can be used by the processes that had forked
	///
	/// \note the lock is automatically released when process dies
	///
	class BOOSTER_API fork_shared_mutex : public noncopyable {
	public:
		///
		/// Create a new mutex - should be done before calling to fork()
		///
		fork_shared_mutex();
		~fork_shared_mutex();

		///
		/// Try to acquire a unique lock on the mutex
		///
		bool try_lock() { return try_unique_lock(); }
		///
		/// Try to acquire a unique lock on the mutex
		///
		bool try_unique_lock();
		///
		/// Try to acquire a shared lock on the mutex
		///
		bool try_shared_lock();

		///
		/// Acquire a unique lock on the mutex
		///
		void lock() { return unique_lock(); }
		///
		/// Acquire a unique lock on the mutex
		///
		void unique_lock();
		///
		/// Acquire a shared lock on the mutex
		///
		void shared_lock();

		///
		/// Release the mutex
		///
		void unlock();
	private:
		struct data;
		hold_ptr<data> d;
	};
#endif

}//booster


#endif
