/**************************************************************************/1/* safe_binary_mutex.h */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#pragma once3132#include "core/error/error_macros.h"33#include "core/os/mutex.h"34#include "core/typedefs.h"3536#ifdef THREADS_ENABLED3738GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wundefined-var-template")3940// A very special kind of mutex, used in scenarios where these41// requirements hold at the same time:42// - Must be used with a condition variable (only binary mutexes are suitable).43// - Must have recursive semnantics (or simulate, as this one does).44// The implementation keeps the lock count in TS. Therefore, only45// one object of each version of the template can exists; hence the Tag argument.46// Tags must be unique across the Godot codebase.47// Also, don't forget to declare the thread_local variable on each use.48template <int Tag>49class SafeBinaryMutex {50friend class MutexLock<SafeBinaryMutex<Tag>>;5152using StdMutexType = THREADING_NAMESPACE::mutex;5354mutable THREADING_NAMESPACE::mutex mutex;5556struct TLSData {57mutable THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock;58uint32_t count = 0;5960TLSData(SafeBinaryMutex<Tag> &p_mutex) :61lock(p_mutex.mutex, THREADING_NAMESPACE::defer_lock) {}62};63static thread_local TLSData tls_data;6465public:66_ALWAYS_INLINE_ void lock() const {67if (++tls_data.count == 1) {68tls_data.lock.lock();69}70}7172_ALWAYS_INLINE_ void unlock() const {73DEV_ASSERT(tls_data.count);74if (--tls_data.count == 0) {75tls_data.lock.unlock();76}77}7879_ALWAYS_INLINE_ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &_get_lock() const {80return const_cast<THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &>(tls_data.lock);81}8283_ALWAYS_INLINE_ SafeBinaryMutex() {84}8586_ALWAYS_INLINE_ ~SafeBinaryMutex() {87DEV_ASSERT(!tls_data.count);88}89};9091template <int Tag>92class MutexLock<SafeBinaryMutex<Tag>> {93friend class ConditionVariable;9495const SafeBinaryMutex<Tag> &mutex;9697public:98explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :99mutex(p_mutex) {100mutex.lock();101}102103~MutexLock() {104mutex.unlock();105}106107_ALWAYS_INLINE_ void temp_relock() const {108mutex.lock();109}110111_ALWAYS_INLINE_ void temp_unlock() const {112mutex.unlock();113}114115// TODO: Implement a `try_temp_relock` if needed (will also need a dummy method below).116};117118GODOT_CLANG_WARNING_POP119120#else // No threads.121122template <int Tag>123class SafeBinaryMutex {124struct TLSData {125TLSData(SafeBinaryMutex<Tag> &p_mutex) {}126};127static thread_local TLSData tls_data;128129public:130void lock() const {}131void unlock() const {}132};133134template <int Tag>135class MutexLock<SafeBinaryMutex<Tag>> {136public:137MutexLock(const SafeBinaryMutex<Tag> &p_mutex) {}138~MutexLock() {}139140void temp_relock() const {}141void temp_unlock() const {}142};143144#endif // THREADS_ENABLED145146147