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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.