ext/Internal/extension-object.cpp (88 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "extension-object.h"
#include "api-handle.h"
#include "capi-state.h"
#include "capi-typeslots.h"
#include "capi.h"
#include "linked-list.h"
#include "object.h"
#include "runtime.h"
#include "scavenger.h"
namespace py {
void disposeExtensionObjects(Runtime* runtime) {
CAPIState* state = capiState(runtime);
while (ListEntry* entry = state->extension_objects) {
untrackExtensionObject(runtime, entry);
std::free(entry);
}
}
void finalizeExtensionObject(Thread* thread, RawObject object) {
HandleScope scope(thread);
Runtime* runtime = thread->runtime();
NativeProxy proxy(&scope, object);
Type type(&scope, runtime->typeOf(*proxy));
DCHECK(type.hasNativeData(),
"A native instance must come from an extension type");
destructor tp_dealloc =
reinterpret_cast<destructor>(typeSlotAt(type, Py_tp_dealloc));
DCHECK(tp_dealloc != nullptr, "Extension types must have a dealloc slot");
ApiHandle* handle = ApiHandle::fromPyObject(
reinterpret_cast<PyObject*>(Int::cast(proxy.native()).asCPtr()));
CHECK(handle->refcnt() == 1,
"The runtime must hold the last reference to the PyObject* (%p). "
"Expecting a refcount of 1, but found %ld\n",
reinterpret_cast<void*>(handle), handle->refcnt());
handle->setRefcnt(0);
handle->setBorrowedNoImmediate();
(*tp_dealloc)(handle);
if (!proxy.native().isNoneType() && handle->refcnt() == 0) {
// `proxy.native()` being `None` indicates the extension object memory was
// not freed. `ob_refcnt == 0` means the object was not resurrected.
// This typically indicates that the user maintains a free-list and wants to
// call `PyObject_Init` on the memory again, we have to untrack it!
ListEntry* entry = reinterpret_cast<ListEntry*>(handle) - 1;
untrackExtensionObject(runtime, entry);
}
}
PyObject* initializeExtensionObject(Thread* thread, PyObject* obj,
PyTypeObject* typeobj,
const Object& instance) {
Runtime* runtime = thread->runtime();
HandleScope scope(thread);
NativeProxy proxy(&scope, *instance);
proxy.setNative(runtime->newIntFromCPtr(obj));
trackExtensionObject(runtime, reinterpret_cast<ListEntry*>(obj) - 1);
// Initialize the native object
obj->reference_ = proxy.raw();
Py_INCREF(typeobj);
obj->ob_refcnt = 2;
return obj;
}
word numExtensionObjects(Runtime* runtime) {
return capiState(runtime)->num_extension_objects;
}
bool trackExtensionObject(Runtime* runtime, ListEntry* entry) {
CAPIState* state = capiState(runtime);
bool did_insert = listEntryInsert(entry, &state->extension_objects);
if (did_insert) state->num_extension_objects++;
return did_insert;
}
bool untrackExtensionObject(Runtime* runtime, ListEntry* entry) {
CAPIState* state = capiState(runtime);
bool did_remove = listEntryRemove(entry, &state->extension_objects);
if (did_remove) state->num_extension_objects--;
return did_remove;
}
void visitExtensionObjects(Runtime* runtime, Scavenger* scavenger,
PointerVisitor* visitor) {
CAPIState* state = capiState(runtime);
for (ListEntry *next, *entry = state->extension_objects; entry != nullptr;
entry = next) {
next = entry->next;
void* native_instance = entry + 1;
ApiHandle* handle = reinterpret_cast<ApiHandle*>(native_instance);
RawObject object = handle->asObjectNoImmediate();
bool alive = handle->refcnt() > 1 ||
!isWhiteObject(scavenger, HeapObject::cast(object));
visitor->visitPointer(&object, PointerKind::kApiHandle);
handle->reference_ = reinterpret_cast<uintptr_t>(object.raw());
// TODO(T58548736): Run safe dealloc slots here when possible rather than
// putting everything on the queue.
if (!alive) {
NativeProxy::enqueue(object, runtime->finalizableReferences());
}
}
}
} // namespace py