/**
 * \file
 * \brief DeferredThreadDeleter class header
 *
 * \author Copyright (C) 2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_INTERNAL_MEMORY_DEFERREDTHREADDELETER_HPP_
#define INCLUDE_DISTORTOS_INTERNAL_MEMORY_DEFERREDTHREADDELETER_HPP_

#include "distortos/distortosConfiguration.h"

#ifdef CONFIG_THREAD_DETACH_ENABLE

#include "distortos/Mutex.hpp"

namespace distortos
{

namespace internal
{

/// DeferredThreadDeleter class can be used to defer deletion of dynamic detached threads
class DeferredThreadDeleter
{
public:

	/**
	 * \brief DeferredThreadDeleter's constructor
	 */

	constexpr DeferredThreadDeleter() :
			list_{},
			mutex_{Mutex::Type::normal, Mutex::Protocol::priorityInheritance},
			notEmpty_{}
	{

	}

	/**
	 * \brief DeferredThreadDeleter's function call operator
	 *
	 * Adds thread to internal list of threads scheduled for deferred deletion and marks the list as "not empty".
	 *
	 * \note The object must be locked (with a successful call to DeferredThreadDeleter::lock()) before this function is
	 * used!
	 *
	 * \param [in] threadControlBlock is a reference to ThreadControlBlock object associated with dynamic and detached
	 * thread that has terminated its execution
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by Mutex::unlock();
	 */

	int operator()(ThreadControlBlock& threadControlBlock);

	/**
	 * \brief Locks the object, preparing it for adding thread to internal list.
	 *
	 * Locks the mutex that synchronizes access to internal list. Locking (performed in this function) and unlocking
	 * (performed at the end of function call operator) are separated, because the locking must be done while the thread
	 * is still runnable, while the transfer to internal list is performed when the thread is not in this state.
	 *
	 * \note This function must be called before function call operator is used!
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by Mutex::lock();
	 */

	int lock();

	/**
	 * \brief Tries to perform deferred deletion of threads.
	 *
	 * Does nothing is the list is not marked as "not empty". Otherwise this function first tries to lock following two
	 * mutexes:
	 * - mutex that protects dynamic memory allocator;
	 * - mutex that synchronizes access to list of threads scheduled for deferred deletion;
	 * If any Mutex::tryLock() call fails, this function just returns (unlocking any mutexes is necessary). Otherwise
	 * the threads are removed from the list and deleted, while the list's "not empty" marker is cleared.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by Mutex::tryLock();
	 * - error codes returned by Mutex::unlock();
	 */

	int tryCleanup();

private:

	/**
	 * \brief Internals of tryCleanup().
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by Mutex::tryLock();
	 * - error codes returned by Mutex::unlock();
	 */

	int tryCleanupInternal();

	/// list of threads scheduled for deferred deletion
	ThreadList::UnsortedIntrusiveList list_;

	/// mutex that synchronizes access to the \a list_
	Mutex mutex_;

	/// true if \a list_ is not empty, false otherwise
	bool notEmpty_;
};

}	// namespace internal

}	// namespace distortos

#endif	// def CONFIG_THREAD_DETACH_ENABLE

#endif	// INCLUDE_DISTORTOS_INTERNAL_MEMORY_DEFERREDTHREADDELETER_HPP_
