blob: fd5a8b11290ddc529eecee42866ac54044fa1aed [file] [log] [blame]
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_JS_ATOMICS_SYNCHRONIZATION_H_
#define V8_OBJECTS_JS_ATOMICS_SYNCHRONIZATION_H_
#include <atomic>
#include "src/execution/thread-id.h"
#include "src/objects/js-objects.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
#include "torque-generated/src/objects/js-atomics-synchronization-tq.inc"
namespace detail {
class WaiterQueueNode;
} // namespace detail
// A non-recursive mutex that is exposed to JS.
//
// It has the following properties:
// - Slim: 8-12 bytes. Lock state is 4 bytes when
// V8_SANDBOXED_EXTERNAL_POINTERS, and sizeof(void*) otherwise. Owner
// thread is an additional 4 bytes.
// - Fast when uncontended: a single weak CAS.
// - Possibly unfair under contention.
// - Moving GC safe. It uses an index into the shared Isolate's external
// pointer table to store a queue of sleeping threads.
// - Parks the main thread LocalHeap when the thread is blocked on acquiring
// the lock. Unparks the main thread LocalHeap when unblocked. This means
// that the lock can only be used with main thread isolates (including
// workers) but not with helper threads that have their own LocalHeap.
//
// This mutex manages its own queue of waiting threads under contention, i.e. a
// it implements a futex in userland. The algorithm is inspired by WebKit's
// ParkingLot.
class JSAtomicsMutex
: public TorqueGeneratedJSAtomicsMutex<JSAtomicsMutex, JSObject> {
public:
DECL_CAST(JSAtomicsMutex)
DECL_PRINTER(JSAtomicsMutex)
EXPORT_DECL_VERIFIER(JSAtomicsMutex)
V8_EXPORT_PRIVATE static Handle<JSAtomicsMutex> Create(Isolate* isolate);
// Lock the mutex, blocking if it's currently owned by another thread.
static inline void Lock(Isolate* requester, Handle<JSAtomicsMutex> mutex);
V8_WARN_UNUSED_RESULT inline bool TryLock();
inline void Unlock(Isolate* requester);
inline bool IsHeld();
inline bool IsCurrentThreadOwner();
static constexpr int kEndOfTaggedFieldsOffset = JSObject::kHeaderSize;
class BodyDescriptor;
TQ_OBJECT_CONSTRUCTORS(JSAtomicsMutex)
private:
friend class detail::WaiterQueueNode;
// There are 2 lock bits: whether the lock itself is locked, and whether the
// associated waiter queue is locked.
static constexpr int kIsLockedBit = 1 << 0;
static constexpr int kIsWaiterQueueLockedBit = 1 << 1;
static constexpr int kLockBitsSize = 2;
#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
using StateT = uint32_t;
static_assert(sizeof(StateT) == kExternalPointerSize);
#else
using StateT = uintptr_t;
#endif
static constexpr StateT kUnlocked = 0;
static constexpr StateT kLockedUncontended = 1;
static constexpr StateT kLockBitsMask = (1 << kLockBitsSize) - 1;
static constexpr StateT kWaiterQueueHeadMask = ~kLockBitsMask;
inline void SetCurrentThreadAsOwner();
inline void ClearOwnerThread();
inline std::atomic<StateT>* AtomicStatePtr();
inline std::atomic<int32_t>* AtomicOwnerThreadIdPtr();
bool TryLockExplicit(std::atomic<StateT>* state, StateT& expected);
bool TryLockWaiterQueueExplicit(std::atomic<StateT>* state, StateT& expected);
V8_EXPORT_PRIVATE static void LockSlowPath(Isolate* requester,
Handle<JSAtomicsMutex> mutex,
std::atomic<StateT>* state);
V8_EXPORT_PRIVATE void UnlockSlowPath(Isolate* requester,
std::atomic<StateT>* state);
using TorqueGeneratedJSAtomicsMutex<JSAtomicsMutex, JSObject>::state;
using TorqueGeneratedJSAtomicsMutex<JSAtomicsMutex, JSObject>::set_state;
using TorqueGeneratedJSAtomicsMutex<JSAtomicsMutex,
JSObject>::owner_thread_id;
using TorqueGeneratedJSAtomicsMutex<JSAtomicsMutex,
JSObject>::set_owner_thread_id;
};
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_ATOMICS_SYNCHRONIZATION_H_