summaryrefslogtreecommitdiff
path: root/libs/ui/FenceTime.cpp
blob: bdfe04b0ddfef5771d9e45f40c64a220c6dac498 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <ui/FenceTime.h>

#define LOG_TAG "FenceTime"

#include <cutils/compiler.h>  // For CC_[UN]LIKELY
#include <utils/Log.h>
#include <inttypes.h>
#include <stdlib.h>

#include <memory>

namespace android {

// ============================================================================
// FenceTime
// ============================================================================

const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE);

FenceTime::FenceTime(const sp<Fence>& fence)
  : mState(((fence.get() != nullptr) && fence->isValid()) ?
            State::VALID : State::INVALID),
    mFence(fence),
    mSignalTime(mState == State::INVALID ?
            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
}

FenceTime::FenceTime(sp<Fence>&& fence)
  : mState(((fence.get() != nullptr) && fence->isValid()) ?
            State::VALID : State::INVALID),
    mFence(std::move(fence)),
    mSignalTime(mState == State::INVALID ?
            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
}

FenceTime::FenceTime(nsecs_t signalTime)
  : mState(Fence::isValidTimestamp(signalTime) ? State::VALID : State::INVALID),
    mFence(nullptr),
    mSignalTime(signalTime) {
    if (CC_UNLIKELY(mSignalTime == Fence::SIGNAL_TIME_PENDING)) {
        ALOGE("Pending signal time not allowed after signal.");
        mSignalTime = Fence::SIGNAL_TIME_INVALID;
    }
}

void FenceTime::applyTrustedSnapshot(const Snapshot& src) {
    if (CC_UNLIKELY(src.state != Snapshot::State::SIGNAL_TIME)) {
        // Applying Snapshot::State::FENCE, could change the valid state of the
        // FenceTime, which is not allowed. Callers should create a new
        // FenceTime from the snapshot instead.
        ALOGE("applyTrustedSnapshot: Unexpected fence.");
        return;
    }

    if (src.state == Snapshot::State::EMPTY) {
        return;
    }

    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
        // We should always get the same signalTime here that we did in
        // getSignalTime(). This check races with getSignalTime(), but it is
        // only a sanity check so that's okay.
        if (CC_UNLIKELY(signalTime != src.signalTime)) {
            ALOGE("FenceTime::applyTrustedSnapshot: signalTime mismatch. "
                    "(%" PRId64 " (old) != %" PRId64 " (new))",
                    signalTime, src.signalTime);
        }
        return;
    }

    std::lock_guard<std::mutex> lock(mMutex);
    mFence.clear();
    mSignalTime.store(src.signalTime, std::memory_order_relaxed);
}

bool FenceTime::isValid() const {
    // We store the valid state in the constructors and return it here.
    // This lets release code remember the valid state even after the
    // underlying fence is destroyed.
    return mState != State::INVALID;
}

nsecs_t FenceTime::getSignalTime() {
    // See if we already have a cached value we can return.
    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
        return signalTime;
    }

    // Hold a reference to the fence on the stack in case the class'
    // reference is removed by another thread. This prevents the
    // fence from being destroyed until the end of this method, where
    // we conveniently do not have the lock held.
    sp<Fence> fence;
    {
        // With the lock acquired this time, see if we have the cached
        // value or if we need to poll the fence.
        std::lock_guard<std::mutex> lock(mMutex);
        if (!mFence.get()) {
            // Another thread set the signal time just before we added the
            // reference to mFence.
            return mSignalTime.load(std::memory_order_relaxed);
        }
        fence = mFence;
    }

    // Make the system call without the lock held.
    signalTime = fence->getSignalTime();

    // Allow tests to override SIGNAL_TIME_INVALID behavior, since tests
    // use invalid underlying Fences without real file descriptors.
    if (CC_UNLIKELY(mState == State::FORCED_VALID_FOR_TEST)) {
        if (signalTime == Fence::SIGNAL_TIME_INVALID) {
            signalTime = Fence::SIGNAL_TIME_PENDING;
        }
    }

    // Make the signal time visible to everyone if it is no longer pending
    // and remove the class' reference to the fence.
    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
        std::lock_guard<std::mutex> lock(mMutex);
        mFence.clear();
        mSignalTime.store(signalTime, std::memory_order_relaxed);
    }

    return signalTime;
}

nsecs_t FenceTime::getCachedSignalTime() const {
    // memory_order_acquire since we don't have a lock fallback path
    // that will do an acquire.
    return mSignalTime.load(std::memory_order_acquire);
}

FenceTime::Snapshot FenceTime::getSnapshot() const {
    // Quick check without the lock.
    nsecs_t signalTime = mSignalTime.load(std::memory_order_relaxed);
    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
        return Snapshot(signalTime);
    }

    // Do the full check with the lock.
    std::lock_guard<std::mutex> lock(mMutex);
    signalTime = mSignalTime.load(std::memory_order_relaxed);
    if (signalTime != Fence::SIGNAL_TIME_PENDING) {
        return Snapshot(signalTime);
    }
    return Snapshot(mFence);
}

// For tests only. If forceValidForTest is true, then getSignalTime will
// never return SIGNAL_TIME_INVALID and isValid will always return true.
FenceTime::FenceTime(const sp<Fence>& fence, bool forceValidForTest)
  : mState(forceValidForTest ?
            State::FORCED_VALID_FOR_TEST : State::INVALID),
    mFence(fence),
    mSignalTime(mState == State::INVALID ?
            Fence::SIGNAL_TIME_INVALID : Fence::SIGNAL_TIME_PENDING) {
}

