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