this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "extension-object.h"
3
4#include "api-handle.h"
5#include "capi-state.h"
6#include "capi-typeslots.h"
7#include "capi.h"
8#include "linked-list.h"
9#include "object.h"
10#include "runtime.h"
11#include "scavenger.h"
12
13namespace py {
14
15void disposeExtensionObjects(Runtime* runtime) {
16 CAPIState* state = capiState(runtime);
17 while (ListEntry* entry = state->extension_objects) {
18 untrackExtensionObject(runtime, entry);
19 std::free(entry);
20 }
21}
22
23void finalizeExtensionObject(Thread* thread, RawObject object) {
24 HandleScope scope(thread);
25 Runtime* runtime = thread->runtime();
26 NativeProxy proxy(&scope, object);
27 Type type(&scope, runtime->typeOf(*proxy));
28 DCHECK(type.hasNativeData(),
29 "A native instance must come from an extension type");
30 destructor tp_dealloc =
31 reinterpret_cast<destructor>(typeSlotAt(type, Py_tp_dealloc));
32 DCHECK(tp_dealloc != nullptr, "Extension types must have a dealloc slot");
33 ApiHandle* handle = ApiHandle::fromPyObject(
34 reinterpret_cast<PyObject*>(Int::cast(proxy.native()).asCPtr()));
35 CHECK(ApiHandle::refcnt(handle) == 1,
36 "The runtime must hold the last reference to the PyObject* (%p). "
37 "Expecting a refcount of 1, but found %ld\n",
38 reinterpret_cast<void*>(handle), ApiHandle::refcnt(handle));
39 ApiHandle::setRefcnt(handle, 0);
40 ApiHandle::setBorrowedNoImmediate(handle);
41 (*tp_dealloc)(handle);
42 if (!proxy.native().isNoneType() && ApiHandle::refcnt(handle) == 0) {
43 // `proxy.native()` being `None` indicates the extension object memory was
44 // not freed. `ob_refcnt == 0` means the object was not resurrected.
45 // This typically indicates that the user maintains a free-list and wants to
46 // call `PyObject_Init` on the memory again, we have to untrack it!
47 ListEntry* entry = reinterpret_cast<ListEntry*>(handle) - 1;
48 untrackExtensionObject(runtime, entry);
49 }
50}
51
52PyObject* initializeExtensionObject(Thread* thread, PyObject* obj,
53 PyTypeObject* typeobj,
54 const Object& instance) {
55 Runtime* runtime = thread->runtime();
56 HandleScope scope(thread);
57
58 NativeProxy proxy(&scope, *instance);
59 proxy.setNative(runtime->newIntFromCPtr(obj));
60 trackExtensionObject(runtime, reinterpret_cast<ListEntry*>(obj) - 1);
61
62 // Initialize the native object
63 obj->reference_ = proxy.raw();
64 Py_INCREF(typeobj);
65 obj->ob_refcnt = 2;
66 return obj;
67}
68
69word numExtensionObjects(Runtime* runtime) {
70 return capiState(runtime)->num_extension_objects;
71}
72
73bool trackExtensionObject(Runtime* runtime, ListEntry* entry) {
74 CAPIState* state = capiState(runtime);
75 bool did_insert = listEntryInsert(entry, &state->extension_objects);
76 if (did_insert) state->num_extension_objects++;
77 return did_insert;
78}
79
80bool untrackExtensionObject(Runtime* runtime, ListEntry* entry) {
81 CAPIState* state = capiState(runtime);
82 bool did_remove = listEntryRemove(entry, &state->extension_objects);
83 if (did_remove) state->num_extension_objects--;
84 return did_remove;
85}
86
87void visitExtensionObjects(Runtime* runtime, Scavenger* scavenger,
88 PointerVisitor* visitor) {
89 CAPIState* state = capiState(runtime);
90 for (ListEntry *next, *entry = state->extension_objects; entry != nullptr;
91 entry = next) {
92 next = entry->next;
93 void* native_instance = entry + 1;
94 ApiHandle* handle = reinterpret_cast<ApiHandle*>(native_instance);
95 RawObject object = ApiHandle::asObjectNoImmediate(handle);
96 bool alive = ApiHandle::refcnt(handle) > 1 ||
97 !isWhiteObject(scavenger, HeapObject::cast(object));
98 visitor->visitPointer(&object, PointerKind::kApiHandle);
99 handle->reference_ = reinterpret_cast<uintptr_t>(object.raw());
100
101 // TODO(T58548736): Run safe dealloc slots here when possible rather than
102 // putting everything on the queue.
103 if (!alive) {
104 NativeProxy::enqueue(object, runtime->finalizableReferences());
105 }
106 }
107}
108
109} // namespace py