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    dprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
32}
33
34void 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
43template<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
54template<typename T> StateQueue<T>::~StateQueue()
55{
56}
57
58// Observer APIs
59
60template<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
76template<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
83template<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
94template<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