Files
TheRock/rocm-libraries/projects/hipthreads/inc/hip/mutex
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

438 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// -*- 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
//
//===----------------------------------------------------------------------===//
// Modifications Copyright (c) 2025 Advanced Micro Devices, Inc.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef __LIBHIPTHREADS_MUTEX___
#define __LIBHIPTHREADS_MUTEX___
/*
mutex synopsis
namespace std
{
class mutex
{
public:
constexpr mutex() noexcept;
~mutex();
mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;
void lock();
bool try_lock();
void unlock();
typedef pthread_mutex_t* native_handle_type;
native_handle_type native_handle();
};
class recursive_mutex
{
public:
recursive_mutex();
~recursive_mutex();
recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;
void lock();
bool try_lock() noexcept;
void unlock();
typedef pthread_mutex_t* native_handle_type;
native_handle_type native_handle();
};
class timed_mutex
{
public:
timed_mutex();
~timed_mutex();
timed_mutex(const timed_mutex&) = delete;
timed_mutex& operator=(const timed_mutex&) = delete;
void lock();
bool try_lock();
template <class Rep, class Period>
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
template <class Clock, class Duration>
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
void unlock();
};
class recursive_timed_mutex
{
public:
recursive_timed_mutex();
~recursive_timed_mutex();
recursive_timed_mutex(const recursive_timed_mutex&) = delete;
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;
void lock();
bool try_lock() noexcept;
template <class Rep, class Period>
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
template <class Clock, class Duration>
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
void unlock();
};
struct defer_lock_t { explicit defer_lock_t() = default; };
struct try_to_lock_t { explicit try_to_lock_t() = default; };
struct adopt_lock_t { explicit adopt_lock_t() = default; };
inline constexpr defer_lock_t defer_lock{};
inline constexpr try_to_lock_t try_to_lock{};
inline constexpr adopt_lock_t adopt_lock{};
template <class Mutex>
class lock_guard
{
public:
typedef Mutex mutex_type;
explicit lock_guard(mutex_type& m);
lock_guard(mutex_type& m, adopt_lock_t);
~lock_guard();
lock_guard(lock_guard const&) = delete;
lock_guard& operator=(lock_guard const&) = delete;
};
template <class... MutexTypes>
class scoped_lock // C++17
{
public:
using mutex_type = Mutex; // Only if sizeof...(MutexTypes) == 1
explicit scoped_lock(MutexTypes&... m);
scoped_lock(adopt_lock_t, MutexTypes&... m);
~scoped_lock();
scoped_lock(scoped_lock const&) = delete;
scoped_lock& operator=(scoped_lock const&) = delete;
private:
tuple<MutexTypes&...> pm; // exposition only
};
template <class Mutex>
class unique_lock
{
public:
typedef Mutex mutex_type;
unique_lock() noexcept;
explicit unique_lock(mutex_type& m);
unique_lock(mutex_type& m, defer_lock_t) noexcept;
unique_lock(mutex_type& m, try_to_lock_t);
unique_lock(mutex_type& m, adopt_lock_t);
template <class Clock, class Duration>
unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
template <class Rep, class Period>
unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);
~unique_lock();
unique_lock(unique_lock const&) = delete;
unique_lock& operator=(unique_lock const&) = delete;
unique_lock(unique_lock&& u) noexcept;
unique_lock& operator=(unique_lock&& u) noexcept;
void lock();
bool try_lock();
template <class Rep, class Period>
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
template <class Clock, class Duration>
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
void unlock();
void swap(unique_lock& u) noexcept;
mutex_type* release() noexcept;
bool owns_lock() const noexcept;
explicit operator bool () const noexcept;
mutex_type* mutex() const noexcept;
};
template <class Mutex>
void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;
template <class L1, class L2, class... L3>
int try_lock(L1&, L2&, L3&...);
template <class L1, class L2, class... L3>
void lock(L1&, L2&, L3&...);
struct once_flag
{
constexpr once_flag() noexcept;
once_flag(const once_flag&) = delete;
once_flag& operator=(const once_flag&) = delete;
};
template<class Callable, class ...Args>
void call_once(once_flag& flag, Callable&& func, Args&&... args);
} // std
*/
#include "hip/thread_config"
#include <cstdint>
#include <mutex>
// #include "hip/__condition_variable/condition_variable.h"
// #include "hip/__memory/shared_ptr.h"
#include "hip/__mutex/lock_guard.h"
#include "hip/__mutex/spin_mutex.h"
#include "hip/__mutex/unique_lock.h"
/**
* @file
* @brief Multi-mutex locking helpers (device-side analogues of `std::try_lock` / `std::lock`).
* @ingroup mutex
*
* Provides overloads of:
* - try_lock(m1, m2[, ...]) : Attempts to acquire all mutexes without indefinite blocking.
* Returns -1 on success (all locked) or the (0-based) index of the first mutex
* that could not be locked. On failure, all successfully locked earlier mutexes
* are unlocked before return.
* - lock(m1, m2[, ...]) : Acquires all mutexes (spinning) using a deadlock‑avoidance
* strategy that alternates starting points until success.
*
* Implementation strategy (adapted from libc++):
* - try_lock variants use RAII (unique_lock) for early resources and release on failure.
* - lock variants loop, attempting acquisition order permutations until all succeed.
*
* Notes:
* - Intended for short critical sections (spin semantics).
* - Mutex types must model BasicLockable (lock(), try_lock(), unlock()).
* - Overloads with >2 locks are variadic templates; helpers (__lock_first / __unlock)
* are internal.
*/
namespace cuda {
/**
* @brief try_lock for two mutex-like objects.
*
* Attempts to lock @p __l0 (non-blocking) then @p __l1 (non-blocking).
* On success both are locked and -1 is returned. On failure:
* - Return 0 if @p __l0 could not be locked.
* - Return 1 if @p __l0 locked but @p __l1 failed (and @p __l0 is released).
*
* @return int Index of the first mutex that failed, or -1 if all locked.
*/
template <class _L0, class _L1>
__device__ _LIBHIPTHREADS_HIDE_FROM_ABI int
try_lock(_L0& __l0, _L1& __l1)
{
unique_lock<_L0> __u0(__l0, ::std::try_to_lock);
if (__u0.owns_lock())
{
if (__l1.try_lock())
{
__u0.release();
return -1;
}
else
return 1;
}
return 0;
}
#ifndef _LIBHIPTHREADS_CXX03_LANG
/**
* @brief try_lock for N >= 3 mutex-like objects.
*
* Attempts to lock the first mutex then recursively tries the remainder.
* On failure the already-locked subset is released (strong exception / failure safety).
*
* @return int Index (0-based) of first mutex that failed, or -1 if all locked.
*/
template <class _L0, class _L1, class _L2, class... _L3>
__device__ _LIBHIPTHREADS_HIDE_FROM_ABI int
try_lock(_L0& __l0, _L1& __l1, _L2& __l2, _L3&... __l3)
{
int __r = 0;
unique_lock<_L0> __u0(__l0, ::std::try_to_lock);
if (__u0.owns_lock())
{
__r = hip::try_lock(__l1, __l2, __l3...);
if (__r == -1)
__u0.release();
else
++__r;
}
return __r;
}
#endif // _LIBHIPTHREADS_CXX03_LANG
/**
* @brief lock for two mutex-like objects (deadlock avoidance).
*
* Repeatedly attempts to acquire both without holding them simultaneously on failure,
* alternating acquisition order until both succeed.
*/
template <class _L0, class _L1>
__device__ _LIBHIPTHREADS_HIDE_FROM_ABI void
lock(_L0& __l0, _L1& __l1)
{
while (true)
{
{
unique_lock<_L0> __u0(__l0);
if (__l1.try_lock())
{
__u0.release();
break;
}
}
// __libcpp_thread_yield();
{
unique_lock<_L1> __u1(__l1);
if (__l0.try_lock())
{
__u1.release();
break;
}
}
// __libcpp_thread_yield();
}
}
#ifndef _LIBHIPTHREADS_CXX03_LANG
/**
* @internal
* @brief Recursive helper that acquires all mutexes using a rotating start index.
* @param __i Starting index permutation.
*/
template <class _L0, class _L1, class _L2, class ..._L3>
__device__ void
__lock_first(int __i, _L0& __l0, _L1& __l1, _L2& __l2, _L3& ...__l3)
{
while (true)
{
switch (__i)
{
case 0:
{
unique_lock<_L0> __u0(__l0);
__i = hip::try_lock(__l1, __l2, __l3...);
if (__i == -1)
{
__u0.release();
return;
}
}
++__i;
// __libcpp_thread_yield();
break;
case 1:
{
unique_lock<_L1> __u1(__l1);
__i = hip::try_lock(__l2, __l3..., __l0);
if (__i == -1)
{
__u1.release();
return;
}
}
if (__i == sizeof...(_L3) + 1)
__i = 0;
else
__i += 2;
// __libcpp_thread_yield();
break;
default:
hip::__lock_first(__i - 2, __l2, __l3..., __l0, __l1);
return;
}
}
}
/**
* @brief lock for N >= 3 mutex-like objects.
*
* Employs permutation / rotation strategy to avoid classic deadlock when multiple
* threads attempt to lock the same set in different orders.
*
* Spins until all locks are held.
*/
template <class _L0, class _L1, class _L2, class ..._L3>
__device__ inline _LIBHIPTHREADS_INLINE_VISIBILITY
void
lock(_L0& __l0, _L1& __l1, _L2& __l2, _L3& ...__l3)
{
hip::__lock_first(0, __l0, __l1, __l2, __l3...);
}
/**
* @internal
* @brief Unlock helper (single).
*/
template <class _L0>
__device__ inline _LIBHIPTHREADS_INLINE_VISIBILITY
void __unlock(_L0& __l0) {
__l0.unlock();
}
/**
* @internal
* @brief Unlock helper (two).
*/
template <class _L0, class _L1>
__device__ inline _LIBHIPTHREADS_INLINE_VISIBILITY
void __unlock(_L0& __l0, _L1& __l1) {
__l0.unlock();
__l1.unlock();
}
/**
* @internal
* @brief Variadic unlock helper (N >= 3).
*/
template <class _L0, class _L1, class _L2, class ..._L3>
__device__ inline _LIBHIPTHREADS_INLINE_VISIBILITY
void __unlock(_L0& __l0, _L1& __l1, _L2& __l2, _L3&... __l3) {
__l0.unlock();
__l1.unlock();
hip::__unlock(__l2, __l3...);
}
#endif // _LIBHIPTHREADS_CXX03_LANG
} // namespace cuda
#endif // __LIBHIPTHREADS_MUTEX___