From: Siebe de V. <Si...@de...> - 2014-02-17 22:56:19
|
Hi, From the spec on barriers: "The file "memory-barriers.txt" ... is highly recommended reading for anyone programming at this level." Unfortunately 'this level' is not defined. Does it include cons and car? Then it would affect any level of Common Lisp, and you need barriers whenever you share places. This would be a simple test: (let ((shared (cons t nil))) (sb-thread:make-thread (lambda () (loop (let ((cons (cons nil nil))) (setf (car cons) t shared cons))))) (sb-thread:make-thread (lambda () (loop (assert (consp shared) () "#'cons needs a barrier!") (assert (car shared) () "(setf car) needs a barrier!"))))) Can it fail on SBCL? The same question, but much broader: my mental model of Lisp threads is shaped by multi-processing on a single core. SMP is of course much hairier and I'd like to get a feeling for it. Can somebody give me or point me to a description of an SMP 'place-model' that applies in SBCL? With a short google I didn't find discussions about SMP in Common Lisp to this level of detail. Thanks, Siebe |
From: Stas B. <sta...@gm...> - 2014-02-18 07:19:37
|
Siebe de Vos <Si...@de...> writes: > Hi, > > From the spec on barriers: "The file "memory-barriers.txt" ... is > highly recommended reading for anyone programming at this level." > > Unfortunately 'this level' is not defined. Does it include cons and car? > Then it would affect any level of Common Lisp, and you need barriers > whenever you share places. > > This would be a simple test: > > (let ((shared (cons t nil))) > (sb-thread:make-thread (lambda () > (loop > (let ((cons (cons nil nil))) > (setf (car cons) t > shared cons))))) > (sb-thread:make-thread (lambda () > (loop > (assert (consp shared) > () "#'cons needs a barrier!") > (assert (car shared) > () "(setf car) needs a barrier!"))))) > > Can it fail on SBCL? It does fail on PPC. -- With best regards, Stas. |
From: Nikodemus S. <nik...@ra...> - 2014-02-19 07:55:45
|
On 18 February 2014 09:19, Stas Boukarev <sta...@gm...> wrote: >> Can it fail on SBCL? > It does fail on PPC. Our memory barrier docs should probably say that read, write, and data-dependency barriers a nops on x86oid platforms, and compare-and-swap and mutex acquisition forms an implicit barrier everywhere. Cheers, -- nikodemus |
From: Nikodemus S. <nik...@ra...> - 2014-02-19 08:00:26
|
On 19 February 2014 09:55, Nikodemus Siivola <nik...@ra...> wrote: > Our memory barrier docs should probably say that read, write, and > data-dependency barriers a nops on x86oid platforms, and s/a nops/are nops/ > compare-and-swap and mutex acquisition forms an implicit barrier > everywhere. Just so that there is no misunderstanding, here's an example where a barrier *is* needed even on x86: http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/ Cheers, -- nikodemus |
From: james a. <jam...@se...> - 2014-02-19 17:01:19
|
good evening; On 18 Feb 2014, at 8:19 AM, Stas Boukarev wrote: > Siebe de Vos <Si...@de...> writes: > >> Hi, >> ... >> This would be a simple test: >> >> (let ((shared (cons t nil))) >> (sb-thread:make-thread (lambda () >> (loop >> (let ((cons (cons nil nil))) >> (setf (car cons) t >> shared cons))))) >> (sb-thread:make-thread (lambda () >> (loop >> (assert (consp shared) >> () "#'cons needs a barrier!") >> (assert (car shared) >> () "(setf car) needs a barrier!"))))) >> >> Can it fail on SBCL? > It does fail on PPC. the second assertion or the first? if the second, because the car value was nil? if so, is it correct to infer that the ppc memory is sufficiently incoherent, that the reading thread sees the write to the 'shared' binding before the write to the car? if so, does the behavior change if the mutation is wrapped in a function? (i would endeavor to answer the questions myself, but have been unable to build a threaded sbcl on my available ppc host...) best regards, from berlin, |
From: Stas B. <sta...@gm...> - 2014-02-19 17:20:44
|
james anderson <jam...@se...> writes: > good evening; > > On 18 Feb 2014, at 8:19 AM, Stas Boukarev wrote: > >> Siebe de Vos <Si...@de...> writes: >> >>> Hi, >>> ... >>> This would be a simple test: >>> >>> (let ((shared (cons t nil))) >>> (sb-thread:make-thread (lambda () >>> (loop >>> (let ((cons (cons nil nil))) >>> (setf (car cons) t >>> shared cons))))) >>> (sb-thread:make-thread (lambda () >>> (loop >>> (assert (consp shared) >>> () "#'cons needs a barrier!") >>> (assert (car shared) >>> () "(setf car) needs a barrier!"))))) >>> >>> Can it fail on SBCL? >> It does fail on PPC. > > the second assertion or the first? > if the second, because the car value was nil? > if so, is it correct to infer that the ppc memory is sufficiently > incoherent, that the reading thread sees the write to the 'shared' > binding before the write to the car? > if so, does the behavior change if the mutation is wrapped in a function? > > (i would endeavor to answer the questions myself, but have been unable > to build a threaded sbcl on my available ppc host...) The second one, allocation was fixed recently to insert SYNC. -- With best regards, Stas. |
From: Siebe <Si...@de...> - 2015-03-30 20:36:16
|
On 2014-02-18 08:19, Stas Boukarev wrote: > Siebe de Vos<Si...@de...> writes: > >> Hi, >> >> From the spec on barriers: "The file "memory-barriers.txt" ... is >> highly recommended reading for anyone programming at this level." >> >> Unfortunately 'this level' is not defined. Does it include cons and car? >> Then it would affect any level of Common Lisp, and you need barriers >> whenever you share places. >> >> This would be a simple test: >> >> (let ((shared (cons t nil))) >> (sb-thread:make-thread (lambda () >> (loop >> (let ((cons (cons nil nil))) >> (setf (car cons) t >> shared cons))))) >> (sb-thread:make-thread (lambda () >> (loop >> (assert (consp shared) >> () "#'cons needs a barrier!") >> (assert (car shared) >> () "(setf car) needs a barrier!"))))) >> >> Can it fail on SBCL? > It does fail on PPC. (Mr. Boukarev, I like your short and precise writing. It forces me to be precise too.) After one year I'm coming back to my question. The first assertion, (CONSP SHARED), can fail in this way: - page 1 is synchronized to other caches - SHARED on page 1 is initialized with a new cons - threads 1 and 2 are started - page 1 is synchronized with thread 2 - thread 2 calls CONSP on the unsynchronized garbage in page1 Is this possible? (It would mean that a new thread is started with memory unsynchronized.) Assuming CONSP only looks at the tag of SHARED and doesn't follow a pointer, it won't fail because of a race between the two started threads. Correct? The second assertion, (CAR SHARED) being true, can fail in this way: - the binding of SHARED is in page 1 - a new cons with car NIL is allocated on page 2 and bound to CONS - page 2 is synchronized with thread 2 - the car of the CONS is set to T and SHARED is set to CONS - page 1 is synchronized with thread 2 - thread 2 sees the new cons (on page 1) still with car being NIL (on page 2) Correct? Can you give any other reason for the second assertion to fail? Thanks, Siebe |