~mixxxdevelopers/mixxx/engine-control-refactor

« back to all changes in this revision

Viewing changes to mixxx/src/control/controlvalue.h

  • Committer: RJ Ryan
  • Date: 2013-06-04 00:41:29 UTC
  • mfrom: (2890.22.101 mixxx)
  • Revision ID: rryan@mixxx.org-20130604004129-8jjxkicsb3givu4a
MergingĀ fromĀ lp:mixxx.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifndef CONTROLVALUE_H
 
2
#define CONTROLVALUE_H
 
3
 
 
4
#include <limits>
 
5
 
 
6
#include <QAtomicInt>
 
7
#include <QObject>
 
8
 
 
9
// for look free access, this value has to be >= the number of value using threads
 
10
// value must be a fraction of an integer
 
11
const int cRingSize = 8;
 
12
// there are basicly unlimited readers allowed at each ring element
 
13
// but we have to count them so max() is just fine.
 
14
// NOTE(rryan): Wrapping max with parentheses avoids conflict with the max macro
 
15
// defined in windows.h.
 
16
const int cReaderSlotCnt = (std::numeric_limits<int>::max)();
 
17
 
 
18
// A single instance of a value of type T along with an atomic integer which
 
19
// tracks the current number of readers or writers of the slot. The value
 
20
// m_readerSlots starts at cReaderSlotCnt and counts down to 0. If the value is
 
21
// 0 or less then reads to the value fail because there are either too many
 
22
// readers or a write is occurring. A write to the value will fail if
 
23
// m_readerSlots is not equal to cReaderSlotCnt (e.g. there is an active
 
24
// reader).
 
25
template<typename T>
 
26
class ControlRingValue {
 
27
  public:
 
28
    ControlRingValue()
 
29
        : m_value(T()),
 
30
          m_readerSlots(cReaderSlotCnt) {
 
31
    }
 
32
 
 
33
    bool tryGet(T* value) const {
 
34
        // Read while consuming one readerSlot
 
35
        bool hasSlot = (m_readerSlots.fetchAndAddAcquire(-1) > 0);
 
36
        if (hasSlot) {
 
37
            *value = m_value;
 
38
        }
 
39
        (void)m_readerSlots.fetchAndAddRelease(1);
 
40
        return hasSlot;
 
41
    }
 
42
 
 
43
    bool trySet(const T& value) {
 
44
        // try to lock this element entirely for reading
 
45
        if (m_readerSlots.testAndSetAcquire(cReaderSlotCnt, 0)) {
 
46
            m_value = value;
 
47
            m_readerSlots.fetchAndAddRelease(cReaderSlotCnt);
 
48
            return true;
 
49
        }
 
50
        return false;
 
51
   }
 
52
 
 
53
  private:
 
54
    T m_value;
 
55
    mutable QAtomicInt m_readerSlots;
 
56
};
 
57
 
 
58
// Ring buffer based implementation for all Types sizeof(T) > sizeof(void*)
 
59
 
 
60
// An implementation of ControlValueAtomicBase for non-atomic types T. Uses a
 
61
// ring-buffer of ControlRingValues and a read pointer and write pointer to
 
62
// provide getValue()/setValue() methods which *sacrifice perfect consistency*
 
63
// for the benefit of wait-free read/write access to a value.
 
64
template<typename T, bool ATOMIC = false>
 
65
class ControlValueAtomicBase {
 
66
  public:
 
67
    inline T getValue() const {
 
68
        T value = T();
 
69
        unsigned int index = (unsigned int)m_readIndex
 
70
                % (cRingSize);
 
71
        while (m_ring[index].tryGet(&value) == false) {
 
72
            // We are here if
 
73
            // 1) there are more then cReaderSlotCnt reader (get) reading the same value or
 
74
            // 2) the formerly current value is locked by a writer
 
75
            // Case 1 does not happen because we have enough (0x7fffffff) reader slots.
 
76
            // Case 2 happens when the a reader is delayed after reading the
 
77
            // m_currentIndex and in the mean while a reader locks the formaly current value
 
78
            // because it has written cRingSize times. Reading the less recent value will fix
 
79
            // it because it is now actualy the current value.
 
80
            index = (index - 1) % (cRingSize);
 
81
        }
 
82
        return value;
 
83
    }
 
84
 
 
85
    inline void setValue(const T& value) {
 
86
        // Test if we can read atomic
 
87
        // This test is const and will be mad only at compile time
 
88
        unsigned int index;
 
89
        do {
 
90
            index = (unsigned int)m_writeIndex.fetchAndAddAcquire(1)
 
91
                    % (cRingSize);
 
92
            // This will be repeated if the value is locked
 
93
            // 1) by an other writer writing at the same time or
 
94
            // 2) a delayed reader is still blocking the formerly current value
 
95
            // In both cases writing to the next value will fix it.
 
96
        } while (!m_ring[index].trySet(value));
 
97
        m_readIndex = (int)index;
 
98
    }
 
99
 
 
100
  protected:
 
101
    ControlValueAtomicBase()
 
102
        : m_readIndex(0),
 
103
          m_writeIndex(1) {
 
104
        // NOTE(rryan): Wrapping max with parentheses avoids conflict with the
 
105
        // max macro defined in windows.h.
 
106
        Q_ASSERT(((std::numeric_limits<unsigned int>::max)() % cRingSize) == (cRingSize - 1));
 
107
    }
 
108
 
 
109
  private:
 
110
    // In worst case, each reader can consume a reader slot from a different ring element.
 
111
    // In this case there is still one ring element available for writing.
 
112
    ControlRingValue<T> m_ring[cRingSize];
 
113
    QAtomicInt m_readIndex;
 
114
    QAtomicInt m_writeIndex;
 
115
};
 
116
 
 
117
// Specialized template for types that are deemed to be atomic on the target
 
118
// architecture. Instead of using a read/write ring to guarantee atomicity,
 
119
// direct assignment/read of an aligned member variable is used.
 
120
template<typename T>
 
121
class ControlValueAtomicBase<T, true> {
 
122
  public:
 
123
    inline T getValue() const {
 
124
        return m_value;
 
125
    }
 
126
 
 
127
    inline void setValue(const T& value) {
 
128
        m_value = value;
 
129
    }
 
130
 
 
131
  protected:
 
132
    ControlValueAtomicBase()
 
133
            : m_value(T()) {
 
134
    }
 
135
 
 
136
  private:
 
137
#if defined(__GNUC__)
 
138
    T m_value __attribute__ ((aligned(sizeof(void*))));
 
139
#elif defined(_MSC_VER)
 
140
#ifdef _WIN64
 
141
    T __declspec(align(8)) m_value;
 
142
#else
 
143
    T __declspec(align(4)) m_value;
 
144
#endif
 
145
#else
 
146
    T m_value;
 
147
#endif
 
148
};
 
149
 
 
150
// ControlValueAtomic is a wrapper around ControlValueAtomicBase which uses the
 
151
// sizeof(T) to determine which underlying implementation of
 
152
// ControlValueAtomicBase to use. For types where sizeof(T) <= sizeof(void*),
 
153
// the specialized implementation of ControlValueAtomicBase for types that are
 
154
// atomic on the architecture is used.
 
155
template<typename T>
 
156
class ControlValueAtomic
 
157
    : public ControlValueAtomicBase<T, sizeof(T) <= sizeof(void*)> {
 
158
  public:
 
159
 
 
160
    ControlValueAtomic()
 
161
        : ControlValueAtomicBase<T, sizeof(T) <= sizeof(void*)>() {
 
162
    }
 
163
};
 
164
 
 
165
#endif /* CONTROLVALUE_H */