// <experimental/synchronized_value> -*- C++ -*-

// Copyright The GNU Toolchain Authors.
//
// 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 3, 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.

// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.

// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
// <http://www.gnu.org/licenses/>.

/** @file include/experimental/synchronized_value
 *  This is a TS C++ Library header.
 *  @ingroup libfund-ts
 */

#ifndef _GLIBCXX_EXPERIMENTAL_SYNCVAL
#define _GLIBCXX_EXPERIMENTAL_SYNCVAL 1

#ifdef _GLIBCXX_SYSHDR
#pragma GCC system_header
#endif

#include <bits/requires_hosted.h> // for std::mutex

#if __cplusplus >= 201703L
#include <mutex>
#include <bits/invoke.h>

namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
namespace experimental::inline concurrency_v2
{
#define __cpp_lib_concurrency_v2_synchronized_value 202302

  template<typename _Tp>
    class synchronized_value
    {
      // TODO: Use partial specialization after PR c++/71954 is fixed.
      template<typename... _Args>
	static inline constexpr bool __is_self
	  = sizeof...(_Args) == 1
	      && (is_same_v<__remove_cvref_t<_Args>, synchronized_value> && ...);

#if ! __cpp_concepts
      template<typename... _Args>
	using __not_self = bool_constant<!__is_self<_Args...>>;
#endif

    public:
      synchronized_value(const synchronized_value&) = delete;
      synchronized_value& operator=(const synchronized_value&) = delete;

#if __cpp_concepts
      template<typename... _Args>
	requires (!__is_self<_Args...>) && is_constructible_v<_Tp, _Args...>
#else
      template<typename... _Args, typename = _Require<__not_self<_Args...>>,
	       typename = _Require<is_constructible<_Tp, _Args...>>>
#endif
	synchronized_value(_Args&&... __args)
	noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
	: _M_val(std::forward<_Args>(__args)...)
	{ }

      template<typename _Fn, typename _Up, typename ... _Types>
	friend invoke_result_t<_Fn, _Up&, _Types&...>
	apply(_Fn&&, synchronized_value<_Up>&, synchronized_value<_Types>&...);

    private:
      mutex _M_mut;
      _Tp _M_val;
    };

  template<typename _Fn, typename _Tp, typename... _Types>
    inline invoke_result_t<_Fn, _Tp&, _Types&...>
    apply(_Fn&& __f, synchronized_value<_Tp>& __val,
	  synchronized_value<_Types>&... __vals)
    {
      scoped_lock __l(__val._M_mut, __vals._M_mut...);
      return std::__invoke(std::forward<_Fn>(__f), __val._M_val,
			   __vals._M_val...);
    }
} // namespace experimental::concurrency_v2
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif // C++20
#endif // _GLIBCXX_EXPERIMENTAL_SYNCVAL
