1/*
2 * Copyright (C) 2016 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#include <errno.h>
18#include <limits.h>
19#include <audio_utils/fifo_index.h>
20#include <audio_utils/futex.h>
21
22// These are not implemented within <audio_utils/fifo_index.h>
23// so that we don't expose futex.
24
25uint32_t audio_utils_fifo_index::loadAcquire()
26{
27    return atomic_load_explicit(&mIndex, std::memory_order_acquire);
28}
29
30// FIXME should inline this, so that writer32 can also inline it
31void audio_utils_fifo_index::storeRelease(uint32_t value)
32{
33    atomic_store_explicit(&mIndex, value, std::memory_order_release);
34}
35
36int audio_utils_fifo_index::wait(int op, uint32_t expected, const struct timespec *timeout)
37{
38    return sys_futex(&mIndex, op, expected, timeout, NULL, 0);
39}
40
41int audio_utils_fifo_index::wake(int op, int waiters)
42{
43    return sys_futex(&mIndex, op, waiters, NULL, NULL, 0);
44}
45
46uint32_t audio_utils_fifo_index::loadConsume()
47{
48    return atomic_load_explicit(&mIndex, std::memory_order_consume);
49}
50
51////
52
53RefIndexDeferredStoreReleaseDeferredWake::RefIndexDeferredStoreReleaseDeferredWake(
54        audio_utils_fifo_index& index)
55    : mIndex(index), mValue(0), mWriteback(false), mWaiters(0), mWakeOp(FUTEX_WAIT_PRIVATE)
56{
57}
58
59RefIndexDeferredStoreReleaseDeferredWake::~RefIndexDeferredStoreReleaseDeferredWake()
60{
61    writeback();
62    wakeNowIfNeeded();
63}
64
65void RefIndexDeferredStoreReleaseDeferredWake::set(uint32_t value) {
66    mValue = value;
67    mWriteback = true;
68}
69
70void RefIndexDeferredStoreReleaseDeferredWake::writeback()
71{
72    if (mWriteback) {
73        // TODO When part of a collection, should use relaxed for all but the last writeback
74        mIndex.storeRelease(mValue);
75        mWriteback = false;
76    }
77}
78
79void RefIndexDeferredStoreReleaseDeferredWake::writethrough(uint32_t value) {
80    set(value);
81    writeback();
82}
83
84void RefIndexDeferredStoreReleaseDeferredWake::wakeDeferred(int op, int waiters)
85{
86    if (waiters <= 0) {
87        return;
88    }
89    // default is FUTEX_WAKE_PRIVATE
90    if (op == FUTEX_WAKE) {
91        mWakeOp = FUTEX_WAKE;
92    }
93    if (waiters < INT_MAX - mWaiters) {
94        mWaiters += waiters;
95    } else {
96        mWaiters = INT_MAX;
97    }
98}
99
100void RefIndexDeferredStoreReleaseDeferredWake::wakeNowIfNeeded()
101{
102    if (mWaiters > 0) {
103        mIndex.wake(mWakeOp, mWaiters);
104        mWaiters = 0;
105        mWakeOp = FUTEX_WAKE_PRIVATE;
106    }
107}
108
109void RefIndexDeferredStoreReleaseDeferredWake::wakeNow(int op, int waiters)
110{
111    wakeDeferred(op, waiters);
112    wakeNowIfNeeded();
113}
114
115////
116
117RefIndexCachedLoadAcquireDeferredWait::RefIndexCachedLoadAcquireDeferredWait(
118        audio_utils_fifo_index& index)
119    : mIndex(index), mValue(0), mLoaded(false)
120{
121}
122
123RefIndexCachedLoadAcquireDeferredWait::~RefIndexCachedLoadAcquireDeferredWait()
124{
125}
126
127uint32_t RefIndexCachedLoadAcquireDeferredWait::get()
128{
129    prefetch();
130    return mValue;
131}
132
133void RefIndexCachedLoadAcquireDeferredWait::prefetch()
134{
135    if (!mLoaded) {
136        // TODO When part of a collection, should use relaxed for all but the last load
137        mValue = mIndex.loadAcquire();
138        mLoaded = true;
139    }
140}
141
142void RefIndexCachedLoadAcquireDeferredWait::invalidate()
143{
144    mLoaded = false;
145}
146
147#if 0
148uint32_t RefIndexCachedLoadAcquireDeferredWait::readthrough()
149{
150    invalidate();
151    return get();
152}
153#endif
154
155int RefIndexCachedLoadAcquireDeferredWait::wait(int op, const struct timespec *timeout)
156{
157    if (!mLoaded) {
158        return -EINVAL;
159    }
160    int err = mIndex.wait(op, mValue /*expected*/, timeout);
161    invalidate();
162    return err;
163}
164