ext/Internal/api-handle.h (128 lines of code) (raw):
/* Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) */
#pragma once
#include <cstdint>
#include "cpython-types.h"
#include "handles.h"
#include "objects.h"
namespace py {
class PointerVisitor;
static const Py_ssize_t kImmediateRefcnt = Py_ssize_t{1} << 63;
class ApiHandle : public PyObject {
public:
// Returns a handle for a managed object. Increments the reference count of
// the handle.
static ApiHandle* newReference(Runtime* runtime, RawObject obj);
// Returns a handle for a managed object. This must not be called with an
// extension object or an object for which `isEncodeableAsImmediate` is true.
static ApiHandle* newReferenceWithManaged(Runtime* runtime, RawObject obj);
// Returns a handle for a managed object. Does not affect the reference count
// of the handle.
static ApiHandle* borrowedReference(Runtime* runtime, RawObject obj);
static ApiHandle* handleFromImmediate(RawObject obj);
// Returns the managed object associated with the handle. Decrements the
// reference count of handle.
static RawObject stealReference(PyObject* py_obj);
// Returns the managed object associated with the handle checking for
static RawObject checkFunctionResult(Thread* thread, PyObject* result);
static ApiHandle* fromPyObject(PyObject* py_obj);
static ApiHandle* fromPyTypeObject(PyTypeObject* type);
// Get the object from the handle's reference field.
RawObject asObject();
RawObject asObjectImmediate();
RawObject asObjectNoImmediate();
// Return native proxy belonging to an extension object.
RawNativeProxy asNativeProxy();
// Each ApiHandle can have one pointer to cached data, which will be freed
// when the handle is destroyed.
void* cache(Runtime* runtime);
void setCache(Runtime* runtime, void* value);
// Decrements the reference count of the handle to signal the removal of a
// reference count from extension code.
void decref();
void decrefNoImmediate();
// Remove the ApiHandle from the dictionary and free its memory
void dispose();
void disposeWithRuntime(Runtime* runtime);
bool isImmediate();
// Increments the reference count of the handle to signal the addition of a
// reference from extension code.
void incref();
void increfNoImmediate();
// Returns the number of references to this handle from extension code.
Py_ssize_t refcnt();
Py_ssize_t refcntNoImmediate();
void setRefcnt(Py_ssize_t count);
void setBorrowedNoImmediate();
bool isBorrowedNoImmediate();
private:
static bool isEncodeableAsImmediate(RawObject obj);
static const Py_ssize_t kBorrowedBit = Py_ssize_t{1} << 63;
static const long kImmediateTag = 0x1;
static const long kImmediateMask = 0x7;
static_assert(kBorrowedBit == kImmediateRefcnt,
"keep kBorrowedBit and kImmediateRefcnt in sync");
static_assert(kImmediateMask < alignof(PyObject*),
"Stronger alignment guarantees are required for immediate "
"tagged PyObject* to work.");
DISALLOW_IMPLICIT_CONSTRUCTORS(ApiHandle);
};
static_assert(sizeof(ApiHandle) == sizeof(PyObject),
"ApiHandle must not add members to PyObject");
struct FreeListNode {
FreeListNode* next;
};
static_assert(sizeof(FreeListNode) <= sizeof(ApiHandle),
"Free ApiHandle should be usable as a FreeListNode");
inline RawObject ApiHandle::asObject() {
if (isImmediate()) return asObjectImmediate();
return asObjectNoImmediate();
}
inline RawObject ApiHandle::asObjectImmediate() {
DCHECK(isImmediate(), "expected immediate");
return RawObject{reinterpret_cast<uword>(this) ^ kImmediateTag};
}
inline RawObject ApiHandle::asObjectNoImmediate() {
DCHECK(!isImmediate(), "must not be called with immediate object");
return RawObject{reference_};
}
inline void ApiHandle::decref() {
if (isImmediate()) return;
decrefNoImmediate();
}
inline void ApiHandle::decrefNoImmediate() {
DCHECK(!isImmediate(), "must not be called with immediate object");
DCHECK((ob_refcnt & ~kBorrowedBit) > 0, "reference count underflow");
--ob_refcnt;
// Dispose `ApiHandle`s without `kBorrowedBit` when they reach refcount zero.
if (ob_refcnt == 0) {
dispose();
}
}
inline ApiHandle* ApiHandle::fromPyObject(PyObject* py_obj) {
return static_cast<ApiHandle*>(py_obj);
}
inline ApiHandle* ApiHandle::fromPyTypeObject(PyTypeObject* type) {
return fromPyObject(reinterpret_cast<PyObject*>(type));
}
inline ApiHandle* ApiHandle::handleFromImmediate(RawObject obj) {
DCHECK(isEncodeableAsImmediate(obj), "expected immediate");
return reinterpret_cast<ApiHandle*>(obj.raw() ^ kImmediateTag);
}
inline void ApiHandle::incref() {
if (isImmediate()) return;
increfNoImmediate();
}
inline void ApiHandle::increfNoImmediate() {
DCHECK(!isImmediate(), "must not be called with immediate object");
DCHECK((ob_refcnt & ~kBorrowedBit) <
(std::numeric_limits<Py_ssize_t>::max() & ~kBorrowedBit),
"Reference count overflowed");
++ob_refcnt;
}
inline bool ApiHandle::isImmediate() {
return (reinterpret_cast<uword>(this) & kImmediateMask) != 0;
}
inline Py_ssize_t ApiHandle::refcnt() {
if (isImmediate()) return kImmediateRefcnt;
return refcntNoImmediate();
}
inline Py_ssize_t ApiHandle::refcntNoImmediate() {
DCHECK(!isImmediate(), "must not be called with immediate object");
return ob_refcnt & ~kBorrowedBit;
}
inline void ApiHandle::setBorrowedNoImmediate() {
DCHECK(!isImmediate(), "must not be called with immediate object");
ob_refcnt |= kBorrowedBit;
}
inline bool ApiHandle::isBorrowedNoImmediate() {
DCHECK(!isImmediate(), "must not be called with immediate object");
return (ob_refcnt & kBorrowedBit) != 0;
}
inline RawObject ApiHandle::stealReference(PyObject* py_obj) {
ApiHandle* handle = ApiHandle::fromPyObject(py_obj);
if (handle->isImmediate()) return handle->asObjectImmediate();
DCHECK((handle->ob_refcnt & ~kBorrowedBit) > 0, "refcount underflow");
// Mark stolen reference as borrowed. This is to support code like this that
// increases refcount after the fact:
// PyModule_AddObject(..., x);
// Py_INCREF(x);
handle->ob_refcnt |= kBorrowedBit;
handle->ob_refcnt--;
return handle->asObjectNoImmediate();
}
} // namespace py