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年7月26日
Python 版本:
2.3
釋出歷史:
2002年7月30日,2002年8月1日

目錄

摘要

本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 設定為記憶體塊的位元組長度。如果失敗,或者物件未實現鎖定緩衝區介面,則它們返回 -1 並設定異常。

後一個函式不返回任何內容,並且不會失敗。

向後相容性

如果實現此提案,PyBufferProcs 結構的大小會發生變化,但可以使用型別的 tp_flags 槽來確定是否存在附加欄位。

參考實現

已將實現作為 https://bugs.python.org/issue652857 上傳到 SourceForge 補丁管理器。

附加說明/評論

Python 字串、unicode 字串、mmap 物件和陣列物件將公開鎖定緩衝區介面。

mmap 和陣列物件在緩衝區活動時實際上會進入鎖定狀態,字串和 unicode 物件不需要此功能。不允許調整鎖定的陣列物件的大小,否則會引發異常。關閉鎖定的 mmap 物件是錯誤還是隻會推遲到鎖定計數達到零是一個實現細節。

Guido 建議

但我仍然非常擔心,如果大多數內建型別(例如字串、位元組)不實現釋放功能,那麼擴充套件很容易在忘記釋放緩衝區的情況下看起來工作正常。

我建議至少某些內建型別使用計數器實現獲取/釋放功能,並斷言在物件刪除時計數器為零——如果斷言失敗,則有人在未釋放物件的情況下對物件的引用進行了 DECREF。(規則應該是,在您獲取物件時,您必須擁有對該物件的引用。)

對於字串來說,這可能不切實際,因為字串物件必須增加 4 位元組來儲存計數器;但新的位元組物件(PEP 296)可以輕鬆實現計數器,陣列物件也可以——這樣就有足夠的機會測試協議的正確使用。

社群反饋

Greg Ewing 懷疑是否根本需要鎖定緩衝區介面,他認為如果每次使用時都(重新)獲取指標,則可以使用普通緩衝區介面。這似乎很危險,因為即使是看起來無害的 Python API 呼叫,例如 Py_DECREF() 也可能觸發任意 Python 程式碼的執行。

此提案的第一個版本沒有 release 函式,但事實證明這會過於嚴格:mmap 和陣列物件將無法實現它,因為 mmap 物件如果未鎖定可以隨時關閉,而陣列物件可以調整或重新分配緩衝區。

這個PEP可能會被拒絕,因為除了作者之外沒有人需要它。

參考資料


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

最後修改: 2025-02-01 08:59:27 GMT