include/hermes/VM/WeakRefSlot.h (71 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef HERMES_VM_WEAKREFSLOT_H
#define HERMES_VM_WEAKREFSLOT_H
#include "hermes/VM/HermesValue.h"
namespace hermes {
namespace vm {
/// This is a single slot in the weak reference table. It contains a pointer to
/// a GC managed object. The GC will make sure it is updated when the object is
/// moved; if the object is garbage-collected, the pointer will be cleared.
class WeakRefSlot {
public:
/// State of this slot for the purpose of reusing slots.
enum State {
Unmarked = 0, /// Unknown whether this slot is in use by the mutator.
Marked, /// Proven to be in use by the mutator.
Free /// Proven to NOT be in use by the mutator.
};
// Mutator methods.
WeakRefSlot(HermesValue v) {
reset(v);
}
bool hasValue() const {
// An empty value means the pointer has been cleared, and a native value
// means it is free.
// Don't use state_ here since that can be modified concurrently by the GC.
assert(!value_.isNativeValue() && "Should never query a free WeakRef");
return !value_.isEmpty();
}
/// Return the object as a HermesValue.
const HermesValue value() const {
// Cannot check state() here because it can race with marking code.
assert(hasValue() && "tried to access collected referent");
return value_;
}
// GC methods to update slot when referent moves/dies.
/// Return true if this slot stores a non-null pointer to something. For any
/// slot reachable by the mutator, that something is a GCCell.
bool hasPointer() const {
return value_.isPointer();
}
/// Return the pointer to a GCCell, whether or not this slot is marked.
GCCell *getPointer() const {
// Cannot check state() here because it can race with marking code.
return static_cast<GCCell *>(value_.getPointer());
}
/// Update the stored pointer (because the object moved).
void setPointer(void *newPtr) {
// Cannot check state() here because it can race with marking code.
value_ = value_.updatePointer(newPtr);
}
/// Clear the pointer (because the object died).
void clearPointer() {
value_ = HermesValue::encodeEmptyValue();
}
// GC methods to recycle slots.
State state() const {
return state_;
}
void mark() {
assert(state() == Unmarked && "already marked");
state_ = Marked;
}
void unmark() {
assert(state() == Marked && "not yet marked");
state_ = Unmarked;
}
void free(WeakRefSlot *nextFree) {
assert(state() == Unmarked && "cannot free a reachable slot");
state_ = Free;
value_ = HermesValue::encodeNativePointer(nextFree);
assert(state() == Free);
}
WeakRefSlot *nextFree() const {
// nextFree is only called during a STW pause, so it's fine to access both
// state and value here.
assert(state() == Free);
return value_.getNativePointer<WeakRefSlot>();
}
/// Re-initialize a freed slot.
void reset(HermesValue v) {
static_assert(Unmarked == 0, "unmarked state should not need tagging");
state_ = Unmarked;
assert(v.isPointer() && "Only pointers are currently supported");
value_ = v;
assert(state() == Unmarked && "initial state should be unmarked");
}
private:
// value_ and state_ are read and written by different threads. We rely on
// them being independent words so that they can be used without
// synchronization.
PinnedHermesValue value_;
State state_;
};
using WeakSlotState = WeakRefSlot::State;
} // namespace vm
} // namespace hermes
#endif