<br><br><div><span class="gmail_quote">On 9/11/07, <b class="gmail_sendername">Guido van Rossum</b> &lt;<a href="mailto:guido@python.org">guido@python.org</a>&gt; wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
On 9/10/07, Travis E. Oliphant &lt;<a href="mailto:oliphant@enthought.com">oliphant@enthought.com</a>&gt; wrote:<br>&gt; Guido van Rossum wrote:<br>&gt; &gt; I&#39;d like to see Travis&#39;s response to this. It&#39;s setting a precedent
<br>&gt; &gt; regarding locking objects in read-only mode; I haven&#39;t found other<br>&gt; &gt; examples of objects using LOCKDATA (the only mentions of it seem to be<br>&gt; &gt; rejecting it :). I keep getting confused by the two separate lock
<br>&gt; &gt; counts (and I think in this version the comment is inconsistent with<br>&gt; &gt; the code). So I&#39;m hoping Travis has a particular way in mind of<br>&gt; &gt; handling LOCKDATA that can be used as a template.
<br>&gt; &gt;<br>&gt; &gt; Travis?<br>&gt;<br>&gt; The use case I had in mind comes about quite often in NumPy when you<br>&gt; want to modify the data-area of an object which may have a<br>&gt; non-contiguous chunk of memory, but the algorithm being used expects
<br>&gt; contiguous data.&nbsp;&nbsp;Imagine, for example, that the exporting object is an<br>&gt; image whose rows are stored in different segments.<br>&gt;<br>&gt; The consumer of the buffer interface, however, may be an extension
<br>&gt; module that does fast image-processing operations and requires<br>&gt; contiguous data.&nbsp;&nbsp;Because it wants to write the results back in to the<br>&gt; memory area when it is done with the algorithm (which may be thread-safe
<br>&gt; and may release the GIL), it requests the object to lock its data to<br>&gt; read-only so that other consumers do not try to get writeable buffers<br>&gt; while it is processing.<br>&gt;<br>&gt; When the algorithm is done, it alone can write to the memory area and
<br>&gt; then when it releases the buffer, the original object will restore<br>&gt; itself to being writeable.&nbsp;&nbsp;Of course, the exporting object must support<br>&gt; this kind of operation and not all objects will.&nbsp;&nbsp;I expect the NumPy
<br>&gt; array object and the PIL to support it for example, and other<br>&gt; media-centric objects.<br><br>Hm, so this is completely different from what I thought. It seems you<br>are describing the following:<br><br>1. acquire the buffer with LOCK_DATA
<br>2. copy the data out of the buffer into a scratch area<br>3. work on the scratch area<br>4. copy the data from the scratch area back into the buffer<br>5. release the buffer<br><br>i would call this an exclusive write lock, which is quite different
<br>from the read lock interpretation implemented by Greg in his patch.<br>Could you add some language to PEP 3118 to clarify this usage? Or is<br>it already there? I admit to not having read it in full...</blockquote><div>
<br>Yes that is different from what I was using it for based on what the pep 3118 description said.&nbsp; Perhaps the existing description in PEP 3118 should be renamed from LOCKDATA to READONLY?<br></div><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
&gt; It would probably be useful if the bytes object supported it because<br>&gt; then other objects could use it as the memory area.&nbsp;&nbsp;&nbsp;&nbsp;To do it<br>&gt; correctly, the object exporting the interface must only allow locking if
<br>&gt; no other writeable interfaces have been exported (which it must keep<br>&gt; track of) and then on release must check to see if the buffer that is<br>&gt; being released is the one that locked its data.<br><br>Right. So it seems you would need a counter of outstanding
<br>non-data-locked buffer requests and a single bit indicating whether<br>there&#39;s a data-locked request. (Rather than two counters like Greg&#39;s<br>patch currently uses.)<br><br>The hacker in me is already exploring the possibility of making the
<br>count negative if there&#39;s a data-locked request; it sounds like the<br>valid transitions are:<br><br>0 -&gt; 1 -&gt; 2 -&gt; ... (SIMPLE or WRITABLE get)<br>... -&gt; 2 -&gt; 1 -&gt; ... (SIMPLE or WRITABLE release)
<br>0 -&gt; -1 (LOCKDATA get)<br>-1 -&gt; 0 (LOCKDATA release)<br><br>Have I got that right? I think that you should only be able to request<br>LOCKDATA if there are no other readers *or* writers, but that SIMPLE<br>and WRITABLE clients should be able to coexist (any mess that creates
<br>would be the requester&#39;s own fault). Any nonzero value here would<br>indicate that the buffer can&#39;t be moved.<br><br>I note that the use case in the bsddb wrapper extension is a bit<br>different -- Greg suspects that BerkeleyDB won&#39;t like the data
<br>changing while it is using it (e.g. it might violate its own invariant<br>if the key changes between the time its hash is computed and the time<br>it is written to disk). To ensure this, currently LOCKDATA is the only
<br>option; but a classic read lock would allow multiple concurrent<br>readers (which is how Greg&#39;s patch to bytesobject.c interprets<br>LOCKDATA).<br><br>I think this needs to be clarified. Perhaps we need to separate
<br>clearer the type of access (read or write) and the amount of locking<br>desired (can others read? can others write?).</blockquote><div><br>bsddb is not alone here but was just the code I was working on that made me think it necessary.&nbsp; I am hoping that -all- file/socket/whatever output operations using the buffer API will get properly read-locked views of the buffer so that they can release the GIL and not have the data change out from underneath them by other threads.&nbsp; (this avoids hard to debug issues which python has so far been pretty good at avoiding)
<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">(BTW The current implementation in bytesobject.c allows changing the<br>size as long as it fits within the allocated size; I think this is
<br>probably too lenient, and begging for latent bugs.)<br><br>(Spelling alert: &#39;writeable&#39; is apparently not an English word. I hope<br>it&#39;s not too late to rename the flag to PyBUF_WRITABLE. I&#39;ve opened<br>
<a href="http://bugs.python.org/issue1150">http://bugs.python.org/issue1150</a> to track this.)</blockquote><div><br>eek, yes please lets spell correctly. :)<br></div><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
&gt; For a real-life example, NumPy has a flag called UPDATEIFCOPY that is a<br>&gt; slightly different implementation of the concept.&nbsp;&nbsp; When this flag is<br>&gt; set during conversion to an array, then if a copy must be made to
<br>&gt; satisfy the requirements, the original array is set as read-only and<br>&gt; this special flag is set on the array.&nbsp;&nbsp;When the copy is deleted, its<br>&gt; memory is automatically copied (and possibly casted, etc.) back into the
<br>&gt; original array.&nbsp;&nbsp;It is a nice abstraction of the concept of an output<br>&gt; data area that was borrowed from Numarray and allows many things to be<br>&gt; implemented very quickly in NumPy.<br><br>So in terms of locks, this effectively sets read *and* write locks on
<br>the original object (since whatever you might read out of it may be<br>invalidated when the modified copy is written back). But how to<br>enforce that at the Python level? If we had something like this for<br>the bytes object, any *use* of the bytes object from Python (
e.g.<br>iterating over it or indexing or slicing it) should be prohibited. Is<br>this reasonable?<br><br>&gt; One of the main things people use the NumPy C-API for is to get a<br>&gt; contiguous chunk of memory from an array in order to do processing in
<br>&gt; another language (such as C or Fortran).&nbsp;&nbsp; It is nice to be able to<br>&gt; specify that the result gets placed back into another chunk of memory<br>&gt; (which may or may not be contiguous) in a unified fashion.&nbsp;&nbsp; NumPy
<br>&gt; handles all the copying for you.<br>&gt;<br>&gt; My thinking was that many people will want to be able to get contiguous<br>&gt; chunks of memory, do processing, and then copy the result back into a<br>&gt; segment of memory from a buffer-exporting object which is passed into
<br>&gt; the routine as an output object.<br><br>This is probably common for numpy; for the bytes object, I expect that<br>it&#39;s all much simpler, since it&#39;s just a contiguous 1D array of<br>bytes...</blockquote><div>
<br>fwiw, in the bsddb and hashlib code I raise an error if the buffer returned is not a 1D array.<br></div><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
&gt; I&#39;m not sure if my explanations are helpful.&nbsp;&nbsp;Please let me know if I<br>&gt; can explain further.<br><br>--<br>--Guido van Rossum (home page: <a href="http://www.python.org/~guido/">http://www.python.org/~guido/
</a>)<br></blockquote></div><br>