/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* pplinterface.h
*
* Parallel Patterns Library - PPL interface definitions
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/

#pragma once

#ifndef _PPLINTERFACE_H
#define _PPLINTERFACE_H

#include <memory>
#include <atomic>

namespace Concurrency
{

/// <summary>
///     An elementary abstraction for a task, defined as <c>void (__cdecl * TaskProc_t)(void *)</c>. A <c>TaskProc</c> is called to
///     invoke the body of a task.
/// </summary>
/**/
typedef void (__cdecl * TaskProc_t)(void *);

/// <summary>
///     Scheduler Interface
/// </summary>
struct __declspec(novtable) scheduler_interface
{
    virtual void schedule( TaskProc_t, void* ) = 0;
};

/// <summary>
///     Represents a pointer to a scheduler. This class exists to allow the
///     the specification of a shared lifetime by using shared_ptr or just
///     a plain reference by using raw pointer.
/// </summary>
struct scheduler_ptr
{
    /// <summary>
    /// Creates a scheduler pointer from shared_ptr to scheduler
    /// </summary>
    explicit scheduler_ptr(::std::shared_ptr<scheduler_interface> _Scheduler) : _M_sharedScheduler(::std::move(_Scheduler))
    {
        _M_scheduler = _M_sharedScheduler.get();
    }

    /// <summary>
    /// Creates a scheduler pointer from raw pointer to scheduler
    /// </summary>
    explicit scheduler_ptr(_In_opt_ scheduler_interface * _PScheduler) : _M_scheduler(_PScheduler)
    {
    }

    /// <summary>
    /// Behave like a pointer
    /// </summary>
    scheduler_interface *operator->() const
    {
        return get();
    }

    /// <summary>
    ///  Returns the raw pointer to the scheduler
    /// </summary>
    scheduler_interface * get() const
    {
        return _M_scheduler;
    }

    /// <summary>
    /// Test whether the scheduler pointer is non-null
    /// </summary>
    operator bool() const { return get() != nullptr; }

private:

    ::std::shared_ptr<scheduler_interface> _M_sharedScheduler;
    scheduler_interface * _M_scheduler;
};

/// <summary>
///     Describes the execution status of a <c>task_group</c> or <c>structured_task_group</c> object.  A value of this type is returned
///     by numerous methods that wait on tasks scheduled to a task group to complete.
/// </summary>
/// <seealso cref="task_group Class"/>
/// <seealso cref="task_group::wait Method"/>
/// <seealso cref="task_group::run_and_wait Method"/>
/// <seealso cref="structured_task_group Class"/>
/// <seealso cref="structured_task_group::wait Method"/>
/// <seealso cref="structured_task_group::run_and_wait Method"/>
/**/
enum task_group_status
{
    /// <summary>
    ///     The tasks queued to the <c>task_group</c> object have not completed.  Note that this value is not presently returned by
    ///     the Concurrency Runtime.
    /// </summary>
    /**/
    not_complete,

    /// <summary>
    ///     The tasks queued to the <c>task_group</c> or <c>structured_task_group</c> object completed successfully.
    /// </summary>
    /**/
    completed,

    /// <summary>
    ///     The <c>task_group</c> or <c>structured_task_group</c> object was canceled.  One or more tasks may not have executed.
    /// </summary>
    /**/
    canceled
};

/// <summary>
///     This class describes an exception thrown when an invalid operation is performed that is not more accurately
///     described by another exception type thrown by the Concurrency Runtime.
/// </summary>
/// <remarks>
///     The various methods which throw this exception will generally document under what circumstances they will throw it.
/// </remarks>
/**/
class invalid_operation : public ::std::exception
{
public:
    /// <summary>
    ///     Constructs an <c>invalid_operation</c> object.
    /// </summary>
    /// <param name="_Message">
    ///     A descriptive message of the error.
    /// </param>
    /**/
    explicit invalid_operation(_In_z_ const char* _Message) noexcept
        : exception(_Message)
    { }

    /// <summary>
    ///     Constructs an <c>invalid_operation</c> object.
    /// </summary>
    /**/
    invalid_operation() noexcept
        : exception()
    {}
};

/// <summary>
///     This class describes an exception thrown by the PPL tasks layer in order to force the current task
///     to cancel. It is also thrown by the <c>get()</c> method on <see cref="task Class">task</see>, for a
///     canceled task.
/// </summary>
/// <seealso cref="task::get Method"/>
/// <seealso cref="cancel_current_task Method"/>
/**/
class task_canceled : public ::std::exception
{
public:
    /// <summary>
    ///     Constructs a <c>task_canceled</c> object.
    /// </summary>
    /// <param name="_Message">
    ///     A descriptive message of the error.
    /// </param>
    /**/
    explicit task_canceled(_In_z_ const char * _Message) noexcept
        : exception(_Message)
    {}

    /// <summary>
    ///     Constructs a <c>task_canceled</c> object.
    /// </summary>
    /**/
    task_canceled() noexcept
        : exception()
    {}
};

namespace details
{

//
// An internal exception that is used for cancellation. Users do not "see" this exception except through the
// resulting stack unwind. This exception should never be intercepted by user code. It is intended
// for use by the runtime only.
//
class _Interruption_exception : public ::std::exception
{
public:
    explicit _Interruption_exception(const char * _Message) noexcept
        : exception(_Message)
    {}

    _Interruption_exception() noexcept
        : exception()
    {}
};

/// <summary>
///     The enum defines inlining scheduling policy for ppltasks.
///     Scheduling a chore or a functor with _TaskInliningMode will give
///     scheduler a hint on whether apply inline execution or not.
/// </summary>
/// <remarks>
///     As an optimization, we assigned an integer number to each option in the enum,
///     which effectively stands for the maximal inlining depth (threshold) for current chore,
///     and the scheduler will compare this threshold with current context's inlining depth to
///     make inline decision.
///     If the current context's inlining depth greater than this threshold,
///     the chore will be scheduled on a new context, otherwise the chore will be scheduled inline.
///     Minimal threshold 0 means do not inline; maximal threshold -1 (0xFFFFFFFF....) means always inline.
///     16 is a good default inlining threshold we figured out from experiment.
/// </remarks>
enum _TaskInliningMode
{
    // Disable inline scheduling
    _NoInline = 0,
    // Let runtime decide whether to do inline scheduling or not
    _DefaultAutoInline = 16,
    // Always do inline scheduling
    _ForceInline = -1,
};

/// <summary>
///     Atomics
/// </summary>
typedef ::std::atomic<long> atomic_long;
typedef ::std::atomic<size_t> atomic_size_t;

template<typename _T>
_T atomic_compare_exchange(::std::atomic<_T>& _Target, _T _Exchange, _T _Comparand)
{
    _T _Result = _Comparand;
    _Target.compare_exchange_strong(_Result, _Exchange);
    return _Result;
}

template<typename _T>
_T atomic_exchange(::std::atomic<_T>& _Target, _T _Value)
{
    return _Target.exchange(_Value);
}

template<typename _T>
_T atomic_increment(::std::atomic<_T>& _Target)
{
    return _Target.fetch_add(1) + 1;
}

template<typename _T>
_T atomic_decrement(::std::atomic<_T>& _Target)
{
    return _Target.fetch_sub(1) - 1;
}

template<typename _T>
_T atomic_add(::std::atomic<_T>& _Target, _T _Value)
{
    return _Target.fetch_add(_Value) + _Value;
}

}} // namespace Concurrency::details

#endif // _PPLINTERFACE_H
