Files
TheRock/rocm-libraries/projects/hipthreads/inc/hip/__mutex/spin_mutex.h
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

156 lines
7.6 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++ -*-
// 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_SPIN_MUTEX_H__
#define __LIBHIPTHREADS___MUTEX_SPIN_MUTEX_H__
#include "hip/thread_config"
#include "hip/hip_runtime.h"
#include "hip/__support/misuse.h"
#include <cassert>
#include <cstdint>
/**
* @file
* @brief Busy‑wait (spin) mutual exclusion primitive for short critical sections.
* @ingroup mutex
*/
namespace cuda {
//====================================================================================================================//
// Adapted from libc++ ::std::mutex
//====================================================================================================================//
// TODO: add a scope paramter
/**
* @class spin_mutex
* @brief Busy‑wait (spin) non‑recursive mutex
* @ingroup mutex
*
* Characteristics:
* - Exclusive, non‑recursive ownership (re‑entering is UB / debug assert).
* - lock() spins with atomicCAS until acquired.
* - try_lock() single non‑blocking attempt.
* - unlock() issues a release fence (__threadfence) then clears ownership.
*
* GPU specifics:
* - It is illegal for more than one fiber to be active while acquiring the lock
* (debug assert or livelock). This results in the extra fibers looping, trying
* to acquire the lock when it's already owned by a fiber in the same SIMD
* wave/hip::wthread.
* - Ownership is tracked using the block id so that we can attempt to detect
* this invalid use in debug and assert instead of livelocking.
* - Prolonged contention wastes execution resources (avoid long critical
* sections).
*
* Use RAII helpers (lock_guard / unique_lock) for safer scope management.
*
* Not copyable or movable.
*/
class _LIBHIPTHREADS_TYPE_VIS _LIBHIPTHREADS_THREAD_SAFETY_ANNOTATION(capability("spin_mutex")) spin_mutex {
// HIP requires that (gridDim * blockDim) < 2^32
enum : uint64_t { INVALID_OWNER = -1ULL };
uint64_t owner = INVALID_OWNER; // stores the owner's blockId
public:
/// Constructs an unlocked spin_mutex.
__device__ _LIBHIPTHREADS_HIDE_FROM_ABI _LIBHIPTHREADS_CONSTEXPR spin_mutex() = default;
/// \name Deleted copy / move operations
/// Instances are neither copyable nor movable.
///@{
__device__ spin_mutex(const spin_mutex &) = delete;
__device__ spin_mutex &operator=(const spin_mutex &) = delete;
///@}
__device__ _LIBHIPTHREADS_HIDE_FROM_ABI ~spin_mutex() = default;
/**
* @brief Acquires the mutex (spins until success).
*
* Uses atomicCAS with acquire semantics to claim the owner slot.
* Undefined behavior (asserts in debug) if the same block attempts to
* re‑enter (non‑recursive) or more than one fiber is active while acquiring..
*/
__device__ void lock() _LIBHIPTHREADS_THREAD_SAFETY_ANNOTATION(acquire_capability()) {
const uint64_t myBlockId = blockIdx.x + gridDim.x*blockIdx.y + gridDim.x*gridDim.y*blockIdx.z;
// The read operation here by atomicCAS counts as an atomic acquire operation, which the 'release fence'
// operation in unlock() synchronizes-with.
for (uint64_t ownerBlockId = atomicCAS(&owner, INVALID_OWNER, myBlockId); ownerBlockId != INVALID_OWNER;
ownerBlockId = atomicCAS(&owner, INVALID_OWNER, myBlockId)) {
// Since execution only continues past the loop once ALL threads in a wave have exited the loop,
// we need to prevent an entire wave from spinning waiting for one of it's own threads to release the lock.
//
// Technically this is more strict than we need to be, because we're doing this at a block level and not a
// wave level, but that's fine.
__HIPTHREADS_ASSERT(ownerBlockId != myBlockId,
"recursive lock of a non-recursive hip::spin_mutex: a block already "
"holding the lock tried to acquire it again (would livelock).");
}
}
// Note that prior calls to lock() do NOT synchronize-with try_lock if it returns false! In otherwords, there is
//no memory order relationship between lock and try_lock or between try_lock and itself. Only unlock() establishes
//any memory order relationship. (see https://en.cppreference.com/w/cpp/thread/mutex/try_lock)
/**
* @brief Attempts to acquire without spinning.
* @return true if ownership obtained; false if already locked.
*
* No memory ordering relationship is created with prior failed try_lock
* attempts or successful lock() calls that did not transfer ownership to
* this block (mirrors std::mutex semantics).
*/
__device__ bool try_lock() _NOEXCEPT _LIBHIPTHREADS_THREAD_SAFETY_ANNOTATION(try_acquire_capability(true)) {
const uint64_t myBlockId = blockIdx.x + gridDim.x*blockIdx.y + gridDim.x*gridDim.y*blockIdx.z;
// The read operation here by atomicCAS counts as an atomic acquire operation, which the 'release fence'
// operation in unlock() synchronizes-with.
return atomicCAS(&owner, INVALID_OWNER, myBlockId) == INVALID_OWNER;
}
/**
* @brief Releases ownership.
*
* Performs a release fence ensuring all writes inside the critical section
* become visible to a subsequent acquiring block before clearing owner.
* Debug builds assert the current block owns the mutex.
*/
__device__ void unlock() _NOEXCEPT _LIBHIPTHREADS_THREAD_SAFETY_ANNOTATION(release_capability()) {
// Create a 'release fence' operation which synchronizes-with the atomic acquire operation in lock() and
// try_lock(). This establishes the synchronizes-with relationship required by the C++ standard:
// - Unlock "synchronizes-with any subsequent lock operation that obtains ownership of the same mutex"
// (see https://en.cppreference.com/w/cpp/thread/mutex/unlock)
// In other words, we make sure all writes in the critical section are visible to other threads when they
// acquire the mutex.
__threadfence();
// This counts as the atomic store operation 'X' described in "Fence-atomic synchronization" at
// https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence.
uint64_t oldOwner = atomicExch(&owner, INVALID_OWNER);
__HIPTHREADS_ASSERT(oldOwner == blockIdx.x + gridDim.x * blockIdx.y + gridDim.x * gridDim.y * blockIdx.z,
"unlock of a hip::spin_mutex by a block that does not own it (corrupts the "
"owner state of whichever block did own it).");
}
};
} // namespace cuda
#endif // __LIBHIPTHREADS___MUTEX_SPIN_MUTEX_H__