rev |
line source |
nuclear@0
|
1 /************************************************************************************
|
nuclear@0
|
2
|
nuclear@0
|
3 PublicHeader: OVR_Kernel.h
|
nuclear@0
|
4 Filename : OVR_Lockless.h
|
nuclear@0
|
5 Content : Lock-less classes for producer/consumer communication
|
nuclear@0
|
6 Created : November 9, 2013
|
nuclear@0
|
7 Authors : John Carmack
|
nuclear@0
|
8
|
nuclear@0
|
9 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
|
nuclear@0
|
10
|
nuclear@0
|
11 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
|
nuclear@0
|
12 you may not use the Oculus VR Rift SDK except in compliance with the License,
|
nuclear@0
|
13 which is provided at the time of installation or download, or which
|
nuclear@0
|
14 otherwise accompanies this software in either electronic or hard copy form.
|
nuclear@0
|
15
|
nuclear@0
|
16 You may obtain a copy of the License at
|
nuclear@0
|
17
|
nuclear@0
|
18 http://www.oculusvr.com/licenses/LICENSE-3.2
|
nuclear@0
|
19
|
nuclear@0
|
20 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
|
nuclear@0
|
21 distributed under the License is distributed on an "AS IS" BASIS,
|
nuclear@0
|
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
nuclear@0
|
23 See the License for the specific language governing permissions and
|
nuclear@0
|
24 limitations under the License.
|
nuclear@0
|
25
|
nuclear@0
|
26 *************************************************************************************/
|
nuclear@0
|
27
|
nuclear@0
|
28 #ifndef OVR_Lockless_h
|
nuclear@0
|
29 #define OVR_Lockless_h
|
nuclear@0
|
30
|
nuclear@0
|
31 #include "OVR_Atomic.h"
|
nuclear@0
|
32
|
nuclear@0
|
33 // Define this to compile-in Lockless test logic
|
nuclear@0
|
34 //#define OVR_LOCKLESS_TEST
|
nuclear@0
|
35
|
nuclear@0
|
36 namespace OVR {
|
nuclear@0
|
37
|
nuclear@0
|
38
|
nuclear@0
|
39 // ***** LocklessUpdater
|
nuclear@0
|
40
|
nuclear@0
|
41 // For single producer cases where you only care about the most recent update, not
|
nuclear@0
|
42 // necessarily getting every one that happens (vsync timing, SensorFusion updates).
|
nuclear@0
|
43 //
|
nuclear@0
|
44 // This is multiple consumer safe, but is currently only used with a single consumer.
|
nuclear@0
|
45 //
|
nuclear@0
|
46 // The SlotType can be the same as T, but should probably be a larger fixed size.
|
nuclear@0
|
47 // This allows for forward compatibility when the updater is shared between processes.
|
nuclear@0
|
48
|
nuclear@0
|
49 // FIXME: ExchangeAdd_Sync() should be replaced with a portable read-only primitive,
|
nuclear@0
|
50 // so that the lockless pose state can be read-only on remote processes and to reduce
|
nuclear@0
|
51 // false sharing between processes and improve performance.
|
nuclear@0
|
52
|
nuclear@0
|
53 template<class T, class SlotType>
|
nuclear@0
|
54 class LocklessUpdater
|
nuclear@0
|
55 {
|
nuclear@0
|
56 public:
|
nuclear@0
|
57 LocklessUpdater() : UpdateBegin( 0 ), UpdateEnd( 0 )
|
nuclear@0
|
58 {
|
nuclear@0
|
59 OVR_COMPILER_ASSERT(sizeof(T) <= sizeof(SlotType));
|
nuclear@0
|
60 }
|
nuclear@0
|
61
|
nuclear@0
|
62 T GetState() const
|
nuclear@0
|
63 {
|
nuclear@0
|
64 // Copy the state out, then retry with the alternate slot
|
nuclear@0
|
65 // if we determine that our copy may have been partially
|
nuclear@0
|
66 // stepped on by a new update.
|
nuclear@0
|
67 T state;
|
nuclear@0
|
68 int begin, end, final;
|
nuclear@0
|
69
|
nuclear@0
|
70 for(;;)
|
nuclear@0
|
71 {
|
nuclear@0
|
72 // We are adding 0, only using these as atomic memory barriers, so it
|
nuclear@0
|
73 // is ok to cast off the const, allowing GetState() to remain const.
|
nuclear@0
|
74 end = UpdateEnd.Load_Acquire();
|
nuclear@0
|
75 state = Slots[ end & 1 ];
|
nuclear@0
|
76 begin = UpdateBegin.Load_Acquire();
|
nuclear@0
|
77 if ( begin == end ) {
|
nuclear@0
|
78 break;
|
nuclear@0
|
79 }
|
nuclear@0
|
80
|
nuclear@0
|
81 // The producer is potentially blocked while only having partially
|
nuclear@0
|
82 // written the update, so copy out the other slot.
|
nuclear@0
|
83 state = Slots[ (begin & 1) ^ 1 ];
|
nuclear@0
|
84 final = UpdateBegin.Load_Acquire();
|
nuclear@0
|
85 if ( final == begin ) {
|
nuclear@0
|
86 break;
|
nuclear@0
|
87 }
|
nuclear@0
|
88
|
nuclear@0
|
89 // The producer completed the last update and started a new one before
|
nuclear@0
|
90 // we got it copied out, so try fetching the current buffer again.
|
nuclear@0
|
91 }
|
nuclear@0
|
92 return state;
|
nuclear@0
|
93 }
|
nuclear@0
|
94
|
nuclear@0
|
95 void SetState( const T& state )
|
nuclear@0
|
96 {
|
nuclear@0
|
97 const int slot = UpdateBegin.ExchangeAdd_Sync(1) & 1;
|
nuclear@0
|
98 // Write to (slot ^ 1) because ExchangeAdd returns 'previous' value before add.
|
nuclear@0
|
99 Slots[slot ^ 1] = state;
|
nuclear@0
|
100 UpdateEnd.ExchangeAdd_Sync(1);
|
nuclear@0
|
101 }
|
nuclear@0
|
102
|
nuclear@0
|
103 AtomicInt<int> UpdateBegin;
|
nuclear@0
|
104 AtomicInt<int> UpdateEnd;
|
nuclear@0
|
105 SlotType Slots[2];
|
nuclear@0
|
106 };
|
nuclear@0
|
107
|
nuclear@0
|
108
|
nuclear@0
|
109 #ifdef OVR_LOCKLESS_TEST
|
nuclear@0
|
110 void StartLocklessTest();
|
nuclear@0
|
111 #endif
|
nuclear@0
|
112
|
nuclear@0
|
113
|
nuclear@0
|
114 } // namespace OVR
|
nuclear@0
|
115
|
nuclear@0
|
116 #endif // OVR_Lockless_h
|
nuclear@0
|
117
|