blob: 31269fc94fa48540b53883456dcb4f549495be8c [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MAC_WORKGROUP_H_
#define BASE_MAC_WORKGROUP_H_
#include <mach/mach.h>
#include <stdint.h>
#include <atomic>
#include <memory>
#include "base/apple/scoped_mach_port.h"
#include "base/base_export.h"
#include "base/feature_list.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/task/thread_type.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
namespace base::mac {
BASE_EXPORT BASE_DECLARE_FEATURE(kMacWorkgroupInterval);
// Thread-safe value handle around macOS os_workgroup_t (retained/released via
// libdispatch).
//
// Cooperating threads across processes can join the same Workgroup via Mach
// port transfer. A thread may belong to at most one os_workgroup_t at a time
// and must leave before exiting; join tokens are tracked in TLS and cleaned up
// automatically on thread exit.
class BASE_EXPORT Workgroup {
public:
// Imports a workgroup from a Mach send right (see CopyPort()). Does not
// consume `port`. Returns an empty Workgroup if unsupported or disabled.
static Workgroup CreateFromPort(const char* name, mach_port_t port);
Workgroup() = default;
Workgroup(const Workgroup& other);
Workgroup(Workgroup&& other) noexcept;
Workgroup& operator=(Workgroup other) noexcept;
~Workgroup();
explicit operator bool() const { return workgroup_ != nullptr; }
// Compares the underlying os_workgroup_t instance pointer (required by
// libdispatch when matching leave to join or checking interval ownership).
bool operator==(const Workgroup& other) const {
return workgroup_ == other.workgroup_;
}
apple::ScopedMachSendRight CopyPort() const;
// Joins the current thread to `workgroup` (leaving any previous non-interval
// Workgroup first; if joining the replacement fails, the thread remains
// unjoined). Passing an empty Workgroup leaves. Explicit joins made here are
// not overwritten by MaybeSyncCurrentThreadToForegroundWorkgroup. Returns
// false if the thread owns a live WorkgroupInterval or if os_workgroup_join
// fails (e.g. EALREADY from an external framework, whose membership is left
// untouched).
static bool JoinCurrentThread(Workgroup workgroup);
static void LeaveCurrentThread();
// Returns the Workgroup the current thread is joined to, or empty if none.
static Workgroup GetCurrentThreadWorkgroup();
private:
friend class WorkgroupInterval;
friend struct ProcessSync;
struct TlsImpl;
Workgroup(void* workgroup, mach_port_t port);
// Unlike operator==, also matches two handles imported via separate
// CreateFromPort() calls in this task that wrap the same coalesced Mach
// send-right name (`port_`). Each CreateFromPort() allocates a distinct
// os_workgroup_t (`*this != other`), so use SameBackingAs() to skip redundant
// leave/rejoin when already targeting the same kernel workgroup, and use
// operator== when checking the exact joined os_workgroup_t instance.
bool SameBackingAs(const Workgroup& other) const {
return workgroup_ == other.workgroup_ ||
(port_ != MACH_PORT_NULL && port_ == other.port_);
}
// Opaque os_workgroup_t managed by os_retain/os_release; kept as void* so
// .cc and .mm translation units see the same layout.
RAW_PTR_EXCLUSION void* workgroup_ = nullptr;
// Send-right name in mach_task_self() when created via CreateFromPort(), kept
// alive by `workgroup_` and used by SameBackingAs() for port deduplication.
mach_port_t port_ = MACH_PORT_NULL;
};
// Excludes background/utility threads as well as audio threads whose cadences
// are independent of display VSyncs.
constexpr bool ShouldJoinProcessForegroundWorkgroup(ThreadType thread_type) {
return thread_type == ThreadType::kDefault ||
thread_type == ThreadType::kPresentation;
}
// Sets (or clears) the Workgroup that eligible SequenceManager threads and
// ThreadPool workers in this process join while enabled. Thread-safe.
BASE_EXPORT void SetProcessForegroundWorkgroup(Workgroup workgroup);
// Enables or suspends joining the configured process foreground workgroup
// (enabled by default). When false, threads joined via foreground sync leave on
// their next wakeup; explicit JoinCurrentThread() memberships are unaffected.
BASE_EXPORT void SetProcessForegroundWorkgroupEnabled(bool enabled);
BASE_EXPORT void ResetProcessForegroundWorkgroupForTesting();
namespace internal {
// Bumped when the effective foreground workgroup changes; stays 0 when unused.
BASE_EXPORT extern std::atomic<uint32_t> g_foreground_workgroup_generation;
BASE_EXPORT void SyncCurrentThreadToForegroundWorkgroupSlow(
uint32_t& local_generation);
} // namespace internal
// Syncs the calling thread's membership when the process foreground workgroup
// generation changes; `local_generation` is per-thread state initialized to 0.
inline void MaybeSyncCurrentThreadToForegroundWorkgroup(
uint32_t& local_generation) {
const uint32_t target = internal::g_foreground_workgroup_generation.load(
std::memory_order_acquire);
if (local_generation != target) [[unlikely]] {
internal::SyncCurrentThreadToForegroundWorkgroupSlow(local_generation);
}
}
// Thread-affine RAII wrapper around os_workgroup_interval_t. Create() joins the
// calling thread to the interval's Workgroup (returning nullptr if unsupported,
// disabled, or unable to join), and ~WorkgroupInterval() finishes any active
// interval and leaves the Workgroup on that same thread.
class BASE_EXPORT WorkgroupInterval {
public:
static bool IsSupported();
static std::unique_ptr<WorkgroupInterval> Create(const char* name);
WorkgroupInterval(const WorkgroupInterval&) = delete;
WorkgroupInterval& operator=(const WorkgroupInterval&) = delete;
~WorkgroupInterval();
const Workgroup& workgroup() const { return workgroup_; }
apple::ScopedMachSendRight CopyPort() const { return workgroup_.CopyPort(); }
bool is_started() const { return is_started_; }
// Starts an interval (finishing any active one first). Both `start` and
// `deadline` must be non-null with `start <= deadline`; `start` is clamped to
// <= now in Mach ticks so slightly-ahead VSync timestamps do not fail.
bool Start(TimeTicks start, TimeTicks deadline);
// Updates the active interval's deadline (`deadline` must be non-null).
bool Update(TimeTicks deadline);
// Finishes the active interval; no-op returning true if not started.
bool Finish();
private:
explicit WorkgroupInterval(Workgroup workgroup);
Workgroup workgroup_;
uint64_t current_start_mach_ = 0;
bool is_started_ = false;
THREAD_CHECKER(thread_checker_);
};
} // namespace base::mac
#endif // BASE_MAC_WORKGROUP_H_