Remove locking scheme from PEP 3118 buffer protocol. See issue 3046 in python bug tracker.
This commit is contained in:
parent
351a920ce3
commit
617bc3a168
137
pep-3118.txt
137
pep-3118.txt
|
@ -147,16 +147,16 @@ Change the ``PyBufferProcs`` structure to ::
|
|||
releasebufferproc bf_releasebuffer;
|
||||
} PyBufferProcs;
|
||||
|
||||
Both of these routines are optional for a type object
|
||||
|
||||
::
|
||||
|
||||
typedef int (*getbufferproc)(PyObject *obj, PyBuffer *view, int flags)
|
||||
|
||||
This function returns ``0`` on success and ``-1`` on failure (and raises an
|
||||
error). The first variable is the "exporting" object. The second
|
||||
argument is the address to a bufferinfo structure. If view is ``NULL``,
|
||||
then no information is returned but a lock on the memory is still
|
||||
obtained. In this case, the corresponding releasebuffer should also
|
||||
be called with ``NULL``.
|
||||
argument is the address to a bufferinfo structure. Both arguments must
|
||||
never be NULL.
|
||||
|
||||
The third argument indicates what kind of buffer the consumer is
|
||||
prepared to deal with and therefore what kind of buffer the exporter
|
||||
|
@ -178,13 +178,16 @@ The exporter should always fill in all elements of the buffer
|
|||
structure (with defaults or NULLs if nothing else is requested). The
|
||||
PyBuffer_FillInfo function can be used for simple cases.
|
||||
|
||||
|
||||
Access flags
|
||||
------------
|
||||
|
||||
Some flags are useful for requesting a specific kind of memory
|
||||
segment, while others indicate to the exporter what kind of
|
||||
information the consumer can deal with. If certain information is not
|
||||
asked for by the consumer, but the exporter cannot share its memory
|
||||
without that information, then a ``PyErr_BufferError`` should be raised.
|
||||
|
||||
|
||||
``PyBUF_SIMPLE``
|
||||
|
||||
This is the default flag state (0). The returned buffer may or may
|
||||
|
@ -198,29 +201,6 @@ without that information, then a ``PyErr_BufferError`` should be raised.
|
|||
The returned buffer must be writable. If it is not writable,
|
||||
then raise an error.
|
||||
|
||||
``PyBUF_LOCK``
|
||||
|
||||
This flag is used to request a lock (either a read-lock or an
|
||||
exclusive write lock) on the data-area of the object before the
|
||||
buffer is returned. If the lock cannot be obtained, then an error
|
||||
should be raised. In conjunction with the PyBUF_WRITABLE flag, the
|
||||
PyBUF_LOCK flag creates four different access modes on the
|
||||
data-area of the buffer memory: read, read-with-write-lock, write,
|
||||
and exclusive write. The access modes are
|
||||
|
||||
================ ================= ==========================
|
||||
flags Description Other Requests Can
|
||||
================ ================= ==========================
|
||||
neither read read and write
|
||||
WRITABLE write read and write
|
||||
LOCK locked read read but not write
|
||||
WRITABLE | LOCK exclusive write neither read nor write
|
||||
================ ================= ==========================
|
||||
|
||||
Care should be taken not to LOCK the buffer unless it is really
|
||||
necessary (especially the exclusive write lock) as it makes the
|
||||
object unable to share its memory until the lock is released.
|
||||
|
||||
``PyBUF_FORMAT``
|
||||
|
||||
The returned buffer must have true format information if this flag
|
||||
|
@ -256,7 +236,6 @@ without that information, then a ``PyErr_BufferError`` should be raised.
|
|||
All of these flags imply PyBUF_STRIDES and guarantee that the
|
||||
strides buffer info structure will be filled in correctly.
|
||||
|
||||
|
||||
``PyBUF_INDIRECT`` (implies ``PyBUF_STRIDES``)
|
||||
|
||||
The returned buffer must have suboffsets information (which can be
|
||||
|
@ -271,29 +250,21 @@ Specialized combinations of flags for specific kinds of memory_sharing.
|
|||
|
||||
| ``PyBUF_CONTIG`` (``PyBUF_ND | PyBUF_WRITABLE``)
|
||||
| ``PyBUF_CONTIG_RO`` (``PyBUF_ND``)
|
||||
| ``PyBUF_CONTIG_LCK`` (``PyBUF_ND | PyBUF_LOCK``)
|
||||
| ``PyBUF_CONTIG_XLCK`` (``PyBUF_ND | PyBUF_WRITABLE | PyBUF_LOCK``)
|
||||
|
||||
Multi-dimensional using strides but aligned
|
||||
|
||||
| ``PyBUF_STRIDED`` (``PyBUF_STRIDES | PyBUF_WRITABLE``)
|
||||
| ``PyBUF_STRIDED_RO`` (``PyBUF_STRIDES``)
|
||||
| ``PyBUF_STRIDED_LCK`` (``PyBUF_STRIDES | PyBUF_LOCK``)
|
||||
| ``PyBUF_STRIDED_XLCK`` (``PyBUF_STRIDES | PyBUF_LOCK | PyBUF_WRITABLE``)
|
||||
|
||||
Multi-dimensional using strides and not necessarily aligned
|
||||
|
||||
| ``PyBUF_RECORDS`` (``PyBUF_STRIDES | PyBUF_WRITABLE | PyBUF_FORMAT``)
|
||||
| ``PyBUF_RECORDS_RO`` (``PyBUF_STRIDES | PyBUF_FORMAT``)
|
||||
| ``PyBUF_RECORDS_LCK`` (``PyBUF_STRIDES | PyBUF_LOCK | PyBUF_FORMAT``)
|
||||
| ``PyBUF_RECORDS_XLCK`` (``PyBUF_STRIDES | PyBUF_LOCK | PyBUF_FORMAT | PyBUF_WRITABLE``)
|
||||
|
||||
Multi-dimensional using sub-offsets
|
||||
|
||||
| ``PyBUF_FULL`` (``PyBUF_INDIRECT | PyBUF_WRITABLE | PyBUF_FORMAT``)
|
||||
| ``PyBUF_FULL_RO`` (``PyBUF_INDIRECT | PyBUF_FORMAT``)
|
||||
| ``PyBUF_FULL_LCK`` (``PyBUF_INDIRECT | PyBUF_LOCK | PyBUF_FORMAT``)
|
||||
| ``PyBUF_FULL_XLCK`` (``PyBUF_INDIRECT | PyBUF_LOCK | PyBUF_FORMAT | PyBUF_WRITABLE``)
|
||||
|
||||
Thus, the consumer simply wanting a contiguous chunk of bytes from
|
||||
the object would use ``PyBUF_SIMPLE``, while a consumer that understands
|
||||
|
@ -307,6 +278,10 @@ There is a C-API that simple exporting objects can use to fill-in the
|
|||
buffer info structure correctly according to the provided flags if a
|
||||
contiguous chunk of "unsigned bytes" is all that can be exported.
|
||||
|
||||
|
||||
The Py_buffer struct
|
||||
--------------------
|
||||
|
||||
The bufferinfo structure is::
|
||||
|
||||
struct bufferinfo {
|
||||
|
@ -322,14 +297,15 @@ The bufferinfo structure is::
|
|||
void *internal;
|
||||
} Py_buffer;
|
||||
|
||||
Before calling the bf_getbuffer function, the bufferinfo structure can be
|
||||
filled with whatever. Upon return from bf_getbuffer, the bufferinfo
|
||||
structure is filled in with relevant information about the buffer.
|
||||
This same bufferinfo structure must be passed to bf_releasebuffer (if
|
||||
available) when the consumer is done with the memory. The caller is
|
||||
responsible for keeping a reference to obj until releasebuffer is
|
||||
called (i.e. the call to bf_getbuffer does not alter the reference
|
||||
count of obj).
|
||||
Before calling the bf_getbuffer function, the bufferinfo structure can
|
||||
be filled with whatever, but the ``buf`` field must be NULL when
|
||||
requesting a new buffer. Upon return from bf_getbuffer, the
|
||||
bufferinfo structure is filled in with relevant information about the
|
||||
buffer. This same bufferinfo structure must be passed to
|
||||
bf_releasebuffer (if available) when the consumer is done with the
|
||||
memory. The caller is responsible for keeping a reference to obj until
|
||||
releasebuffer is called (i.e. the call to bf_getbuffer does not alter
|
||||
the reference count of obj).
|
||||
|
||||
The members of the bufferinfo structure are:
|
||||
|
||||
|
@ -343,14 +319,7 @@ The members of the bufferinfo structure are:
|
|||
|
||||
``readonly``
|
||||
an integer variable to hold whether or not the memory is readonly.
|
||||
1 means the memory is readonly, zero means the memory is writable,
|
||||
-1 means the memory was read "locked" when this Py_buffer
|
||||
structure was filled-in therefore should be unlocked when this
|
||||
Py_buffer structure is "released." A -2 means this Py_buffer
|
||||
structure has an exclusive-write lock on the memory. This should
|
||||
be unlocked when the Py_buffer structure is released. The concept
|
||||
of locking is not supported by all objects that expose the buffer
|
||||
protocol.
|
||||
1 means the memory is readonly, zero means the memory is writable.
|
||||
|
||||
``format``
|
||||
a NULL-terminated format-string (following the struct-style syntax
|
||||
|
@ -359,7 +328,7 @@ The members of the bufferinfo structure are:
|
|||
is the number of bytes implied by the format. This can be NULL which
|
||||
implies standard unsigned bytes ("B").
|
||||
|
||||
``ndims``
|
||||
``ndim``
|
||||
a variable storing the number of dimensions the memory represents.
|
||||
Must be >=0. A value of 0 means that shape and strides and suboffsets
|
||||
must be ``NULL`` (i.e. the memory represents a scalar).
|
||||
|
@ -440,21 +409,25 @@ called then it should allocate those arrays when getbuffer is called
|
|||
when releasebuffer is called.
|
||||
|
||||
|
||||
Releasing the buffer
|
||||
--------------------
|
||||
|
||||
The same bufferinfo struct should be used in the release-buffer
|
||||
interface call. The caller is responsible for the memory of the
|
||||
Py_buffer structure itself.
|
||||
interface call. The caller is responsible for the memory of the
|
||||
Py_buffer structure itself.
|
||||
|
||||
``typedef void (*releasebufferproc)(PyObject *obj, Py_buffer *view)``
|
||||
Callers of getbufferproc must make sure that this function is
|
||||
called when memory previously acquired from the object is no
|
||||
longer needed. The exporter of the interface must make sure that
|
||||
any memory pointed to in the bufferinfo structure remains valid
|
||||
until releasebuffer is called.
|
||||
::
|
||||
|
||||
Both of these routines are optional for a type object
|
||||
typedef void (*releasebufferproc)(PyObject *obj, Py_buffer *view)
|
||||
|
||||
If the bf_releasebuffer function is not provided (i.e. it is NULL),
|
||||
then it does not ever need to be called.
|
||||
Callers of getbufferproc must make sure that this function is called
|
||||
when memory previously acquired from the object is no longer needed.
|
||||
The exporter of the interface must make sure that any memory pointed
|
||||
to in the bufferinfo structure remains valid until releasebuffer is
|
||||
called.
|
||||
|
||||
If the bf_releasebuffer function is not provided (i.e. it is NULL),
|
||||
then it does not ever need to be called.
|
||||
|
||||
Exporters will need to define a bf_releasebuffer function if they can
|
||||
re-allocate their memory, strides, shape, suboffsets, or format
|
||||
|
@ -520,10 +493,6 @@ reference to base is kept and the memory view is not re-grabbed.
|
|||
Thus, this memory view object holds on to the memory of base until it
|
||||
is deleted.
|
||||
|
||||
The bf_getbuffer and bf_releasebuffer for this object increments and
|
||||
decrements the lock on base, but passes on the contents of view to the
|
||||
caller.
|
||||
|
||||
This memory-view object will support multi-dimensional slicing and be
|
||||
the first object provided with Python to do so. Slices of the
|
||||
memory-view object are other memory-view objects with the same base
|
||||
|
@ -571,7 +540,7 @@ description.
|
|||
::
|
||||
|
||||
PyObject * PyMemoryView_GetContiguous(PyObject *obj, int buffertype,
|
||||
char fort)
|
||||
char fortran)
|
||||
|
||||
Return a memoryview object to a contiguous chunk of memory represented
|
||||
by obj. If a copy must be made (because the memory pointed to by obj
|
||||
|
@ -586,9 +555,8 @@ contiguous an error will be raised. In this circumstance, the user
|
|||
can use PyBUF_UPDATEIFCOPY to ensure that a a writable temporary
|
||||
contiguous buffer is returned. The contents of this contiguous buffer
|
||||
will be copied back into the original object after the memoryview
|
||||
object is deleted as long as the original object is writable and
|
||||
allows applying a read-write lock. If this is not allowed by
|
||||
the original object, then a BufferError is raised.
|
||||
object is deleted as long as the original object is writable. If this
|
||||
is not allowed by the original object, then a BufferError is raised.
|
||||
|
||||
If the object is multi-dimensional, then if fortran is 'F', the first
|
||||
dimension of the underlying array will vary the fastest in the buffer.
|
||||
|
@ -597,9 +565,7 @@ If fortran is 'C', then the last dimension will vary the fastest
|
|||
you will get whatever the object decides is more efficient. If a copy
|
||||
is made, then the memory must be freed by calling ``PyMem_Free``.
|
||||
|
||||
You receive a new reference to the memoryview object which will also
|
||||
hold a lock on the objects data area (this lock will be released when
|
||||
the memoryview object is deleted).
|
||||
You receive a new reference to the memoryview object.
|
||||
|
||||
::
|
||||
|
||||
|
@ -816,12 +782,15 @@ It is intended that this PEP will be back-ported to Python 2.6 by
|
|||
adding the C-API and the two functions to the existing buffer
|
||||
protocol.
|
||||
|
||||
The proposed locking mechanism relies entirely on the exporter object
|
||||
to not invalidate any of the memory pointed to by the buffer structure
|
||||
until a corresponding releasebuffer is called. If it wants to be able
|
||||
to change its own shape and/or strides arrays, then it needs to create
|
||||
memory for these in the bufferinfo structure and copy information
|
||||
over.
|
||||
Previous versions of this PEP proposed a read/write locking scheme,
|
||||
but it was later perceived as a) too complicated for common simple use
|
||||
cases that do not require any locking and b) too simple for use cases
|
||||
that required concurrent read/write access to a buffer with changing,
|
||||
short-living locks. It is therefore left to users to implement their
|
||||
own specific locking scheme around buffer objects if they require
|
||||
consistent views across concurrent read/write access. A future PEP
|
||||
may be proposed which includes a separate locking API after some
|
||||
experience with these user-schemes is obtained
|
||||
|
||||
The sharing of strided memory and suboffsets is new and can be seen as
|
||||
a modification of the multiple-segment interface. It is motivated by
|
||||
|
@ -860,8 +829,6 @@ The authors of the PEP promise to contribute and maintain the code for
|
|||
this proposal but will welcome any help.
|
||||
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
|
@ -943,7 +910,7 @@ alive) would do that.
|
|||
}
|
||||
|
||||
/* No releasebuffer is necessary because the memory will never
|
||||
be re-allocated so the locking mechanism is not needed
|
||||
be re-allocated
|
||||
*/
|
||||
|
||||
Ex. 3
|
||||
|
|
Loading…
Reference in New Issue