1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "StateQueue"
18 //#define LOG_NDEBUG 0
19 
20 #include "Configuration.h"
21 #include <time.h>
22 #include <cutils/atomic.h>
23 #include <utils/Log.h>
24 #include "StateQueue.h"
25 
26 namespace android {
27 
28 #ifdef STATE_QUEUE_DUMP
dump(int fd)29 void StateQueueObserverDump::dump(int fd)
30 {
31     dprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
32 }
33 
dump(int fd)34 void StateQueueMutatorDump::dump(int fd)
35 {
36     dprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
37             mPushDirty, mPushAck, mBlockedSequence);
38 }
39 #endif
40 
41 // Constructor and destructor
42 
StateQueue()43 template<typename T> StateQueue<T>::StateQueue() :
44     mAck(NULL), mCurrent(NULL),
45     mMutating(&mStates[0]), mExpecting(NULL),
46     mInMutation(false), mIsDirty(false), mIsInitialized(false)
47 #ifdef STATE_QUEUE_DUMP
48     , mObserverDump(&mObserverDummyDump), mMutatorDump(&mMutatorDummyDump)
49 #endif
50 {
51     atomic_init(&mNext, static_cast<uintptr_t>(0));
52 }
53 
~StateQueue()54 template<typename T> StateQueue<T>::~StateQueue()
55 {
56 }
57 
58 // Observer APIs
59 
poll()60 template<typename T> const T* StateQueue<T>::poll()
61 {
62     const T *next = (const T *) atomic_load_explicit(&mNext, memory_order_acquire);
63 
64     if (next != mCurrent) {
65         mAck = next;    // no additional barrier needed
66         mCurrent = next;
67 #ifdef STATE_QUEUE_DUMP
68         mObserverDump->mStateChanges++;
69 #endif
70     }
71     return next;
72 }
73 
74 // Mutator APIs
75 
begin()76 template<typename T> T* StateQueue<T>::begin()
77 {
78     ALOG_ASSERT(!mInMutation, "begin() called when in a mutation");
79     mInMutation = true;
80     return mMutating;
81 }
82 
end(bool didModify)83 template<typename T> void StateQueue<T>::end(bool didModify)
84 {
85     ALOG_ASSERT(mInMutation, "end() called when not in a mutation");
86     ALOG_ASSERT(mIsInitialized || didModify, "first end() must modify for initialization");
87     if (didModify) {
88         mIsDirty = true;
89         mIsInitialized = true;
90     }
91     mInMutation = false;
92 }
93 
push(StateQueue<T>::block_t block)94 template<typename T> bool StateQueue<T>::push(StateQueue<T>::block_t block)
95 {
96 #define PUSH_BLOCK_ACK_NS    3000000L   // 3 ms: time between checks for ack in push()
97                                         //       FIXME should be configurable
98     static const struct timespec req = {0, PUSH_BLOCK_ACK_NS};
99 
100     ALOG_ASSERT(!mInMutation, "push() called when in a mutation");
101 
102 #ifdef STATE_QUEUE_DUMP
103     if (block == BLOCK_UNTIL_ACKED) {
104         mMutatorDump->mPushAck++;
105     }
106 #endif
107 
108     if (mIsDirty) {
109 
110 #ifdef STATE_QUEUE_DUMP
111         mMutatorDump->mPushDirty++;
112 #endif
113 
114         // wait for prior push to be acknowledged
115         if (mExpecting != NULL) {
116 #ifdef STATE_QUEUE_DUMP
117             unsigned count = 0;
118 #endif
119             for (;;) {
120                 const T *ack = (const T *) mAck;    // no additional barrier needed
121                 if (ack == mExpecting) {
122                     // unnecessary as we're about to rewrite
123                     //mExpecting = NULL;
124                     break;
125                 }
126                 if (block == BLOCK_NEVER) {
127                     return false;
128                 }
129 #ifdef STATE_QUEUE_DUMP
130                 if (count == 1) {
131                     mMutatorDump->mBlockedSequence++;
132                 }
133                 ++count;
134 #endif
135                 nanosleep(&req, NULL);
136             }
137 #ifdef STATE_QUEUE_DUMP
138             if (count > 1) {
139                 mMutatorDump->mBlockedSequence++;
140             }
141 #endif
142         }
143 
144         // publish
145         atomic_store_explicit(&mNext, (uintptr_t)mMutating, memory_order_release);
146         mExpecting = mMutating;
147 
148         // copy with circular wraparound
149         if (++mMutating >= &mStates[kN]) {
150             mMutating = &mStates[0];
151         }
152         *mMutating = *mExpecting;
153         mIsDirty = false;
154 
155     }
156 
157     // optionally wait for this push or a prior push to be acknowledged
158     if (block == BLOCK_UNTIL_ACKED) {
159         if (mExpecting != NULL) {
160 #ifdef STATE_QUEUE_DUMP
161             unsigned count = 0;
162 #endif
163             for (;;) {
164                 const T *ack = (const T *) mAck;    // no additional barrier needed
165                 if (ack == mExpecting) {
166                     mExpecting = NULL;
167                     break;
168                 }
169 #ifdef STATE_QUEUE_DUMP
170                 if (count == 1) {
171                     mMutatorDump->mBlockedSequence++;
172                 }
173                 ++count;
174 #endif
175                 nanosleep(&req, NULL);
176             }
177 #ifdef STATE_QUEUE_DUMP
178             if (count > 1) {
179                 mMutatorDump->mBlockedSequence++;
180             }
181 #endif
182         }
183     }
184 
185     return true;
186 }
187 
188 }   // namespace android
189 
190 // hack for gcc
191 #ifdef STATE_QUEUE_INSTANTIATIONS
192 #include STATE_QUEUE_INSTANTIATIONS
193 #endif
194