this repo has no description
at trunk 208 lines 7.2 kB view raw
1/* Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) */ 2#pragma once 3 4#include <cstdint> 5 6#include "cpython-types.h" 7 8#include "handles.h" 9#include "objects.h" 10 11namespace py { 12 13class PointerVisitor; 14 15static const Py_ssize_t kImmediateRefcnt = Py_ssize_t{1} << 63; 16 17class ApiHandle : public PyObject { 18 public: 19 // Returns a handle for a managed object. Increments the reference count of 20 // the handle. 21 static ApiHandle* newReference(Runtime* runtime, RawObject obj); 22 23 // Returns a handle for a managed object. This must not be called with an 24 // extension object or an object for which `isEncodeableAsImmediate` is true. 25 static ApiHandle* newReferenceWithManaged(Runtime* runtime, RawObject obj); 26 27 // Returns a handle for a managed object. Does not affect the reference count 28 // of the handle. 29 static ApiHandle* borrowedReference(Runtime* runtime, RawObject obj); 30 31 static ApiHandle* handleFromImmediate(RawObject obj); 32 33 // Returns the managed object associated with the handle. Decrements the 34 // reference count of handle. 35 static RawObject stealReference(PyObject* py_obj); 36 37 // Returns the managed object associated with the handle checking for 38 static RawObject checkFunctionResult(Thread* thread, PyObject* result); 39 40 static ApiHandle* fromPyObject(PyObject* py_obj); 41 42 static ApiHandle* fromPyTypeObject(PyTypeObject* type); 43 44 // Get the object from the handle's reference field. 45 static RawObject asObject(ApiHandle* handle); 46 47 static RawObject asObjectImmediate(ApiHandle* handle); 48 49 static RawObject asObjectNoImmediate(ApiHandle* handle); 50 51 // Return native proxy belonging to an extension object. 52 static RawNativeProxy asNativeProxy(ApiHandle* handle); 53 54 // Each ApiHandle can have one pointer to cached data, which will be freed 55 // when the handle is destroyed. 56 static void* cache(Runtime* runtime, ApiHandle* handle); 57 static void setCache(Runtime* runtime, ApiHandle* handle, void* value); 58 59 // Decrements the reference count of the handle to signal the removal of a 60 // reference count from extension code. 61 static void decref(ApiHandle* handle); 62 63 static void decrefNoImmediate(ApiHandle* handle); 64 65 // Remove the ApiHandle from the dictionary and free its memory 66 static void dispose(ApiHandle* handle); 67 static void disposeWithRuntime(Runtime* runtime, ApiHandle* handle); 68 69 static bool isImmediate(ApiHandle* handle); 70 71 // Increments the reference count of the handle to signal the addition of a 72 // reference from extension code. 73 static void incref(ApiHandle* handle); 74 75 static void increfNoImmediate(ApiHandle* handle); 76 77 // Returns the number of references to this handle from extension code. 78 static Py_ssize_t refcnt(ApiHandle* handle); 79 80 static Py_ssize_t refcntNoImmediate(ApiHandle* handle); 81 82 static void setRefcnt(ApiHandle* handle, Py_ssize_t count); 83 84 static void setBorrowedNoImmediate(ApiHandle* handle); 85 static bool isBorrowedNoImmediate(ApiHandle* handle); 86 87 private: 88 static bool isEncodeableAsImmediate(RawObject obj); 89 90 static const Py_ssize_t kBorrowedBit = Py_ssize_t{1} << 63; 91 92 static const long kImmediateTag = 0x1; 93 static const long kImmediateMask = 0x7; 94 95 static_assert(kBorrowedBit == kImmediateRefcnt, 96 "keep kBorrowedBit and kImmediateRefcnt in sync"); 97 static_assert(kImmediateMask < alignof(PyObject*), 98 "Stronger alignment guarantees are required for immediate " 99 "tagged PyObject* to work."); 100 101 DISALLOW_IMPLICIT_CONSTRUCTORS(ApiHandle); 102}; 103 104static_assert(sizeof(ApiHandle) == sizeof(PyObject), 105 "ApiHandle must not add members to PyObject"); 106 107struct FreeListNode { 108 FreeListNode* next; 109}; 110 111static_assert(sizeof(FreeListNode) <= sizeof(ApiHandle), 112 "Free ApiHandle should be usable as a FreeListNode"); 113 114inline RawObject ApiHandle::asObject(ApiHandle* handle) { 115 if (isImmediate(handle)) return asObjectImmediate(handle); 116 return asObjectNoImmediate(handle); 117} 118 119inline RawObject ApiHandle::asObjectImmediate(ApiHandle* handle) { 120 DCHECK(isImmediate(handle), "expected immediate"); 121 return RawObject{reinterpret_cast<uword>(handle) ^ kImmediateTag}; 122} 123 124inline RawObject ApiHandle::asObjectNoImmediate(ApiHandle* handle) { 125 DCHECK(!isImmediate(handle), "must not be called with immediate object"); 126 return RawObject{handle->reference_}; 127} 128 129inline void ApiHandle::decref(ApiHandle* handle) { 130 if (isImmediate(handle)) return; 131 decrefNoImmediate(handle); 132} 133 134inline void ApiHandle::decrefNoImmediate(ApiHandle* handle) { 135 DCHECK(!isImmediate(handle), "must not be called with immediate object"); 136 DCHECK((handle->ob_refcnt & ~kBorrowedBit) > 0, "reference count underflow"); 137 --handle->ob_refcnt; 138 // Dispose `ApiHandle`s without `kBorrowedBit` when they reach refcount zero. 139 if (handle->ob_refcnt == 0) { 140 dispose(handle); 141 } 142} 143 144inline ApiHandle* ApiHandle::fromPyObject(PyObject* py_obj) { 145 return static_cast<ApiHandle*>(py_obj); 146} 147 148inline ApiHandle* ApiHandle::fromPyTypeObject(PyTypeObject* type) { 149 return fromPyObject(reinterpret_cast<PyObject*>(type)); 150} 151 152inline ApiHandle* ApiHandle::handleFromImmediate(RawObject obj) { 153 DCHECK(isEncodeableAsImmediate(obj), "expected immediate"); 154 return reinterpret_cast<ApiHandle*>(obj.raw() ^ kImmediateTag); 155} 156 157inline void ApiHandle::incref(ApiHandle* handle) { 158 if (isImmediate(handle)) return; 159 // fprintf(stderr, "incref(%p)\n", (void*)this); 160 increfNoImmediate(handle); 161} 162 163inline void ApiHandle::increfNoImmediate(ApiHandle* handle) { 164 DCHECK(!isImmediate(handle), "must not be called with immediate object"); 165 DCHECK((handle->ob_refcnt & ~kBorrowedBit) < 166 (std::numeric_limits<Py_ssize_t>::max() & ~kBorrowedBit), 167 "Reference count overflowed"); 168 ++handle->ob_refcnt; 169} 170 171inline bool ApiHandle::isImmediate(ApiHandle* handle) { 172 return (reinterpret_cast<uword>(handle) & kImmediateMask) != 0; 173} 174 175inline Py_ssize_t ApiHandle::refcnt(ApiHandle* handle) { 176 if (isImmediate(handle)) return kImmediateRefcnt; 177 return refcntNoImmediate(handle); 178} 179 180inline Py_ssize_t ApiHandle::refcntNoImmediate(ApiHandle* handle) { 181 DCHECK(!isImmediate(handle), "must not be called with immediate object"); 182 return handle->ob_refcnt & ~kBorrowedBit; 183} 184 185inline void ApiHandle::setBorrowedNoImmediate(ApiHandle* handle) { 186 DCHECK(!isImmediate(handle), "must not be called with immediate object"); 187 handle->ob_refcnt |= kBorrowedBit; 188} 189 190inline bool ApiHandle::isBorrowedNoImmediate(ApiHandle* handle) { 191 DCHECK(!isImmediate(handle), "must not be called with immediate object"); 192 return (handle->ob_refcnt & kBorrowedBit) != 0; 193} 194 195inline RawObject ApiHandle::stealReference(PyObject* py_obj) { 196 ApiHandle* handle = ApiHandle::fromPyObject(py_obj); 197 if (isImmediate(handle)) return asObjectImmediate(handle); 198 DCHECK((handle->ob_refcnt & ~kBorrowedBit) > 0, "refcount underflow"); 199 // Mark stolen reference as borrowed. This is to support code like this that 200 // increases refcount after the fact: 201 // PyModule_AddObject(..., x); 202 // Py_INCREF(x); 203 handle->ob_refcnt |= kBorrowedBit; 204 handle->ob_refcnt--; 205 return asObjectNoImmediate(handle); 206} 207 208} // namespace py