xref: /llvm-project/libcxx/include/latch (revision b9a2658a3e8bd13b0f9e7a8a440832a95b377216)
154fa9ecdSOlivier Giroux// -*- C++ -*-
2eb8650a7SLouis Dionne//===----------------------------------------------------------------------===//
354fa9ecdSOlivier Giroux//
454fa9ecdSOlivier Giroux// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
554fa9ecdSOlivier Giroux// See https://llvm.org/LICENSE.txt for license information.
654fa9ecdSOlivier Giroux// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
754fa9ecdSOlivier Giroux//
854fa9ecdSOlivier Giroux//===----------------------------------------------------------------------===//
954fa9ecdSOlivier Giroux
1054fa9ecdSOlivier Giroux#ifndef _LIBCPP_LATCH
1154fa9ecdSOlivier Giroux#define _LIBCPP_LATCH
1254fa9ecdSOlivier Giroux
1354fa9ecdSOlivier Giroux/*
1454fa9ecdSOlivier Giroux    latch synopsis
1554fa9ecdSOlivier Giroux
1654fa9ecdSOlivier Girouxnamespace std
1754fa9ecdSOlivier Giroux{
1854fa9ecdSOlivier Giroux
19bf1666fbSLouis Dionne  class latch                                     // since C++20
2054fa9ecdSOlivier Giroux  {
2154fa9ecdSOlivier Giroux  public:
22e2279c23SMarek Kurdej    static constexpr ptrdiff_t max() noexcept;
23e2279c23SMarek Kurdej
2454fa9ecdSOlivier Giroux    constexpr explicit latch(ptrdiff_t __expected);
2554fa9ecdSOlivier Giroux    ~latch();
2654fa9ecdSOlivier Giroux
2754fa9ecdSOlivier Giroux    latch(const latch&) = delete;
2854fa9ecdSOlivier Giroux    latch& operator=(const latch&) = delete;
2954fa9ecdSOlivier Giroux
3054fa9ecdSOlivier Giroux    void count_down(ptrdiff_t __update = 1);
3154fa9ecdSOlivier Giroux    bool try_wait() const noexcept;
3254fa9ecdSOlivier Giroux    void wait() const;
3354fa9ecdSOlivier Giroux    void arrive_and_wait(ptrdiff_t __update = 1);
3454fa9ecdSOlivier Giroux
3554fa9ecdSOlivier Giroux  private:
3654fa9ecdSOlivier Giroux    ptrdiff_t __counter; // exposition only
3754fa9ecdSOlivier Giroux  };
3854fa9ecdSOlivier Giroux
3954fa9ecdSOlivier Giroux}
4054fa9ecdSOlivier Giroux
4154fa9ecdSOlivier Giroux*/
4254fa9ecdSOlivier Giroux
43*b9a2658aSNikolas Klauser#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
44*b9a2658aSNikolas Klauser#  include <__cxx03/latch>
45*b9a2658aSNikolas Klauser#else
4634933d18SMark de Wever#  include <__config>
4734933d18SMark de Wever
48c6f3b7bcSNikolas Klauser#  if _LIBCPP_HAS_THREADS
4934933d18SMark de Wever
5037dca605SLouis Dionne#    include <__assert>
513a634076SLouis Dionne#    include <__atomic/atomic.h>
5270617a1aSNikolas Klauser#    include <__atomic/atomic_sync.h>
5370617a1aSNikolas Klauser#    include <__atomic/memory_order.h>
54e99c4906SNikolas Klauser#    include <__cstddef/ptrdiff_t.h>
55643df8faSLouis Dionne#    include <limits>
56bd6e6846SMark de Wever#    include <version>
5754fa9ecdSOlivier Giroux
5854fa9ecdSOlivier Giroux#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
5954fa9ecdSOlivier Giroux#      pragma GCC system_header
6054fa9ecdSOlivier Giroux#    endif
6154fa9ecdSOlivier Giroux
62d4303307SArthur O'Dwyer_LIBCPP_PUSH_MACROS
63d4303307SArthur O'Dwyer#    include <__undef_macros>
64d4303307SArthur O'Dwyer
65bf1666fbSLouis Dionne#    if _LIBCPP_STD_VER >= 20
6654fa9ecdSOlivier Giroux
6754fa9ecdSOlivier Giroux_LIBCPP_BEGIN_NAMESPACE_STD
6854fa9ecdSOlivier Giroux
69bf1666fbSLouis Dionneclass latch {
703a634076SLouis Dionne  atomic<ptrdiff_t> __a_;
7154fa9ecdSOlivier Giroux
7254fa9ecdSOlivier Girouxpublic:
739783f28cSLouis Dionne  static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept { return numeric_limits<ptrdiff_t>::max(); }
7454fa9ecdSOlivier Giroux
759783f28cSLouis Dionne  inline _LIBCPP_HIDE_FROM_ABI constexpr explicit latch(ptrdiff_t __expected) : __a_(__expected) {
76dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
779783f28cSLouis Dionne        __expected >= 0,
785e807c38SEdoardo Sanguineti        "latch::latch(ptrdiff_t): latch cannot be "
795e807c38SEdoardo Sanguineti        "initialized with a negative value");
80dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
819783f28cSLouis Dionne        __expected <= max(),
825e807c38SEdoardo Sanguineti        "latch::latch(ptrdiff_t): latch cannot be "
835e807c38SEdoardo Sanguineti        "initialized with a value greater than max()");
845e807c38SEdoardo Sanguineti  }
8554fa9ecdSOlivier Giroux
8683ce1397SNikolas Klauser  _LIBCPP_HIDE_FROM_ABI ~latch() = default;
8754fa9ecdSOlivier Giroux  latch(const latch&)            = delete;
8854fa9ecdSOlivier Giroux  latch& operator=(const latch&) = delete;
8954fa9ecdSOlivier Giroux
909783f28cSLouis Dionne  inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void count_down(ptrdiff_t __update = 1) {
91dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__update >= 0, "latch::count_down called with a negative value");
9284fc2c3cSNikolas Klauser    auto const __old = __a_.fetch_sub(__update, memory_order_release);
93dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
949783f28cSLouis Dionne        __update <= __old,
959783f28cSLouis Dionne        "latch::count_down called with a value greater "
965e807c38SEdoardo Sanguineti        "than the internal counter");
9754fa9ecdSOlivier Giroux    if (__old == __update)
9884fc2c3cSNikolas Klauser      __a_.notify_all();
9954fa9ecdSOlivier Giroux  }
10095ebf2beSJan Kokemüller  inline _LIBCPP_HIDE_FROM_ABI bool try_wait() const noexcept {
10195ebf2beSJan Kokemüller    auto __value = __a_.load(memory_order_acquire);
10295ebf2beSJan Kokemüller    return try_wait_impl(__value);
10395ebf2beSJan Kokemüller  }
1049783f28cSLouis Dionne  inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void wait() const {
105d681e103SLouis Dionne    std::__atomic_wait_unless(__a_, memory_order_acquire, [this](ptrdiff_t& __value) -> bool {
106d681e103SLouis Dionne      return try_wait_impl(__value);
107d681e103SLouis Dionne    });
10854fa9ecdSOlivier Giroux  }
1099783f28cSLouis Dionne  inline _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void arrive_and_wait(ptrdiff_t __update = 1) {
110dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__update >= 0, "latch::arrive_and_wait called with a negative value");
1115e807c38SEdoardo Sanguineti    // other preconditions on __update are checked in count_down()
1125e807c38SEdoardo Sanguineti
11354fa9ecdSOlivier Giroux    count_down(__update);
11454fa9ecdSOlivier Giroux    wait();
11554fa9ecdSOlivier Giroux  }
11695ebf2beSJan Kokemüller
11795ebf2beSJan Kokemüllerprivate:
1181f613bceSHui  _LIBCPP_HIDE_FROM_ABI bool try_wait_impl(ptrdiff_t& __value) const noexcept { return __value == 0; }
11954fa9ecdSOlivier Giroux};
12054fa9ecdSOlivier Giroux
12154fa9ecdSOlivier Giroux_LIBCPP_END_NAMESPACE_STD
12254fa9ecdSOlivier Giroux
123bf1666fbSLouis Dionne#    endif // _LIBCPP_STD_VER >= 20
124ab41129bSLouis Dionne
125d4303307SArthur O'Dwyer_LIBCPP_POP_MACROS
126d4303307SArthur O'Dwyer
127c6f3b7bcSNikolas Klauser#  endif // _LIBCPP_HAS_THREADS
128ef51e617SLouis Dionne
12970617a1aSNikolas Klauser#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
13070617a1aSNikolas Klauser#    include <atomic>
131e99c4906SNikolas Klauser#    include <cstddef>
13270617a1aSNikolas Klauser#  endif
133*b9a2658aSNikolas Klauser#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
13470617a1aSNikolas Klauser
13554fa9ecdSOlivier Giroux#endif // _LIBCPP_LATCH
136