Transducers were never designed to work in parallel context. So I'd define any behavior that arises from using the same transducers in multiple threads *at the same time*, as undefined behavior.
On Sun, Apr 9, 2017 at 4:39 PM, Alexander Gunnarson < alexandergunnar...@gmail.com> wrote: > I should add that, as Timothy pointed out, if multiple threads mutate and > read the value but only one ever does so at a time, as is the case in > `core.async`, then a volatile is sufficient. My preliminary conclusions > above about volatiles apply only to concurrent mutation via e.g. `fold` or > the like. > > Also, regarding the locks you mentioned, Seth, I read up a little on the > Java memory model here > <http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#synchronization> > and I can confirm that a lock is sufficient to provide *both* write *and* > read thread-safety guarantees: > > ... acquir[ing a] monitor ... has the effect of invalidating the local >> processor cache so that variables will be reloaded from main memory. We >> will then be able to see all of the writes made visible by the previous >> release. >> > > `Volatile` only provides a subset of these read-safety guarantees, so a > `volatile` in addition to a lock is indeed overkill, if that's what is > happening. > > On Sunday, April 9, 2017 at 6:19:51 PM UTC-4, Alexander Gunnarson wrote: >> >> It looks that way to me too, Seth, though I'd have to comb over the >> details of the locks implemented there to give a reasoned opinion of my >> own. But yes, if that's the case, the volatile isn't adding anything. >> >> Anyway, I'm not trying to poke holes in the current implementation of >> transducers — on the contrary, I'm very appreciative of and impressed by >> the efforts the clojure.core (and core.async) contributors have made on >> that and other fronts. Transducers are an extremely powerful and elegant >> way to express code that would otherwise be a lot more complex and >> difficult to reason about. I'm just trying to figure out where I can get >> away with having unsynchronized mutable versions of stateful transducers >> that currently use volatiles, and where I need even stronger measures of >> thread safety than volatiles. >> >> To take these thoughts further, I did a simple test to compare the three >> types of mutability we've been talking about (unsynchronized, volatile, and >> atomic — I can reproduce the code here if you'd like) and the takeaway is >> that `map-indexed` really does rely on atomic operations in a multithreaded >> context, as each index depends on the previous index value. When doing a >> `volatile`-based `map-indexed` in parallel on a small collection (8 >> elements), the `volatile` value stays consistent — that is, all the correct >> indices are passed to the mapping function. However, over a sufficiently >> large collection (100 elements, though it could happen on smaller scales >> too), the `volatile` value starts to break down: duplicate index values are >> passed to the mapping function and the highest index value only ever >> reaches 97 at the maximum. The same phenomenon happens, of course, with the >> unsynchronized-mutable-box-based `map-indexed`, though it happens at a >> small scale too (calling the unsynchronized `map-indexed` on 8 elements >> operated on by 2 threads produces only 7 unique indices). >> >> My preliminary conclusions are: >> - Unsynchronized mutability is fine in contexts known to be only >> single-threaded, in which I could replace the `volatile` in `map-indexed` >> and other transducers with unsynchronized mutable boxes. >> - Volatiles are good when all you want to do is set the value and have >> multiple threads always read the most up-to-date value, without having to >> depend on a previous value via e.g. `inc`. >> - Atomic boxes (`atom`, `AtomicLong`, etc.) are necessary when the >> mutable value relies on the previous value via e.g. `inc`, as is the case >> with `map-indexed`. >> >> My guess is that all this applies to e.g. the unsynchronized `ArrayList` >> in `partition-by` as well, which might need to be a synchronized collection >> or an immutable one boxed in an atom, but I haven't tested this. >> >> Would you agree with these conclusions, Seth and Timothy? >> >> On Sunday, April 9, 2017 at 1:56:38 PM UTC-4, Seth Verrinder wrote: >>> >>> I'll defer to Timothy on the particulars of core.async but it looks like >>> [1] the transducer in channel is protected by a lock. If that's the case >>> volatile isn't adding anything in terms memory barriers. >>> >>> 1: https://github.com/clojure/core.async/blob/master/src/mai >>> n/clojure/clojure/core/async/impl/channels.clj#L71 >>> >>> On Sunday, April 9, 2017 at 11:58:00 AM UTC-5, Alexander Gunnarson wrote: >>>> >>>> Thanks so much for your well-considered reply, Timothy! That makes >>>> sense about volatiles being used in e.g. core.async or core.reducers >>>> contexts where the reducing function that closes over the mutable value of >>>> the stateful transducer is called in different threads. Why, then, are >>>> unsynchronized ArrayLists used e.g. in 'partition-by'? It's also closed >>>> over by the reducing function in just the same way as the volatile long >>>> value internal to e.g. 'map-indexed'. I'm not yet clear on how one (the >>>> ArrayList) is acceptable being non-volatile and the other (the volatile >>>> long) is unacceptable. When .add is called, an unsynchronized mutable >>>> counter is updated so the ArrayList can insert the next value at the >>>> correct index. Do you have any insight into this? Meanwhile I'll go do some >>>> digging myself on the Clojure JIRA etc. so I'm more informed on the >>>> subject. >>> >>> -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.