"Please post all Errors and Corrections here"
http://www.manning-sandbox.com/thread.jspa?threadID=41321&tstart=0

On Friday, July 29, 2011 9:10:00 PM UTC-7, Julien Chastang wrote:
>
> Thanks for your in-depth analysis. 
>
> In conclusion, the 11.5 listing is broken specifically with the 
> reification of the seq function. The problem is that the seq function 
> allows the array reference to escape in an unsafe manner. The issue is 
> concurrency as well as visibility. As you suggest, the only fix is to 
> safely copy (via the locking function, i.e. synchronized block) the 
> contents of the array into a data structure that would be passed back 
> to the calling code. 
>
> By the way, I still think you need a lock in the count function in the 
> case where the caller tries to invoke the count function on a 
> partially constructed object. You may be able to reason otherwise, but 
> it is simply confusing to do so. Just access shared mutables in a safe 
> manner and you will be OK. 
>
> On Jul 29, 3:38 pm, Ken Wesson <[email protected]> wrote: 
> > On Fri, Jul 29, 2011 at 3:19 PM, Julien <[email protected]> wrote: 
> > > This listing is an attempt to make the function safe for concurrent 
> > > modification. My claim is that count and seq should also be locking 
> > > around "a" for exactly same reason as aget and aset. In particular, 
> > > locking is not only ensuring mutual exclusion but also *visibility*. 
> > > The danger is that count and seq may be accessing stale values of "a". 
> > 
> > In the case of "count" that danger doesn't exist; Java arrays are of 
> > fixed size from creation, so the "count" value cannot change. 
> > 
> > The case of "seq" is trickier: the array can indeed change contents 
> > midway through iteration. But there's no simple fix. One could write 
> > something like 
> > 
> > (let [cnt (dec (count a)) 
> >       step (fn step [i] 
> >              (lazy-seq 
> >                (if (> i cnt) 
> >                  (do (monitorexit a) nil) 
> >                  (cons x (step (inc i))))))] 
> >   (monitorenter a) 
> >   (step 0)) 
> > 
> > to hold a lock all the way through seq traversal, but if the seq is 
> > only partially traversed (either deliberately, or because of a thrown 
> > exception) the lock will never get released. 
> > 
> > More complicatedly, you could create a CloseableSeq defprotocol that 
> > extends ISeq and Closeable and create closeable seqs (line-seq could 
> > benefit from this as well); for the above the close operation would be 
> > (monitorexit a) (and for line-seq (.close rdr)). Closeable seqs could 
> > then be created in (with-open ...) and handled in an exception-safe 
> > manner -- so long as you made sure to consume or doall the seq before 
> > the end of the with-open scope. In the array seq case, the seq would 
> > still seem to work after the scope exited, but it would show an array 
> > in a possible state of flux again instead of a fixed state. 
> > 
> > Given all this complication, easiest might be 
> > 
> > (seq [this] 
> >   (locking a 
> >     (seq (into [] a)))) 
> > 
> > which has the downside of copying the array into a vector but the 
> > upside of being thread- and exception-safe; the seq is backed by a 
> > fixed snapshot of the array. And can be traversed lazily and at 
> > leisure without holding onto a lock the whole time; the lock's held 
> > only during the copying. If you're worried about seeing inconsistent 
> > cell values during a traversal of a short array this is what you ought 
> > to use. 
> > 
> > But you should probably prefer to avoid arrays for the most part. :) 
> > 
> > Note: you will still potentially see *stale* values; the array can 
> > change after the snapshot is made. To avoid that you'd always have to 
> > lock during the operation where you want to be seeing the most up to 
> > date values. But you wouldn't see *inconsistent* values. If the array 
> > contains all 1s, and then someone else locks it, changes each cell to 
> > 2, and unlocks, and you are traversing the seq given above, you might 
> > get back (1 1 1 1 2 2 2 ...) which violates the intended invariant of 
> > all-the-same-number. With the snapshot you'll possibly see all-1s even 
> > after it's been updated to all-2s but you will never see a mixture. 
> > With locking the array the whole time you're working with the seq, 
> > you'll see all-2s if the array was ever updated to all-2s and 
> > otherwise the thing that will eventually update it to all-2s will 
> > block until you're done with the seq. 
> > 
> > > See Java Concurrency in Practice, Section 3.1 for more of a 
> > > discussion. My reasoning is based on the assumption that locking is 
> > > simply an abstraction for the Java synchronized keyword. That 
> > > assumption may or may not be correct. 
> > 
> > It is correct, though Clojure exposes the lower-level monitorenter and 
> > monitorexit; both Java synchronized and Clojure locking boil down to 
> > (the bytecode emitted by) 
> > 
> > (try 
> >   (monitorenter foo) 
> >   ... 
> >   (finally (monitorexit foo))) 
> > 
> > so as to ensure the lock is released even if an exception is thrown 
> > out of the critical section. Java just doesn't let you get at the 
> > separate lock and unlock operations for normal objects (but there are 
> > the java.util.concurrent lock object classes). 
> > 
> > -- 
> > Protege: What is this seething mass of parentheses?! 
> > Master: Your father's Lisp REPL. This is the language of a true 
> > hacker. Not as clumsy or random as C++; a language for a more 
> > civilized age.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to