blob: 48399c01cd23f2b9e9952575a68471262e0bd373 [file] [log] [blame]
Glenn Kastendc998c82012-03-23 18:53:59 -07001/*
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
Glenn Kasten153b9fe2013-07-15 11:23:36 -070020#include "Configuration.h"
Glenn Kastendc998c82012-03-23 18:53:59 -070021#include <time.h>
22#include <cutils/atomic.h>
23#include <utils/Log.h>
24#include "StateQueue.h"
25
26namespace android {
27
Glenn Kasten39993082012-05-31 13:40:27 -070028#ifdef STATE_QUEUE_DUMP
29void StateQueueObserverDump::dump(int fd)
30{
31 fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
32}
33
34void StateQueueMutatorDump::dump(int fd)
35{
36 fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
37 mPushDirty, mPushAck, mBlockedSequence);
38}
39#endif
40
Glenn Kastendc998c82012-03-23 18:53:59 -070041// Constructor and destructor
42
43template<typename T> StateQueue<T>::StateQueue() :
44 mNext(NULL), mAck(NULL), mCurrent(NULL),
45 mMutating(&mStates[0]), mExpecting(NULL),
46 mInMutation(false), mIsDirty(false), mIsInitialized(false)
Glenn Kasten39993082012-05-31 13:40:27 -070047#ifdef STATE_QUEUE_DUMP
48 , mObserverDump(&mObserverDummyDump), mMutatorDump(&mMutatorDummyDump)
49#endif
Glenn Kastendc998c82012-03-23 18:53:59 -070050{
51}
52
53template<typename T> StateQueue<T>::~StateQueue()
54{
55}
56
57// Observer APIs
58
59template<typename T> const T* StateQueue<T>::poll()
60{
Kévin PETIT377b2ec2014-02-03 12:35:36 +000061#ifdef __LP64__
62 const T *next = (const T *) android_atomic_acquire_load64((volatile int64_t *) &mNext);
63#else
Glenn Kastendc998c82012-03-23 18:53:59 -070064 const T *next = (const T *) android_atomic_acquire_load((volatile int32_t *) &mNext);
Kévin PETIT377b2ec2014-02-03 12:35:36 +000065#endif
Glenn Kastendc998c82012-03-23 18:53:59 -070066 if (next != mCurrent) {
67 mAck = next; // no additional barrier needed
68 mCurrent = next;
Glenn Kasten39993082012-05-31 13:40:27 -070069#ifdef STATE_QUEUE_DUMP
70 mObserverDump->mStateChanges++;
71#endif
Glenn Kastendc998c82012-03-23 18:53:59 -070072 }
73 return next;
74}
75
76// Mutator APIs
77
78template<typename T> T* StateQueue<T>::begin()
79{
80 ALOG_ASSERT(!mInMutation, "begin() called when in a mutation");
81 mInMutation = true;
82 return mMutating;
83}
84
85template<typename T> void StateQueue<T>::end(bool didModify)
86{
87 ALOG_ASSERT(mInMutation, "end() called when not in a mutation");
88 ALOG_ASSERT(mIsInitialized || didModify, "first end() must modify for initialization");
89 if (didModify) {
90 mIsDirty = true;
91 mIsInitialized = true;
92 }
93 mInMutation = false;
94}
95
96template<typename T> bool StateQueue<T>::push(StateQueue<T>::block_t block)
97{
98#define PUSH_BLOCK_ACK_NS 3000000L // 3 ms: time between checks for ack in push()
99 // FIXME should be configurable
100 static const struct timespec req = {0, PUSH_BLOCK_ACK_NS};
101
102 ALOG_ASSERT(!mInMutation, "push() called when in a mutation");
103
Glenn Kasten39993082012-05-31 13:40:27 -0700104#ifdef STATE_QUEUE_DUMP
105 if (block == BLOCK_UNTIL_ACKED) {
106 mMutatorDump->mPushAck++;
107 }
108#endif
109
Glenn Kastendc998c82012-03-23 18:53:59 -0700110 if (mIsDirty) {
111
Glenn Kasten39993082012-05-31 13:40:27 -0700112#ifdef STATE_QUEUE_DUMP
113 mMutatorDump->mPushDirty++;
114#endif
115
Glenn Kastendc998c82012-03-23 18:53:59 -0700116 // wait for prior push to be acknowledged
117 if (mExpecting != NULL) {
Glenn Kasten39993082012-05-31 13:40:27 -0700118#ifdef STATE_QUEUE_DUMP
119 unsigned count = 0;
120#endif
Glenn Kastendc998c82012-03-23 18:53:59 -0700121 for (;;) {
122 const T *ack = (const T *) mAck; // no additional barrier needed
123 if (ack == mExpecting) {
124 // unnecessary as we're about to rewrite
125 //mExpecting = NULL;
126 break;
127 }
128 if (block == BLOCK_NEVER) {
129 return false;
130 }
Glenn Kasten39993082012-05-31 13:40:27 -0700131#ifdef STATE_QUEUE_DUMP
132 if (count == 1) {
133 mMutatorDump->mBlockedSequence++;
134 }
135 ++count;
136#endif
Glenn Kastendc998c82012-03-23 18:53:59 -0700137 nanosleep(&req, NULL);
138 }
Glenn Kasten39993082012-05-31 13:40:27 -0700139#ifdef STATE_QUEUE_DUMP
140 if (count > 1) {
141 mMutatorDump->mBlockedSequence++;
142 }
143#endif
Glenn Kastendc998c82012-03-23 18:53:59 -0700144 }
145
146 // publish
Kévin PETIT377b2ec2014-02-03 12:35:36 +0000147#ifdef __LP64__
148 android_atomic_release_store64((int64_t) mMutating, (volatile int64_t *) &mNext);
149#else
Glenn Kastendc998c82012-03-23 18:53:59 -0700150 android_atomic_release_store((int32_t) mMutating, (volatile int32_t *) &mNext);
Kévin PETIT377b2ec2014-02-03 12:35:36 +0000151#endif
Glenn Kastendc998c82012-03-23 18:53:59 -0700152 mExpecting = mMutating;
153
154 // copy with circular wraparound
155 if (++mMutating >= &mStates[kN]) {
156 mMutating = &mStates[0];
157 }
158 *mMutating = *mExpecting;
159 mIsDirty = false;
160
161 }
162
163 // optionally wait for this push or a prior push to be acknowledged
164 if (block == BLOCK_UNTIL_ACKED) {
165 if (mExpecting != NULL) {
Glenn Kasten39993082012-05-31 13:40:27 -0700166#ifdef STATE_QUEUE_DUMP
167 unsigned count = 0;
168#endif
Glenn Kastendc998c82012-03-23 18:53:59 -0700169 for (;;) {
170 const T *ack = (const T *) mAck; // no additional barrier needed
171 if (ack == mExpecting) {
172 mExpecting = NULL;
173 break;
174 }
Glenn Kasten39993082012-05-31 13:40:27 -0700175#ifdef STATE_QUEUE_DUMP
176 if (count == 1) {
177 mMutatorDump->mBlockedSequence++;
178 }
179 ++count;
180#endif
Glenn Kastendc998c82012-03-23 18:53:59 -0700181 nanosleep(&req, NULL);
182 }
Glenn Kasten39993082012-05-31 13:40:27 -0700183#ifdef STATE_QUEUE_DUMP
184 if (count > 1) {
185 mMutatorDump->mBlockedSequence++;
186 }
187#endif
Glenn Kastendc998c82012-03-23 18:53:59 -0700188 }
189 }
190
191 return true;
192}
193
194} // namespace android
195
196// hack for gcc
197#ifdef STATE_QUEUE_INSTANTIATIONS
198#include STATE_QUEUE_INSTANTIATIONS
199#endif