Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(676)

Unified Diff: content/common/gamepad_seqlock.h

Issue 8772004: Improve GamepadSeqLock impl Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/common/gamepad_seqlock.h
===================================================================
--- content/common/gamepad_seqlock.h (revision 112429)
+++ content/common/gamepad_seqlock.h (working copy)
@@ -6,41 +6,92 @@
#define CONTENT_COMMON_GAMEPAD_SEQLOCK_H_
#include "base/atomicops.h"
-#include "base/threading/platform_thread.h"
#include "content/common/content_export.h"
namespace content {
// This SeqLock handles only *one* writer and multiple readers. It may be
-// suitable for low-contention with relatively infrequent writes, and many
-// readers. See:
+// suitable for high read frequency scenarios and is especially useful in shared
+// memory environment. See for the basic idea:
// http://en.wikipedia.org/wiki/Seqlock
-// http://www.concurrencykit.org/doc/ck_sequence.html
-// This implementation is based on ck_sequence.h from http://concurrencykit.org.
-//
-// Currently, this is used in only one location. It may make sense to
-// generalize with a higher-level construct that owns both the lock and the
-// data buffer, if it is to be used more widely.
-//
-// You must be very careful not to operate on potentially inconsistent read
-// buffers. If the read must be retry'd, the data in the read buffer could
-// contain any random garbage. e.g., contained pointers might be
-// garbage, or indices could be out of range. Probably the only suitable thing
-// to do during the read loop is to make a copy of the data, and operate on it
-// only after the read was found to be consistent.
+// However, this implementation is an improved lock-free variant.
+// The SeqLock can hold only POD fully-embed data (no pointers
+// to satellite data), copies are done with memcpy.
+template<typename T>
class CONTENT_EXPORT GamepadSeqLock {
darin (slow to review) 2011/12/01 17:45:16 nit: since this is all implemented inline, there i
dvyukov 2011/12/01 21:27:28 Will do.
public:
GamepadSeqLock();
- base::subtle::Atomic32 ReadBegin();
- bool ReadRetry(base::subtle::Atomic32 version);
- void WriteBegin();
- void WriteEnd();
+ void ReadTo(T* obj);
+ void Write(T const* obj);
private:
- base::subtle::Atomic32 sequence_;
+ typedef base::subtle::Atomic32 Atomic32;
+ static size_t const kSize = sizeof(T) / sizeof(Atomic32) + 1;
+ static size_t const kEntries = 2;
+ struct Entry {
+ Atomic32 sequence_;
+ Atomic32 data_[kSize];
+ };
+ Atomic32 current_;
+ Entry entries_[kEntries];
DISALLOW_COPY_AND_ASSIGN(GamepadSeqLock);
};
+template<typename T>
+GamepadSeqLock<T>::GamepadSeqLock()
+ : current_(0) {
+ memset(entries_, 0, sizeof(entries_));
+}
+
+template<typename T>
+void GamepadSeqLock<T>::ReadTo(T* obj) {
+ using base::subtle::NoBarrier_Load;
+ using base::subtle::Acquire_Load;
+ using base::subtle::MemoryBarrier;
+ for (;;) {
scottmg 2011/12/01 22:36:39 Could you explain (perhaps in a comment here) why
+ Atomic32 cur = Acquire_Load(&current_);
+ Entry* ent = &entries_[cur % 2];
jbates 2011/12/01 19:31:12 I'm not sure this additional complexity is necessa
dvyukov 2011/12/01 21:27:28 Well, the current algo already has quite substanti
scottmg 2011/12/01 22:36:39 2 should be kEntries? (and below)
+ Atomic32 seq = Acquire_Load(&ent->sequence_); // membar #LoadLoad
+ // If the counter is even, then the entry is not already current -> retry.
+ if (seq % 2)
+ continue;
jbates 2011/12/01 19:31:12 Without some kind of yield or sleep, this can caus
dvyukov 2011/12/01 21:27:28 This algorithm does not require yield, it is compl
jbates 2011/12/02 21:40:14 After rereading the code 10 times or so :), I thin
+ // Copy out the entry.
+ Atomic32 tmp[kSize];
+ for (size_t i = 0; i < kSize; ++i)
scottmg 2011/12/01 22:36:39 This seems very subtle. Could you explain (again i
+ tmp[i] = NoBarrier_Load(&ent->data_[i]);
jbates 2011/12/01 19:31:12 Why not store a volatile T in ent, and change this
dvyukov 2011/12/01 21:27:28 Because it leads to undefined behavior.
jbates 2011/12/02 21:40:14 I'd be interested in learning how you came to that
+ MemoryBarrier(); // membar #LoadLoad
+ Atomic32 seq2 = NoBarrier_Load(&ent->sequence_);
+ // If the counter is changed, then we've read inconsistend data -> retry.
scottmg 2011/12/01 22:36:39 nit: 'inconsistent'
+ if (seq2 != seq)
+ continue;
jbates 2011/12/01 19:31:12 Less likely to cause problems assuming the writer
dvyukov 2011/12/01 21:27:28 yield is not required here.
jbates 2011/12/02 21:40:14 Is that because it can only happen once per call t
+ // Kickback to strict-aliasing.
+ memcpy(reinterpret_cast<char*>(obj), tmp, sizeof(*obj));
+ break;
+ }
+}
+
+template<typename T>
+void GamepadSeqLock<T>::Write(T const* obj) {
jbates 2011/12/01 19:31:12 nit: consider changing this to const T&?
dvyukov 2011/12/01 21:27:28 Will do.
+ using base::subtle::NoBarrier_Store;
+ using base::subtle::Release_Store;
+ using base::subtle::MemoryBarrier;
+ Atomic32 tmp[kSize];
+ // Kickback to strict-aliasing.
+ memcpy(tmp, reinterpret_cast<char const*>(obj), sizeof(*obj));
jbates 2011/12/01 19:31:12 Why the extra memcpy?
dvyukov 2011/12/01 21:27:28 I don't know how to implement w/o it.
jbates 2011/12/02 21:40:14 It appears this is to prevent the compiler from re
+ // Get the non current entry...
+ Entry* ent = &entries_[(current_ + 1) % 2];
+ // ... and mark it as inconsistent.
+ NoBarrier_Store(&ent->sequence_, ent->sequence_ + 1);
jbates 2011/12/01 19:31:12 If the writer thread calls Write multiple times wh
dvyukov 2011/12/01 21:27:28 Yes.
+ MemoryBarrier(); // membar #StoreStore
+ // Copy in the entry.
+ for (size_t i = 0; i < kSize; ++i)
+ NoBarrier_Store(&ent->data_[i], tmp[i]);
jbates 2011/12/01 19:31:12 Why not store a volatile T in ent, and change this
dvyukov 2011/12/01 21:27:28 It leads to undefined behavior, complicates manual
+ // Mark the entry as consistent again.
+ Release_Store(&ent->sequence_, ent->sequence_ + 1); // membar #StoreStore
+ // Switch the current entry.
+ Release_Store(&current_, current_ + 1);
+}
+
} // namespace content
#endif // CONTENT_COMMON_GAMEPAD_SEQLOCK_H_

Powered by Google App Engine
This is Rietveld 408576698