//
// Activity.h
//
// Library: Foundation
// Package: Threading
// Module:  ActiveObjects
//
// Definition of the Activity template class.
//
// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier:	BSL-1.0
//


#ifndef Foundation_Activity_INCLUDED
#define Foundation_Activity_INCLUDED


#include "Poco/Foundation.h"
#include "Poco/RunnableAdapter.h"
#include "Poco/ThreadPool.h"
#include "Poco/Event.h"
#include "Poco/Mutex.h"


namespace Poco {


template <class C>
class Activity: public Runnable
	/// This template class helps to implement active objects.
	/// An active object uses threads to decouple method
	/// execution from method invocation, or to perform tasks
	/// autonomously, without intervention of a caller.
	///
	/// An activity is a (typically longer running) method
	/// that executes within its own task. Activities can
	/// be started automatically (upon object construction)
	/// or manually at a later time. Activities can also
	/// be stopped at any time. However, to make stopping
	/// an activity work, the method implementing the
	/// activity has to check periodically whether it
	/// has been requested to stop, and if so, return.
	/// Activities are stopped before the object they belong to is
	/// destroyed. Methods implementing activities cannot have arguments
	/// or return values.
	///
	/// Activity objects are used as follows:
	///
	///     class ActiveObject
	///     {
	///     public:
	///         ActiveObject():
	///             _activity(this, &ActiveObject::runActivity)
	///         {
	///             ...
	///         }
	///
	///         ...
	///
	///     protected:
	///         void runActivity()
	///         {
	///             while (!_activity.isStopped())
	///             {
	///                 ...
	///             }
	///         }
	///
	///     private:
	///         Activity<ActiveObject> _activity;
	///     };
{
public:
	using RunnableAdapterType = RunnableAdapter<C>;
	using Callback = typename RunnableAdapterType::Callback;

	Activity(C* pOwner, Callback method):
		_pOwner(pOwner),
		_runnable(*pOwner, method),
		_stopped(true),
		_running(false),
		_done(Event::EVENT_MANUALRESET)
		/// Creates the activity. Call start() to
		/// start it.
	{
		poco_check_ptr (pOwner);
	}

	~Activity() override
	/// Stops and destroys the activity.
	{
		try
		{
			stop();
			wait();
		}
		catch (...)
		{
			poco_unexpected();
		}
	}

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

	void start()
		/// Starts the activity by acquiring a
		/// thread for it from the default thread pool.
	{
		start(ThreadPool::defaultPool());
	}

	void start(ThreadPool& pool)
	{
		FastMutex::ScopedLock lock(_mutex);

		if (!_running)
		{
			_done.reset();
			_stopped = false;
			_running = true;
			try
			{
				pool.start(*this);
			}
			catch (...)
			{
				_running = false;
				throw;
			}
		}
	}

	void stop()
		/// Requests to stop the activity.
	{
		_stopped = true;
	}

	void wait()
		/// Waits for the activity to complete.
	{
		if (_running)
		{
			_done.wait();
			_running = false;
		}
	}

	void wait(long milliseconds)
		/// Waits the given interval for the activity to complete.
		/// An TimeoutException is thrown if the activity does not
		/// complete within the given interval.
	{
		if (_running)
		{
			_done.wait(milliseconds);
			_running = false;
		}
	}

	bool isStopped() const
		/// Returns true if the activity has been requested to stop.
	{
		return _stopped;
	}

	bool isRunning() const
		/// Returns true if the activity is running.
	{
		return _running;
	}

protected:
	void run() override
	{
		try
		{
			_runnable.run();
		}
		catch (...)
		{
			_running = false;
			_done.set();
			throw;
		}
		_running = false;
		_done.set();
	}

private:
	C*                  _pOwner;
	RunnableAdapterType _runnable;
	std::atomic<bool>   _stopped;
	std::atomic<bool>   _running;
	Event               _done;
	FastMutex           _mutex;
};


} // namespace Poco


#endif // Foundation_Activity_INCLUDED
