text: //===-- llvm/Support/thread.h - Wrapper for <thread> ------------*- C++ -*-===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  //
  // This header is a wrapper for <thread> that works around problems with the
  // MSVC headers when exceptions are disabled. It also provides llvm::thread,
  // which is either a typedef of std::thread or a replacement that calls the
  // function synchronously depending on the value of LLVM_ENABLE_THREADS.
  //
  //===----------------------------------------------------------------------===//
  
  #ifndef LLVM_SUPPORT_THREAD_H
  #define LLVM_SUPPORT_THREAD_H
  
  #include "llvm/ADT/Optional.h"
  #include "llvm/Config/llvm-config.h"
  
  #ifdef _WIN32
  typedef unsigned long DWORD;
  typedef void *PVOID;
  typedef PVOID HANDLE;
  #endif
  
  #if LLVM_ENABLE_THREADS
  
  #include <thread>
  
  namespace llvm {
  
  #if LLVM_ON_UNIX || _WIN32
  
  /// LLVM thread following std::thread interface with added constructor to
  /// specify stack size.
  class thread {
    template <typename FPtr, typename... Args, size_t... Indices>
    static void Apply(std::tuple<FPtr, Args...> &Callee,
                      std::index_sequence<Indices...>) {
      std::move(std::get<0>(Callee))(std::move(std::get<Indices + 1>(Callee))...);
    }
  
    template <typename CalleeTuple> static void GenericThreadProxy(void *Ptr) {
      std::unique_ptr<CalleeTuple> Callee(static_cast<CalleeTuple *>(Ptr));
  
      // FIXME: use std::apply when C++17 is allowed.
      std::make_index_sequence<std::tuple_size<CalleeTuple>() - 1> Indices{};
      Apply(*Callee.get(), Indices);
    }
  
  public:
  #if LLVM_ON_UNIX
    using native_handle_type = pthread_t;
    using id = pthread_t;
    using start_routine_type = void *(*)(void *);
  
    template <typename CalleeTuple> static void *ThreadProxy(void *Ptr) {
      GenericThreadProxy<CalleeTuple>(Ptr);
      return nullptr;
    }
  #elif _WIN32
    using native_handle_type = HANDLE;
    using id = DWORD;
    using start_routine_type = unsigned(__stdcall *)(void *);
  
    template <typename CalleeTuple>
    static unsigned __stdcall ThreadProxy(void *Ptr) {
      GenericThreadProxy<CalleeTuple>(Ptr);
      return 0;
    }
  #endif
  
    static const llvm::Optional<unsigned> DefaultStackSize;
  
    thread() : Thread(native_handle_type()) {}
    thread(thread &&Other) noexcept
        : Thread(std::exchange(Other.Thread, native_handle_type())) {}
  
    template <class Function, class... Args>
    explicit thread(Function &&f, Args &&...args)
        : thread(DefaultStackSize, f, args...) {}
  
    template <class Function, class... Args>
    explicit thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
                    Args &&...args);
    thread(const thread &) = delete;
  
    ~thread() {
      if (joinable())
        std::terminate();
    }
  
    thread &operator=(thread &&Other) noexcept {
      if (joinable())
        std::terminate();
      Thread = std::exchange(Other.Thread, native_handle_type());
      return *this;
    }
  
    bool joinable() const noexcept { return Thread != native_handle_type(); }
  
    inline id get_id() const noexcept;
  
    native_handle_type native_handle() const noexcept { return Thread; }
  
    static unsigned hardware_concurrency() {
      return std::thread::hardware_concurrency();
    };
  
    inline void join();
    inline void detach();
  
    void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); }
  
  private:
    native_handle_type Thread;
  };
  
  thread::native_handle_type
  llvm_execute_on_thread_impl(thread::start_routine_type ThreadFunc, void *Arg,
                              llvm::Optional<unsigned> StackSizeInBytes);
  void llvm_thread_join_impl(thread::native_handle_type Thread);
  void llvm_thread_detach_impl(thread::native_handle_type Thread);
  thread::id llvm_thread_get_id_impl(thread::native_handle_type Thread);
  thread::id llvm_thread_get_current_id_impl();
  
  template <class Function, class... Args>
  thread::thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
                 Args &&...args) {
    typedef std::tuple<typename std::decay<Function>::type,
                       typename std::decay<Args>::type...>
        CalleeTuple;
    std::unique_ptr<CalleeTuple> Callee(
        new CalleeTuple(std::forward<Function>(f), std::forward<Args>(args)...));
  
    Thread = llvm_execute_on_thread_impl(ThreadProxy<CalleeTuple>, Callee.get(),
                                         StackSizeInBytes);
    if (Thread != native_handle_type())
      Callee.release();
  }
  
  thread::id thread::get_id() const noexcept {
    return llvm_thread_get_id_impl(Thread);
  }
  
  void thread::join() {
    llvm_thread_join_impl(Thread);
    Thread = native_handle_type();
  }
  
  void thread::detach() {
    llvm_thread_detach_impl(Thread);
    Thread = native_handle_type();
  }
  
  namespace this_thread {
  inline thread::id get_id() { return llvm_thread_get_current_id_impl(); }
  } // namespace this_thread
  
  #else // !LLVM_ON_UNIX && !_WIN32
  
  /// std::thread backed implementation of llvm::thread interface that ignores the
  /// stack size request.
  class thread {
  public:
    using native_handle_type = std::thread::native_handle_type;
    using id = std::thread::id;
  
    thread() : Thread(std::thread()) {}
    thread(thread &&Other) noexcept
        : Thread(std::exchange(Other.Thread, std::thread())) {}
  
    template <class Function, class... Args>
    explicit thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
                    Args &&...args)
        : Thread(std::forward<Function>(f), std::forward<Args>(args)...) {}
  
    template <class Function, class... Args>
    explicit thread(Function &&f, Args &&...args) : Thread(f, args...) {}
  
    thread(const thread &) = delete;
  
    ~thread() {}
  
    thread &operator=(thread &&Other) noexcept {
      Thread = std::exchange(Other.Thread, std::thread());
      return *this;
    }
  
    bool joinable() const noexcept { return Thread.joinable(); }
  
    id get_id() const noexcept { return Thread.get_id(); }
  
    native_handle_type native_handle() noexcept { return Thread.native_handle(); }
  
    static unsigned hardware_concurrency() {
      return std::thread::hardware_concurrency();
    };
  
    inline void join() { Thread.join(); }
    inline void detach() { Thread.detach(); }
  
    void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); }
  
  private:
    std::thread Thread;
  };
  
  namespace this_thread {
    inline thread::id get_id() { return std::this_thread::get_id(); }
  }
  
  #endif // LLVM_ON_UNIX || _WIN32
  
  } // namespace llvm
  
  #else // !LLVM_ENABLE_THREADS
  
  #include <utility>
  
  namespace llvm {
  
  struct thread {
    thread() {}
    thread(thread &&other) {}
    template <class Function, class... Args>
    explicit thread(llvm::Optional<unsigned> StackSizeInBytes, Function &&f,
                    Args &&...args) {
      f(std::forward<Args>(args)...);
    }
    template <class Function, class... Args>
    explicit thread(Function &&f, Args &&...args) {
      f(std::forward<Args>(args)...);
    }
    thread(const thread &) = delete;
  
    void detach() {
      report_fatal_error("Detaching from a thread does not make sense with no "
                         "threading support");
    }
    void join() {}
    static unsigned hardware_concurrency() { return 1; };
  };
  
  } // namespace llvm
  
  #endif // LLVM_ENABLE_THREADS
  
  #endif // LLVM_SUPPORT_THREAD_H
  