Hi there,

On Tuesday, November 4, 2014 8:05:03 AM UTC+1, Daniel Marjenburgh wrote:
>
> I know transients aren't bash-in-place, as they may return new references, 
> but that is not the problem I'm having here.
> If you bash in place, you would get unexpected results, but still the same 
> result every time. The problem here is that the 'value' of the transient is 
> changing underneath you because of another thread.
>

To ensure consistency, you must ensure that you update a transient value at 
most once – all calls (including reads) on the same transient value after 
an update is illegal/undefined behaviour. Transient updates – minus 
persistent! – will always return a new transient value, even though the 
transient returned may be the same reference. So the code

(let [v (transient {:a 0})
      f1 (future (reduce #(assoc! % (+ (:a %) %2)) v (range 10)))
      f2 (future (reduce #(assoc! % (+ (:a %) %2)) v (range 10)))]
  @f1 @f2 ; wait for futures
  (persistent! @f1))

will result in undefined behaviour, because the original v is updated twice 
(and is potentially read after an update). The correct way would be to do 
the following:

(let [v {:a 0}
      f1 (future (reduce #(assoc! % (+ (:a %) %2)) (transient v) (range 
10)))
      f2 (future (reduce #(assoc! % (+ (:a %) %2)) (transient v) (range 
10)))]
  @f1 @f2 ; wait for futures
  (persistent! @f1))

I don't see an explicit rule at http://clojure.org/transients stating this, 
although I feel it is implicit from the bash in-place rule. Perhaps that's 
something worth adding to the page to avoid confusion.

--
Regards,
Jean Niklas L'orange

 

> Op dinsdag 4 november 2014 06:13:00 UTC+1 schreef Alex Miller:
>>
>>
>>
>> On Monday, November 3, 2014 10:44:19 PM UTC-6, Atamert Ölçgen wrote:
>>>
>>> Thanks Alex!
>>>
>>> Now that I took a second look at Daniel's code, it seems assoc! is used 
>>> like swap!, as if it would modify m in place. So I would expect, if it runs 
>>> without errors, result to be {:a 0}.
>>>
>>
>> Right, that is an incorrect usage - it will actually modify with changes 
>> though, but not in expected ways (this is independent of the change we're 
>> discussing - you can get the same behavior in a single thread modifying a 
>> transient without reusing the return).
>>
>> Given that transients are values (not reference types like ref or atom) I 
>>> can't think of a case where they can be modified concurrently.
>>>
>>
>> You can, but not without going pretty far out of normal Clojure code. 
>>  
>>
>>>
>>>
>>> On Tue, Nov 4, 2014 at 11:19 AM, Alex Miller <al...@puredanger.com> 
>>> wrote:
>>>
>>>>
>>>>
>>>> On Monday, November 3, 2014 9:00:10 PM UTC-6, Atamert Ölçgen wrote:
>>>>>
>>>>>
>>>>>
>>>>> On Mon, Nov 3, 2014 at 5:57 PM, Daniel Marjenburgh <
>>>>> dmarje...@gmail.com> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> I just want to address this issue (CLJ-1498 
>>>>>> <http://dev.clojure.org/jira/browse/CLJ-1498>). It was accepted in 
>>>>>> 1.7-alpha2 and I haven't seen a lot of discussion around it, even though 
>>>>>> it's quite a big change.
>>>>>>
>>>>>> With this change the following code is possible:
>>>>>>
>>>>>
>>>>> With persistents the result would be the same, every time. If this is 
>>>>> now valid Clojure, I didn't run it myself, we are sacrificing 
>>>>> consistency. 
>>>>> I don't understand what we're getting in return.
>>>>>
>>>>> Even the simple example in the ticket (with one future) doesn't make a 
>>>>> lot of sense to me.
>>>>>
>>>>> Am I missing something obvious?
>>>>>
>>>>
>>>> Transients always expect thread isolation. In the past this was locked 
>>>> to a single thread (the one that made the transient). That restriction now 
>>>> extends to being used by multiple threads, but isolation should still be 
>>>> maintained for proper use. 
>>>>
>>>> What we are gaining is the ability to use transient collections in go 
>>>> blocks with core.async, where the thread being used is just one pool out 
>>>> of 
>>>> a thread. For example (just typing this not running it, so excuse any 
>>>> typos):
>>>>
>>>> (defn drain [ch coll] 
>>>>   (let [t (transient []]
>>>>     (go-loop
>>>>       (if-some [v (<! ch)]
>>>>         (do (conj! t v) (recur))
>>>>         (persistent! t)))))
>>>>
>>>> This doesn't necessarily work in core.async because each time the <! 
>>>> parks, it may be woken up with a different thread from the pool. The 
>>>> transient change allows this kind of code to succeed.
>>>>
>>>>
>>>>>
>>>>>> (let [m (transient {:a 0})
>>>>>>       futs (for [n (range 100)]
>>>>>>              (future (assoc! m :a (inc (:a m)))))]
>>>>>>   (mapv deref futs) ; wait until futures are done
>>>>>>   (persistent! m))
>>>>>>
>>>>>>
>>>>>> The results will vary per run, where it used to throw 
>>>>>> an IllegalAccessError: "Transient used by non-owner thread".
>>>>>>
>>>>>> I understand the problems of not being able to have 1 go routine 
>>>>>> access a transient, even though it would be safe, but this solution 
>>>>>> feels 
>>>>>> like it's throwing out the baby with the bathwater. Basically, it's 
>>>>>> doing 
>>>>>> away with what the following block on clojure.org 
>>>>>> <http://clojure.org/transients> says:
>>>>>>
>>>>>> *Transients enforce thread isolation**.* Because each result of a 
>>>>>>> transient operation shares (mutable) structure with the previous, it 
>>>>>>> would 
>>>>>>> be very dangerous if more than one thread were to manipulate a 
>>>>>>> transient at 
>>>>>>> once. In order to prevent this, transients will detect any (read or 
>>>>>>> write) 
>>>>>>> use from a thread other than the one that created them and throw an 
>>>>>>> exception.
>>>>>>
>>>>>>  
>>>>>>
>>>>>>>
>>>>>>> This may not sound like a concurrency story, but single-thread 
>>>>>>> isolation is actually a very useful concurrency semantic. The whole 
>>>>>>> point 
>>>>>>> of using transients is that doing so is safe, because their use is an 
>>>>>>> isolated implementation detail of otherwise functional code. Having 
>>>>>>> that be 
>>>>>>> enforced means that some things that would normally be very hard to 
>>>>>>> make 
>>>>>>> safe with ordinary mutable data structures become easy.
>>>>>>
>>>>>>
>>>>>> I don't have a good solution for dealing with transients and logical 
>>>>>> threads, but I would much prefer keeping the semantics of transients as 
>>>>>> they are and maybe pass an option to transient to disable owner checking.
>>>>>>
>>>>>> -- 
>>>>>> You received this message because you are subscribed to the Google
>>>>>> Groups "Clojure" group.
>>>>>> To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com.
>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>> -- 
>>>>> Kind Regards,
>>>>> Atamert Ölçgen
>>>>>
>>>>> -+-
>>>>> --+
>>>>> +++
>>>>>
>>>>> www.muhuk.com
>>>>>  
>>>>  -- 
>>>> You received this message because you are subscribed to the Google
>>>> Groups "Clojure" group.
>>>> To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com.
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>
>>>
>>>
>>> -- 
>>> Kind Regards,
>>> Atamert Ölçgen
>>>
>>> -+-
>>> --+
>>> +++
>>>
>>> www.muhuk.com
>>>  
>>

-- 
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.

Reply via email to