docs
Docs
C++ API
C API
Search API
Ctrl K
2.0
2.0
latest
1.4
Ultralight C++ API
2.0.0
Toggle main menu visibility
Loading...
Searching...
No Matches
Lock.h
Go to the documentation of this file.
1
///
2
/// Copyright (C) 2026 Ultralight, Inc. All rights reserved.
3
/// A license is required for commercial use. https://ultralig.ht
4
///
5
#pragma once
6
#include <
Ultralight/Defines.h
>
7
#include <atomic>
8
#include <mutex>
// for std::lock_guard<>
9
10
namespace
ultralight
{
11
12
///
13
/// Lightweight mutex optimized for short locking periods.
14
///
15
/// Uncontended lock and unlock are a single inline atomic operation. Contended waiters spin
16
/// briefly, then block in the OS until the lock is released (no CPU is consumed while waiting).
17
///
18
/// Can be used in place of std::mutex since it implements the STL's Lockable interface.
19
///
20
/// @note This lock is not recursive: locking it again from the thread that already holds it
21
/// will deadlock. Debug builds assert when this is attempted.
22
///
23
class
Lock
{
24
public
:
25
constexpr
Lock
() =
default
;
26
27
UL_ALWAYS_INLINE
void
lock
() noexcept {
28
// CAS rather than exchange: an exchange would overwrite kLockedContended and lose the
29
// pending-waiter state, so a blocked waiter would never be woken.
30
uint32_t expected =
kUnlocked
;
31
if
(!
state_
.compare_exchange_strong(expected,
kLocked
, std::memory_order_acquire,
32
std::memory_order_relaxed))
33
contended_lock
();
34
#ifndef NDEBUG
35
owner_
.store(
current_thread_id
(), std::memory_order_relaxed);
36
#endif
37
}
38
39
UL_ALWAYS_INLINE
bool
try_lock
() noexcept {
40
// First do a relaxed load to check if lock is free in order to prevent
41
// unnecessary cache misses if someone does while(!try_lock())
42
if
(
state_
.load(std::memory_order_relaxed) !=
kUnlocked
)
43
return
false
;
44
uint32_t expected =
kUnlocked
;
45
if
(!
state_
.compare_exchange_strong(expected,
kLocked
, std::memory_order_acquire,
46
std::memory_order_relaxed))
47
return
false
;
48
#ifndef NDEBUG
49
owner_
.store(
current_thread_id
(), std::memory_order_relaxed);
50
#endif
51
return
true
;
52
}
53
54
UL_ALWAYS_INLINE
void
unlock
() noexcept {
55
#ifndef NDEBUG
56
// Clear before releasing the lock word so the next holder never observes a stale owner.
57
owner_
.store(0, std::memory_order_relaxed);
58
#endif
59
if
(
state_
.exchange(
kUnlocked
, std::memory_order_release) ==
kLockedContended
)
60
contended_unlock
();
61
}
62
63
protected
:
64
Lock
(
const
Lock
&) =
delete
;
65
Lock
&
operator=
(
const
Lock
&) =
delete
;
66
67
// Lock word states. kLockedContended means a waiter may be blocked in the OS, so unlock()
68
// must issue a wake.
69
static
constexpr
uint32_t
kUnlocked
= 0;
70
static
constexpr
uint32_t
kLocked
= 1;
71
static
constexpr
uint32_t
kLockedContended
= 2;
72
73
UExport
void
contended_lock
() noexcept;
74
UExport
void
contended_unlock
() noexcept;
75
UExport
static uint32_t
current_thread_id
() noexcept;
76
77
std
::atomic<uint32_t>
state_
= {
kUnlocked
};
78
// Diagnostic only: thread id of the current holder, checked to assert on recursive locking.
79
// Written only by debug builds, but present in all builds so the layout of Lock (and of
80
// public types that embed it) does not vary with the consumer's build configuration.
81
std::atomic<uint32_t>
owner_
= { 0 };
82
};
83
84
using
LockHolder
= std::lock_guard<Lock>;
85
86
}
// namespace ultralight
UExport
#define UExport
Definition
Exports.h:22
Defines.h
UL_ALWAYS_INLINE
#define UL_ALWAYS_INLINE
Definition
Defines.h:70
ultralight::Lock::state_
std::atomic< uint32_t > state_
Definition
Lock.h:77
ultralight::Lock::Lock
constexpr Lock()=default
ultralight::Lock::contended_unlock
void contended_unlock() noexcept
ultralight::Lock::unlock
UL_ALWAYS_INLINE void unlock() noexcept
Definition
Lock.h:54
ultralight::Lock::lock
UL_ALWAYS_INLINE void lock() noexcept
Definition
Lock.h:27
ultralight::Lock::contended_lock
void contended_lock() noexcept
ultralight::Lock::try_lock
UL_ALWAYS_INLINE bool try_lock() noexcept
Definition
Lock.h:39
ultralight::Lock::Lock
Lock(const Lock &)=delete
ultralight::Lock::kUnlocked
static constexpr uint32_t kUnlocked
Definition
Lock.h:69
ultralight::Lock::current_thread_id
static uint32_t current_thread_id() noexcept
ultralight::Lock::owner_
std::atomic< uint32_t > owner_
Definition
Lock.h:81
ultralight::Lock::operator=
Lock & operator=(const Lock &)=delete
ultralight::Lock::kLocked
static constexpr uint32_t kLocked
Definition
Lock.h:70
ultralight::Lock::kLockedContended
static constexpr uint32_t kLockedContended
Definition
Lock.h:71
std
Definition
StringSTL.h:166
ultralight
Root namespace for every public Ultralight type, function, and enumeration.
ultralight::LockHolder
std::lock_guard< Lock > LockHolder
Definition
Lock.h:84
Ultralight
Lock.h
Docs
C++ API
C API
Version
2.0
2.0
latest
1.4