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 // Observer APIs
42 
poll()43 template<typename T> const T* StateQueue<T>::poll()
44 {
45     const T *next = (const T *) atomic_load_explicit(&mNext, memory_order_acquire);
46 
47     if (next != mCurrent) {
48         mAck = next;    // no additional barrier needed
49         mCurrent = next;
50 #ifdef STATE_QUEUE_DUMP
51         mObserverDump->mStateChanges++;
52 #endif
53     }
54     return next;
55 }
56 
57 // Mutator APIs
58 
begin()59 template<typename T> T* StateQueue<T>::begin()
60 {
61     ALOG_ASSERT(!mInMutation, "begin() called when in a mutation");
62     mInMutation = true;
63     return mMutating;
64 }
65 
end(bool didModify)66 template<typename T> void StateQueue<T>::end(bool didModify)
67 {
68     ALOG_ASSERT(mInMutation, "end() called when not in a mutation");
69     ALOG_ASSERT(mIsInitialized || didModify, "first end() must modify for initialization");
70     if (didModify) {
71         mIsDirty = true;
72         mIsInitialized = true;
73     }
74     mInMutation = false;
75 }
76 
push(StateQueue<T>::block_t block)77 template<typename T> bool StateQueue<T>::push(StateQueue<T>::block_t block)
78 {
79 #define PUSH_BLOCK_ACK_NS    3000000L   // 3 ms: time between checks for ack in push()
80                                         //       FIXME should be configurable
81     static const struct timespec req = {0, PUSH_BLOCK_ACK_NS};
82 
83     ALOG_ASSERT(!mInMutation, "push() called when in a mutation");
84 
85 #ifdef STATE_QUEUE_DUMP
86     if (block == BLOCK_UNTIL_ACKED) {
87         mMutatorDump->mPushAck++;
88     }
89 #endif
90 
91     if (mIsDirty) {
92 
93 #ifdef STATE_QUEUE_DUMP
94         mMutatorDump->mPushDirty++;
95 #endif
96 
97         // wait for prior push to be acknowledged
98         if (mExpecting != nullptr) {
99 #ifdef STATE_QUEUE_DUMP
100             unsigned count = 0;
101 #endif
102             for (;;) {
103                 const T *ack = (const T *) mAck;    // no additional barrier needed
104                 if (ack == mExpecting) {
105                     // unnecessary as we're about to rewrite
106                     //mExpecting = nullptr;
107                     break;
108                 }
109                 if (block == BLOCK_NEVER) {
110                     return false;
111                 }
112 #ifdef STATE_QUEUE_DUMP
113                 if (count == 1) {
114                     mMutatorDump->mBlockedSequence++;
115                 }
116                 ++count;
117 #endif
118                 nanosleep(&req, nullptr);
119             }
120 #ifdef STATE_QUEUE_DUMP
121             if (count > 1) {
122                 mMutatorDump->mBlockedSequence++;
123             }
124 #endif
125         }
126 
127         // publish
128         atomic_store_explicit(&mNext, (uintptr_t)mMutating, memory_order_release);
129         mExpecting = mMutating;
130 
131         // copy with circular wraparound
132         if (++mMutating >= &mStates[kN]) {
133             mMutating = &mStates[0];
134         }
135         *mMutating = *mExpecting;
136         mIsDirty = false;
137 
138     }
139 
140     // optionally wait for this push or a prior push to be acknowledged
141     if (block == BLOCK_UNTIL_ACKED) {
142         if (mExpecting != nullptr) {
143 #ifdef STATE_QUEUE_DUMP
144             unsigned count = 0;
145 #endif
146             for (;;) {
147                 const T *ack = (const T *) mAck;    // no additional barrier needed
148                 if (ack == mExpecting) {
149                     mExpecting = nullptr;
150                     break;
151                 }
152 #ifdef STATE_QUEUE_DUMP
153                 if (count == 1) {
154                     mMutatorDump->mBlockedSequence++;
155                 }
156                 ++count;
157 #endif
158                 nanosleep(&req, nullptr);
159             }
160 #ifdef STATE_QUEUE_DUMP
161             if (count > 1) {
162                 mMutatorDump->mBlockedSequence++;
163             }
164 #endif
165         }
166     }
167 
168     return true;
169 }
170 
171 }   // namespace android
172 
173 // Instantiate StateQueue template for the types we need.
174 // This needs to be done in the same translation unit as the template
175 // method definitions above.
176 
177 #include "FastCaptureState.h"
178 #include "FastMixerState.h"
179 
180 namespace android {
181 template class StateQueue<FastCaptureState>;
182 template class StateQueue<FastMixerState>;
183 }   // namespace android
184