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