Re: DataObjectLockFree: fix facts in doxygen
Sébastien Barthélémy <[email protected]>
| Newsgroups | gmane.science.robotics.orocos.devel |
|---|---|
| Message-ID | <CAPkDDd7hmYgFvROdNpx0qBzaOcjbwNbusH42FfHBG4-ku6Q-Pw@mail.gmail.com> |
On Wed, Dec 12, 2012 at 2:14 PM, Peter Soetens <[email protected]>wrote: > On Wed, Dec 12, 2012 at 1:07 PM, Sébastien Barthélémy > <[email protected]> wrote: > > Regarding the way this race condition is dealt with (in the loop at line > > 170), if I got it right, a reader thread can get delayed if a write > occurs > > while it is between lines 171 and 172: it has to spin the loop again (it > > kinds of polls the buffer). > > > > If this happens repetitively the reader might be delayed forever. Even if > > the writer has lower priority. > > On the contrary ! This is the whole reason of using lock-free loops > instead of mutexes: if the writer has lower priority, it won't preampt > the read I was thinking of the two threads running concurrently on a multi-core machine a reader and a writer. Hence one does not preempt the other. In such a case, the low priority reader can get (very) slightly delayed by the write since it may have to copy read_ptr again. And again if another write at the wrong time again. I guess this repetition just does not show up in practice. The reader has enough time to poll the read_ptr while the writer is writing. and the read returns immediately. If the writer has higher > priority, the writer returns immediately. It's always the higher > priority thread which is favoured, and never the lower priority > thread. Indeed, the lower priority thread can starve, but that's your > architecture doing that, not my algorithm. > > [...] > > Please stop quoting data corruption in your patches. Agreed, as said in the previous mail, I do not suspect data corruption any more. My second doxygen patch held a few lines from the previous version apparently. Sorry for the mess. Here is a third one. -- Orocos-Dev mailing list [email protected] http://lists.mech.kuleuven.be/mailman/listinfo/orocos-dev
0001-DataObjectLockFree-fix-doxygen.patch
(application/octet-stream, 2.7 KB)
From 9713a4e8a84a1c781dd67090696072f1ea008bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Barth=C3=A9l=C3=A9my?= <[email protected]> Date: Tue, 11 Dec 2012 15:01:10 +0100 Subject: [PATCH 1/2] DataObjectLockFree: fix doxygen to make clear that this port is only thread-safe with a single concurrent writer. --- rtt/base/DataObjectLockFree.hpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/rtt/base/DataObjectLockFree.hpp b/rtt/base/DataObjectLockFree.hpp index d857255..cf0c9e5 100644 --- a/rtt/base/DataObjectLockFree.hpp +++ b/rtt/base/DataObjectLockFree.hpp @@ -46,31 +46,37 @@ namespace RTT { namespace base { /** - * @brief This DataObject is a Lock-Free implementation, - * such that reads and writes can happen concurrently without priority - * inversions. + * @brief This DataObject is a Lock-Free implementation, such that + * several reads and a single write can happen concurrently without + * priority inversions. * - * When there are more writes than reads, the last write will - * be returned. The internal buffer can get full if too many - * concurrent reads are taking to long. In that case, each new - * read will read the element the previous read returned. + * The freshest value (the one from the last write) is returned upon + * read. + * + * The internal buffer can get full if too many concurrent reads are + * taking too long. In that case, each new read will read the element + * the previous read returned instead of the freshest one, until one + * or two buffer slots are freed up. + * + * Readers are garanteed to get the freshest value as long as there are + * at most max_threads concurrent reads. * * @verbatim * The following Truth table applies when a Low Priority thread is * preempted by a High Priority thread : * * L\H | Set | Get | - * Set | Ok | Ok | + * Set | NA | Ok | * Get | Ok | Ok | * * legend : L : Low Priority thread * H : High Priority thread * Blk: Blocks High Priority thread (bad!) - * internal::NA : Not allowed ! + * internal::NA : Not allowed! * @endverbatim * Further, multiple reads may occur before, during and after - * a write operation simultaneously. The buffer needs readers+2*writers - * elements to be guaranteed non blocking. + * a write operation simultaneously. The buffer needs readers+2 + * elements to be guaranteed thread-safe. * @ingroup PortBuffers */ template<class T> @@ -228,4 +234,3 @@ namespace RTT }} #endif - -- 1.7.9.5
0002-DataObjectLockFree-improve-comments.patch
(application/octet-stream, 3.3 KB)
From fc197f4c27fe31e7223ddaa32940e3a3948c896b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Barth=C3=A9l=C3=A9my?= <[email protected]> Date: Wed, 12 Dec 2012 12:38:19 +0100 Subject: [PATCH 2/2] DataObjectLockFree: improve comments --- rtt/base/DataObjectLockFree.hpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/rtt/base/DataObjectLockFree.hpp b/rtt/base/DataObjectLockFree.hpp index cf0c9e5..ba495b5 100644 --- a/rtt/base/DataObjectLockFree.hpp +++ b/rtt/base/DataObjectLockFree.hpp @@ -172,13 +172,24 @@ namespace RTT PtrType reading; // loop to combine Read/Modify of counter // This avoids a race condition where read_ptr - // could become write_ptr ( then we would read corrupted data). + // could become write_ptr (then we would read corrupted data). do { reading = read_ptr; // copy buffer location - oro_atomic_inc(&reading->counter); // lock buffer, no more writes + // Beware: if a write occurs while we are here, read_ptr and + // write_ptr will change behind our back. + // Worst, if BUF_LEN-1 writes occur, write_ptr will point to + // the same buffer element as the reading variable. A later + // write could then corrupt the data while we are reading it. + // Hence the later check to detect this case. + oro_atomic_inc(&reading->counter); + // we locked the buffer element, new writes will not set + // write_ptr to this element. + // XXX smp_mb - if ( reading != read_ptr ) // if read_ptr changed, - oro_atomic_dec(&reading->counter); // better to start over. + if ( reading != read_ptr ) + // if read_ptr changed, better to start over to avoid + // corruption. + oro_atomic_dec(&reading->counter); else break; } while ( true ); @@ -200,12 +211,12 @@ namespace RTT * This method can not be called concurrently (only one * producer). With a minimum of 3 buffers, if the * write_ptr+1 field is not occupied, it will remain so - * because the read_ptr is at write_ptr-1 (and can - * not increment the counter on write_ptr+1). Hence, no + * because the read_ptr is pointing elsewhere and can + * only be changed by a write (this very method). Hence, no * locking is needed. */ // writeout in any case - write_ptr->data = push; + write_ptr->data = push; // takes some time PtrType wrote_ptr = write_ptr; // if next field is occupied (by read_ptr or counter), // go to next and check again... @@ -213,7 +224,7 @@ namespace RTT { write_ptr = write_ptr->next; if (write_ptr == wrote_ptr) - return; // nothing found, to many readers ! + return; // nothing found, too many readers ! } // we will be able to move, so replace read_ptr -- 1.7.9.5