SingleStateQueue.h revision 9b82924cbd203f1678c2f78aa73b15909efa3e81
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#ifndef SINGLE_STATE_QUEUE_H
18#define SINGLE_STATE_QUEUE_H
19
20// Non-blocking single element state queue, or
21// Non-blocking single-reader / single-writer multi-word atomic load / store
22
23#include <stdint.h>
24#include <cutils/atomic.h>
25
26namespace android {
27
28template<typename T> class SingleStateQueue {
29
30public:
31
32    class Mutator;
33    class Observer;
34
35    struct Shared {
36        // needs to be part of a union so don't define constructor or destructor
37
38        friend class Mutator;
39        friend class Observer;
40
41private:
42        void                init() { mAck = 0; mSequence = 0; }
43
44        volatile int32_t    mAck;
45        volatile int32_t    mSequence;
46        T                   mValue;
47    };
48
49    class Mutator {
50    public:
51        Mutator(Shared *shared)
52            : mSequence(0), mShared(shared)
53        {
54            // exactly one of Mutator and Observer must initialize, currently it is Observer
55            // shared->init();
56        }
57
58        // push new value onto state queue, overwriting previous value;
59        // returns a sequence number which can be used with ack()
60        int32_t push(const T& value)
61        {
62            Shared *shared = mShared;
63            int32_t sequence = mSequence;
64            sequence++;
65            android_atomic_acquire_store(sequence, &shared->mSequence);
66            shared->mValue = value;
67            sequence++;
68            android_atomic_release_store(sequence, &shared->mSequence);
69            mSequence = sequence;
70            // consider signalling a futex here, if we know that observer is waiting
71            return sequence;
72        }
73
74        // return true if most recent push has been observed
75        bool ack() const
76        {
77            return mShared->mAck - mSequence == 0;
78        }
79
80        // return true if a push with specified sequence number or later has been observed
81        bool ack(int32_t sequence) const
82        {
83            // this relies on 2's complement rollover to detect an ancient sequence number
84            return mShared->mAck - sequence >= 0;
85        }
86
87    private:
88        int32_t     mSequence;
89        Shared * const mShared;
90    };
91
92    class Observer {
93    public:
94        Observer(Shared *shared)
95            : mSequence(0), mSeed(1), mShared(shared)
96        {
97            // exactly one of Mutator and Observer must initialize, currently it is Observer
98            shared->init();
99        }
100
101        // return true if value has changed
102        bool poll(T& value)
103        {
104            Shared *shared = mShared;
105            int32_t before = shared->mSequence;
106            if (before == mSequence) {
107                return false;
108            }
109            for (int tries = 0; ; ) {
110                const int MAX_TRIES = 5;
111                if (before & 1) {
112                    if (++tries >= MAX_TRIES) {
113                        return false;
114                    }
115                    before = shared->mSequence;
116                } else {
117                    android_memory_barrier();
118                    T temp = shared->mValue;
119                    int32_t after = android_atomic_release_load(&shared->mSequence);
120                    if (after == before) {
121                        value = temp;
122                        shared->mAck = before;
123                        mSequence = before;
124                        return true;
125                    }
126                    if (++tries >= MAX_TRIES) {
127                        return false;
128                    }
129                    before = after;
130                }
131            }
132        }
133
134    private:
135        int32_t     mSequence;
136        int         mSeed;  // for PRNG
137        Shared * const mShared;
138    };
139
140#if 0
141    SingleStateQueue(void /*Shared*/ *shared);
142    /*virtual*/ ~SingleStateQueue() { }
143
144    static size_t size() { return sizeof(Shared); }
145#endif
146
147};
148
149}   // namespace android
150
151#endif  // SINGLE_STATE_QUEUE_H
152