Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 298 – 锁定缓冲区接口

作者:
Thomas Heller <theller at python.net>
状态:
已撤回
类型:
标准跟踪
创建:
2002-07-26
Python 版本:
2.3
历史记录:
2002-07-30, 2002-08-01

目录

摘要

本 PEP 提出对缓冲区接口的扩展,称为“锁定缓冲区接口”。

锁定缓冲区接口避免了在 Python 版本 2.2 及更早版本中定义的“旧”缓冲区接口 [1] 的缺陷,并具有以下语义

  • 检索到的指针的生命周期明确定义并由客户端控制。
  • 缓冲区大小以“size_t”数据类型返回,这允许在 sizeof(int) != sizeof(void *) 的平台上访问大型缓冲区。

(Guido 评论:第二点听起来像是我们也可以对“旧”缓冲区接口进行的更改,如果我们引入另一个属于默认标志的标志位。)

规范

锁定缓冲区接口公开新的函数,这些函数返回任何选择实现此接口的 Python 对象的内部内存块的大小和指针。

从对象检索缓冲区会将该对象置于锁定状态,在此期间,缓冲区不得被释放、调整大小或重新分配。

如果不再使用缓冲区,则必须通过调用锁定缓冲区接口中的另一个函数来释放缓冲区,从而解锁对象。如果对象在其生命周期内从未调整大小或重新分配缓冲区,则此函数可能为 NULL。如果未调用此函数(如果它不等于 NULL),则为编程错误,可能会导致意外结果。

锁定缓冲区接口省略了“旧”缓冲区接口中存在的内存段模型 - 只能公开单个内存块。

可以访问内存块,而无需持有全局解释器锁。

实现

在 Include/object.h 中定义一个新标志

/* PyBufferProcs contains bf_acquirelockedreadbuffer,
   bf_acquirelockedwritebuffer, and bf_releaselockedbuffer */
#define Py_TPFLAGS_HAVE_LOCKEDBUFFER (1L<<15)

此标志将包含在 Py_TPFLAGS_DEFAULT

#define Py_TPFLAGS_DEFAULT  ( \
                    ....
                    Py_TPFLAGS_HAVE_LOCKEDBUFFER | \
                    ....
                    0)

通过 Include/object.h 中的新字段扩展 PyBufferProcs 结构

typedef size_t (*acquirelockedreadbufferproc)(PyObject *,
                                              const void **);
typedef size_t (*acquirelockedwritebufferproc)(PyObject *,
                                               void **);
typedef void (*releaselockedbufferproc)(PyObject *);

typedef struct {
    getreadbufferproc bf_getreadbuffer;
    getwritebufferproc bf_getwritebuffer;
    getsegcountproc bf_getsegcount;
    getcharbufferproc bf_getcharbuffer;
    /* locked buffer interface functions */
    acquirelockedreadbufferproc bf_acquirelockedreadbuffer;
    acquirelockedwritebufferproc bf_acquirelockedwritebuffer;
    releaselockedbufferproc bf_releaselockedbuffer;
} PyBufferProcs;

如果对象类型的 Py_TPFLAGS_HAVE_LOCKEDBUFFER 标志设置,则存在新字段。

Py_TPFLAGS_HAVE_LOCKEDBUFFER 标志意味着 Py_TPFLAGS_HAVE_GETCHARBUFFER 标志。

acquirelockedreadbufferprocacquirelockedwritebufferproc 函数在成功时返回内存块的大小(以字节为单位),并在成功时填充传入的 void * 指针。如果这些函数失败 - 要么是因为发生错误,要么是因为没有公开内存块 - 它们必须将 void * 指针设置为 NULL 并引发异常。在这些情况下,返回值未定义,不应使用。

如果对这些函数的调用成功,最终必须通过调用 releaselockedbufferproc 来释放缓冲区,并将原始对象作为参数提供。releaselockedbufferproc 不会失败。对于实际维护内部锁定计数的对象,如果 releaselockedbufferproc 函数被调用过于频繁,导致锁定计数为负,将是致命错误。

与“旧”缓冲区接口类似,任何这些函数都可以设置为 NULL,但强烈建议实现 releaselockedbufferproc 函数(即使它什么也不做),如果任何 acquireread/writelockedbufferproc 函数已实现,以阻止扩展编写者检查 NULL 值而不调用它。

这些函数不应直接调用,它们通过在 Include/abstract.h 中声明的便利函数调用。

int PyObject_AcquireLockedReadBuffer(PyObject *obj,
                                    const void **buffer,
                                    size_t *buffer_len);

int PyObject_AcquireLockedWriteBuffer(PyObject *obj,
                                      void **buffer,
                                      size_t *buffer_len);

void PyObject_ReleaseLockedBuffer(PyObject *obj);

前两个函数在成功时返回 0,将 buffer 设置为内存位置,并将 buffer_len 设置为内存块的长度(以字节为单位)。在失败时,或如果 obj 未实现锁定缓冲区接口,它们将返回 -1 并设置异常。

最后一个函数不返回值,并且不会失败。

向后兼容性

如果实施了本提案,PyBufferProcs 结构的大小将发生变化,但类型的 tp_flags 插槽可用于确定是否存在附加字段。

参考实现

已将实现上传到 SourceForge 修补程序管理器,作为 https://bugs.python.org/issue652857

其他说明/评论

Python 字符串、Unicode 字符串、mmap 对象和数组对象将公开锁定缓冲区接口。

mmap 和数组对象实际上将在缓冲区处于活动状态时进入锁定状态,字符串和 Unicode 对象不需要这样做。不允许调整锁定数组对象的大小,这将引发异常。关闭锁定 mmap 对象是错误还是仅延迟到锁定计数达到零是实现细节。

Guido 建议

但我仍然非常担心,如果大多数内置类型(例如字符串、字节)没有实现释放功能,那么扩展很容易看起来有效,而忘记释放缓冲区。

我建议至少一些内置类型使用计数器实现获取/释放功能,并断言在删除对象时计数器为零 - 如果断言失败,则有人在未释放对象的情况下对其引用进行了 DECREF。(规则应该是,在获取对象时,必须拥有该对象的引用。)

对于字符串,这可能不切实际,因为字符串对象必须增长 4 个字节才能保存计数器;但是新的字节对象 (PEP 296) 可以轻松实现计数器,数组对象也可以 - 这样一来,将有充分的机会测试协议的正确使用。

社区反馈

Greg Ewing 怀疑是否需要锁定缓冲区接口,他认为如果每次使用指针时都(重新)获取指针,则可以使用普通缓冲区接口。这似乎很危险,因为即使是对 Python API 的看似无辜的调用(如 Py_DECREF())也可能会触发任意 Python 代码的执行。

本提案的第一个版本没有释放函数,但事实证明这将过于严格:mmap 和数组对象将无法实现它,因为 mmap 对象可以在任何时间关闭(如果未锁定),并且数组对象可以调整大小或重新分配缓冲区。

本 PEP 可能会被拒绝,因为除了作者之外,没有人需要它。

参考文献


来源: https://github.com/python/peps/blob/main/peps/pep-0298.rst

最后修改时间: 2023-09-09 17:39:29 GMT