text: // Copyright 2018 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef SRC_ZIRCON_TESTS_FUTEX_OWNERSHIP_UTILS_H_
  #define SRC_ZIRCON_TESTS_FUTEX_OWNERSHIP_UTILS_H_
  
  #include <lib/fit/function.h>
  #include <lib/stdcompat/atomic.h>
  #include <lib/zx/channel.h>
  #include <lib/zx/thread.h>
  #include <lib/zx/time.h>
  #include <zircon/assert.h>
  #include <zircon/syscalls.h>
  #include <zircon/threads.h>
  #include <zircon/types.h>
  
  #include <atomic>
  
  #include <fbl/macros.h>
  
  // Allow up to 4 pointers worth of storage for any lambdas we need to capture
  // into a fit::inline_function object (instead of falling back on the implicit
  // heap allocation behavior of fit::function or std::function)
  static constexpr size_t kMaxLambdaStorage = sizeof(void*) * 4;
  
  // TODO(fxbug.dev/55744): An extremely long timeout we use as a proxy for "forever".
  // Someday, if the test framework ever gives us an environment specific timeout
  // to use as a soft watchdog threshold, we should switch to using that instead.
  static constexpr zx::duration kLongTimeout = zx::sec(60);
  
  // A small helper which allows us to poll with a timeout for a condition to
  // become true.  Sadly, some of the futex-ownership tests require this as there
  // is no opportunity for a thread which has become blocked on a futex to signal
  // another thread without unblocking.  When testing the state of the system
  // while we have a thread blocked via zx_futex_wait, the best we can do is have
  // the thread give us a signal, and then wait a "reasonable" amount of time for
  // the system to achieve the desired state (or not).
  using WaitFn = fit::inline_function<bool(void), kMaxLambdaStorage>;
  bool WaitFor(zx::duration timeout, WaitFn wait_fn);
  
  // A small helper which fetches the Koid for the current thread.
  zx_koid_t CurrentThreadKoid();
  
  // A lightweight signal based on an unowned futex which can be used to
  // block/unblock threads.  Used extensively in the futex ownership tests for
  // sequencing thread behavior which would typically just be a bunch of timing
  // races in real code.
  class Event {
   public:
    Event() = default;
    DISALLOW_COPY_ASSIGN_AND_MOVE(Event);
  
    zx_status_t Wait(zx::duration timeout);
    void Signal();
    void Reset();
  
   private:
    cpp20::atomic_ref<zx_futex_t> signaled() { return cpp20::atomic_ref(futex_state_); }
  
    zx_futex_t futex_state_ = 0;
  };
  
  // A lightweight wrapper for threads which allow us to create threads and have
  // then run a quick lambda, while automating much of the boilerplate we need for
  // the ownership tests (things like fetching a thread's KOID)
  class Thread {
   public:
    using Thunk = fit::inline_function<int(void), kMaxLambdaStorage>;
  
    enum class State : uint32_t {
      WAITING_TO_START,
      RUNNING,
      WAITING_TO_STOP,
      STOPPED,
    };
  
    Thread() { Reset(); }
    DISALLOW_COPY_ASSIGN_AND_MOVE(Thread);
  
    void Start(const char* name, Thunk thunk);
    zx_status_t Stop();
    zx_status_t GetRunState(uint32_t* run_state) const;
  
    const zx::thread& handle() const { return handle_; }
    zx_koid_t koid() const { return koid_; }
    State state() const { return state_.load(); }
  
   private:
    static constexpr zx::duration THREAD_POLL_INTERVAL = zx::msec(1);
  
    void SetState(State state) { state_.store(state); }
  
    void Reset();
  
    thrd_t thread_;
    zx::thread handle_;
    zx_koid_t koid_;
    Event started_evt_;
    Event stop_evt_;
    std::atomic<State> state_{State::STOPPED};
    Thunk thunk_;
  };
  
  // A small wrapper used to launch a process which creates a thread and sends us
  // a handle to the thread, then waits until we tell it to terminate.  This
  // allows us to test the requirement that a process is not allowed to declare a
  // thread from another process as the owner of one of its mutexes.
  class ExternalThread {
   public:
    static void SetProgramName(const char* program_name) { program_name_ = program_name; }
    static const char* ProgramName() { return program_name_; }
  
    static int DoHelperThread();
    static const char* helper_flag() { return helper_flag_; }
  
    ExternalThread() = default;
    ~ExternalThread() { Stop(); }
    DISALLOW_COPY_ASSIGN_AND_MOVE(ExternalThread);
  
    void Start();
    void Stop();
  
    const zx::thread& thread() const { return external_thread_; }
  
   private:
    static const char* program_name_;
    static const char* helper_flag_;
  
    zx::thread external_thread_;
    zx::channel control_channel_;
  };
  
  #endif  // SRC_ZIRCON_TESTS_FUTEX_OWNERSHIP_UTILS_H_
  