Renamed everything from 'fixed buffer' to 'locked buffer'.
Recommandations on how to implement the interface.
This commit is contained in:
parent
419b7d99fb
commit
7fadff7e8a
131
pep-0298.txt
131
pep-0298.txt
|
@ -1,5 +1,5 @@
|
|||
PEP: 298
|
||||
Title: The Fixed Buffer Interface
|
||||
Title: The Locked Buffer Interface
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Thomas Heller <theller@python.net>
|
||||
|
@ -7,15 +7,15 @@ Status: Draft
|
|||
Type: Standards Track
|
||||
Created: 26-Jul-2002
|
||||
Python-Version: 2.3
|
||||
Post-History: 30-Jul-2002
|
||||
Post-History: 30-Jul-2002, 1-Aug-2002
|
||||
|
||||
|
||||
Abstract
|
||||
|
||||
This PEP proposes an extension to the buffer interface called the
|
||||
'fixed buffer interface'.
|
||||
'locked buffer interface'.
|
||||
|
||||
The fixed buffer interface fixes the flaws of the 'old' buffer
|
||||
The locked buffer interface avoids the flaws of the 'old' buffer
|
||||
interface [1] as defined in Python versions up to and including
|
||||
2.2, and has the following semantics:
|
||||
|
||||
|
@ -33,7 +33,7 @@ Abstract
|
|||
|
||||
Specification
|
||||
|
||||
The fixed buffer interface exposes new functions which return the
|
||||
The locked buffer interface exposes new functions which return the
|
||||
size and the pointer to the internal memory block of any python
|
||||
object which chooses to implement this interface.
|
||||
|
||||
|
@ -42,31 +42,31 @@ Specification
|
|||
reallocated.
|
||||
|
||||
The object must be unlocked again by releasing the buffer if it's
|
||||
no longer used by calling another function in the fixed buffer
|
||||
no longer used by calling another function in the locked buffer
|
||||
interface. If the object never resizes or reallocates the buffer
|
||||
during it's lifetime, this function may be NULL. Failure to call
|
||||
during it's lifetime, this function may be NULL. Failure to call
|
||||
this function (if it is != NULL) is a programming error and may
|
||||
have unexpected results.
|
||||
|
||||
The fixed buffer interface omits the memory segment model which is
|
||||
present in the old buffer interface - only a single memory block
|
||||
can be exposed.
|
||||
The locked buffer interface omits the memory segment model which
|
||||
is present in the old buffer interface - only a single memory
|
||||
block can be exposed.
|
||||
|
||||
|
||||
Implementation
|
||||
|
||||
Define a new flag in Include/object.h:
|
||||
|
||||
/* PyBufferProcs contains bf_acquirefixedreadbuffer,
|
||||
bf_acquirefixedwritebuffer, and bf_releasefixedbuffer */
|
||||
#define Py_TPFLAGS_HAVE_FIXEDBUFFER (1L<<15)
|
||||
/* PyBufferProcs contains bf_acquirelockedreadbuffer,
|
||||
bf_acquirelockedwritebuffer, and bf_releaselockedbuffer */
|
||||
#define Py_TPFLAGS_HAVE_LOCKEDBUFFER (1L<<15)
|
||||
|
||||
|
||||
This flag would be included in Py_TPFLAGS_DEFAULT:
|
||||
|
||||
#define Py_TPFLAGS_DEFAULT ( \
|
||||
....
|
||||
Py_TPFLAGS_HAVE_FIXEDBUFFER | \
|
||||
Py_TPFLAGS_HAVE_LOCKEDBUFFER | \
|
||||
....
|
||||
0)
|
||||
|
||||
|
@ -74,31 +74,31 @@ Implementation
|
|||
Extend the PyBufferProcs structure by new fields in
|
||||
Include/object.h:
|
||||
|
||||
typedef size_t (*acquirefixedreadbufferproc)(PyObject *,
|
||||
const void **);
|
||||
typedef size_t (*acquirefixedwritebufferproc)(PyObject *,
|
||||
void **);
|
||||
typedef void (*releasefixedbufferproc)(PyObject *);
|
||||
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;
|
||||
/* fixed buffer interface functions */
|
||||
acquirefixedreadbufferproc bf_acquirefixedreadbuffer;
|
||||
acquirefixedwritebufferproc bf_acquirefixedwritebuffer;
|
||||
releasefixedbufferproc bf_releasefixedbuffer;
|
||||
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;
|
||||
|
||||
|
||||
The new fields are present if the Py_TPFLAGS_HAVE_FIXEDBUFFER
|
||||
The new fields are present if the Py_TPFLAGS_HAVE_LOCKEDBUFFER
|
||||
flag is set in the object's type.
|
||||
|
||||
The Py_TPFLAGS_HAVE_FIXEDBUFFER flag implies the
|
||||
The Py_TPFLAGS_HAVE_LOCKEDBUFFER flag implies the
|
||||
Py_TPFLAGS_HAVE_GETCHARBUFFER flag.
|
||||
|
||||
The acquirefixedreadbufferproc and acquirefixedwritebufferproc
|
||||
The acquirelockedreadbufferproc and acquirelockedwritebufferproc
|
||||
functions return the size in bytes of the memory block on success,
|
||||
and fill in the passed void * pointer on success. If these
|
||||
functions fail - either because an error occurs or no memory block
|
||||
|
@ -107,26 +107,36 @@ Implementation
|
|||
should not be used.
|
||||
|
||||
If calls to these functions succeed, eventually the buffer must be
|
||||
released by a call to the releasefixedbufferproc, supplying the
|
||||
original object as argument. The releasefixedbufferproc cannot
|
||||
fail.
|
||||
released by a call to the releaselockedbufferproc, supplying the
|
||||
original object as argument. The releaselockedbufferproc cannot
|
||||
fail. For objects that actually maintain an internal lock count
|
||||
it would be a fatal error if the releaselockedbufferproc function
|
||||
would be called too often, leading to a negative lock count.
|
||||
|
||||
Usually these functions aren't called directly, they are called
|
||||
through convenience functions declared in Include/abstract.h:
|
||||
Similar to the 'old' buffer interface, any of these functions may
|
||||
be set to NULL, but it is strongly recommended to implement the
|
||||
releaselockedbufferproc function (even if it does nothing) if any
|
||||
of the acquireread/writelockedbufferproc functions are
|
||||
implemented, to discourage extension writers from checking for a
|
||||
NULL value and not calling it.
|
||||
|
||||
int PyObject_AquireFixedReadBuffer(PyObject *obj,
|
||||
const void **buffer,
|
||||
size_t *buffer_len);
|
||||
These functions aren't supposed to be called directly, they are
|
||||
called through convenience functions declared in
|
||||
Include/abstract.h:
|
||||
|
||||
int PyObject_AcquireFixedWriteBuffer(PyObject *obj,
|
||||
void **buffer,
|
||||
size_t *buffer_len);
|
||||
int PyObject_AquireLockedReadBuffer(PyObject *obj,
|
||||
const void **buffer,
|
||||
size_t *buffer_len);
|
||||
|
||||
void PyObject_ReleaseFixedBuffer(PyObject *obj);
|
||||
int PyObject_AcquireLockedWriteBuffer(PyObject *obj,
|
||||
void **buffer,
|
||||
size_t *buffer_len);
|
||||
|
||||
void PyObject_ReleaseLockedBuffer(PyObject *obj);
|
||||
|
||||
The former two functions return 0 on success, set buffer to the
|
||||
memory location and buffer_len to the length of the memory block
|
||||
in bytes. On failure, or if the fixed buffer interface is not
|
||||
in bytes. On failure, or if the locked buffer interface is not
|
||||
implemented by obj, they return -1 and set an exception.
|
||||
|
||||
The latter function doesn't return anything, and cannot fail.
|
||||
|
@ -146,13 +156,40 @@ Reference Implementation
|
|||
|
||||
Additional Notes/Comments
|
||||
|
||||
Python strings, Unicode strings, mmap objects, and array objects
|
||||
would expose the fixed buffer interface.
|
||||
Python strings, unicode strings, mmap objects, and array objects
|
||||
would expose the locked buffer interface.
|
||||
|
||||
mmap and array objects would actually enter a locked state while
|
||||
the buffer is active, this is not needed for strings and unicode
|
||||
objects. Resizing locked array objects is not allowed and will
|
||||
raise an exception. Whether closing a locked mmap object is an
|
||||
error or will only be deferred until the lock count reaches zero
|
||||
is an implementation detail.
|
||||
|
||||
Guido recommends:
|
||||
|
||||
But I'm still very concerned that if most built-in types
|
||||
(e.g. strings, bytes) don't implement the release
|
||||
functionality, it's too easy for an extension to seem to work
|
||||
while forgetting to release the buffer.
|
||||
|
||||
I recommend that at least some built-in types implement the
|
||||
acquire/release functionality with a counter, and assert that
|
||||
the counter is zero when the object is deleted -- if the
|
||||
assert fails, someone DECREF'ed their reference to the object
|
||||
without releasing it. (The rule should be that you must own a
|
||||
reference to the object while you've aquired the object.)
|
||||
|
||||
For strings that might be impractical because the string
|
||||
object would have to grow 4 bytes to hold the counter; but the
|
||||
new bytes object (PEP 296) could easily implement the counter,
|
||||
and the array object too -- that way there will be plenty of
|
||||
opportunity to test proper use of the protocol.
|
||||
|
||||
|
||||
Community Feedback
|
||||
|
||||
Greg Ewing doubts the fixed buffer interface is needed at all, he
|
||||
Greg Ewing doubts the locked buffer interface is needed at all, he
|
||||
thinks the normal buffer interface could be used if the pointer is
|
||||
(re)fetched each time it's used. This seems to be dangerous,
|
||||
because even innocent looking calls to the Python API like
|
||||
|
@ -167,8 +204,6 @@ Community Feedback
|
|||
|
||||
Credits
|
||||
|
||||
Scott Gilbert came up with the name 'fixed buffer interface'.
|
||||
|
||||
|
||||
References
|
||||
|
||||
|
|
Loading…
Reference in New Issue