void FenceTime::signalForTest(nsecs_t signalTime) {
    // To be realistic, this should really set a hidden value that
    // gets picked up in the next call to getSignalTime, but this should
    // be good enough.
    std::lock_guard<std::mutex> lock(mMutex);
    mFence.clear();
    mSignalTime.store(signalTime, std::memory_order_relaxed);
}

// ============================================================================
// FenceTime::Snapshot
// ============================================================================
FenceTime::Snapshot::Snapshot(const sp<Fence>& srcFence)
    : state(State::FENCE), fence(srcFence) {
}

FenceTime::Snapshot::Snapshot(nsecs_t srcSignalTime)
    : state(State::SIGNAL_TIME), signalTime(srcSignalTime) {
}

size_t FenceTime::Snapshot::getFlattenedSize() const {
    constexpr size_t min = sizeof(state);
    switch (state) {
        case State::EMPTY:
            return min;
        case State::FENCE:
            return min + fence->getFlattenedSize();
        case State::SIGNAL_TIME:
            return min + sizeof(signalTime);
    }
    return 0;
}

size_t FenceTime::Snapshot::getFdCount() const {
    return state == State::FENCE ? fence->getFdCount() : 0u;
}

status_t FenceTime::Snapshot::flatten(
        void*& buffer, size_t& size, int*& fds, size_t& count) const {
    if (size < getFlattenedSize()) {
        return NO_MEMORY;
    }

    FlattenableUtils::write(buffer, size, state);
    switch (state) {
        case State::EMPTY:
            return NO_ERROR;
        case State::FENCE:
            return fence->flatten(buffer, size, fds, count);
        case State::SIGNAL_TIME:
            FlattenableUtils::write(buffer, size, signalTime);
            return NO_ERROR;
    }

    return NO_ERROR;
}

status_t FenceTime::Snapshot::unflatten(
        void const*& buffer, size_t& size, int const*& fds, size_t& count) {
    if (size < sizeof(state)) {
        return NO_MEMORY;
    }

    FlattenableUtils::read(buffer, size, state);
    switch (state) {
        case State::EMPTY:
            return NO_ERROR;
        case State::FENCE:
            fence = new Fence;
            return fence->unflatten(buffer, size, fds, count);
        case State::SIGNAL_TIME:
            if (size < sizeof(signalTime)) {
                return NO_MEMORY;
            }
            FlattenableUtils::read(buffer, size, signalTime);
            return NO_ERROR;
    }

    return NO_ERROR;
}

// ============================================================================
// FenceTimeline
// ============================================================================
void FenceTimeline::push(const std::shared_ptr<FenceTime>& fence) {
    std::lock_guard<std::mutex> lock(mMutex);
    while (mQueue.size() >= MAX_ENTRIES) {
        // This is a sanity check to make sure the queue doesn't grow unbounded.
        // MAX_ENTRIES should be big enough not to trigger this path.
        // In case this path is taken though, users of FenceTime must make sure
        // not to rely solely on FenceTimeline to get the final timestamp and
        // should eventually call Fence::getSignalTime on their own.
        std::shared_ptr<FenceTime> front = mQueue.front().lock();
        if (front) {
            // Make a last ditch effort to get the signalTime here since
            // we are removing it from the timeline.
            front->getSignalTime();
        }
        mQueue.pop();
    }
    mQueue.push(fence);
}

void FenceTimeline::updateSignalTimes() {
    std::lock_guard<std::mutex> lock(mMutex);
    while (!mQueue.empty()) {
        std::shared_ptr<FenceTime> fence = mQueue.front().lock();
        if (!fence) {
            // The shared_ptr no longer exists and no one cares about the
            // timestamp anymore.
            mQueue.pop();
            continue;
        } else if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
            // The fence has signaled and we've removed the sp<Fence> ref.
            mQueue.pop();
            continue;
        } else {
            // The fence didn't signal yet. Break since the later ones
            // shouldn't have signaled either.
            break;
        }
    }
}

// ============================================================================
// FenceToFenceTimeMap
// ============================================================================
std::shared_ptr<FenceTime> FenceToFenceTimeMap::createFenceTimeForTest(
        const sp<Fence>& fence) {
    std::lock_guard<std::mutex> lock(mMutex);
    // Always garbage collecting isn't efficient, but this is only for testing.
    garbageCollectLocked();
    std::shared_ptr<FenceTime> fenceTime(new FenceTime(fence, true));
    mMap[fence.get()].push_back(fenceTime);
    return fenceTime;
}

void FenceToFenceTimeMap::signalAllForTest(
        const sp<Fence>& fence, nsecs_t signalTime) {
    bool signaled = false;

    std::lock_guard<std::mutex> lock(mMutex);
    auto it = mMap.find(fence.get());
    if (it != mMap.end()) {
        for (auto& weakFenceTime : it->second) {
            std::shared_ptr<FenceTime> fenceTime = weakFenceTime.lock();
            if (!fenceTime) {
                continue;
            }
            ALOGE_IF(!fenceTime->isValid(),
                    "signalAllForTest: Signaling invalid fence.");
            fenceTime->signalForTest(signalTime);
            signaled = true;
        }
    }

    ALOGE_IF(!signaled, "signalAllForTest: Nothing to signal.");
}

void FenceToFenceTimeMap::garbageCollectLocked() {
    for (auto& it : mMap) {
        // Erase all expired weak pointers from the vector.
        auto& vect = it.second;
        vect.erase(
                std::remove_if(vect.begin(), vect.end(),
                        [](const std::weak_ptr<FenceTime>& ft) {
                            return ft.expired();
                        }),
                vect.end());

        // Also erase the map entry if the vector is now empty.
        if (vect.empty()) {
            mMap.erase(it.first);
        }
    }
}

} // namespace android