• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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_NDEBUG 0
18 #define LOG_TAG "C2SurfaceSyncObj"
19 #include <limits.h>
20 #include <linux/futex.h>
21 #include <sys/mman.h>
22 #include <sys/syscall.h>
23 #include <sys/time.h>
24 #include <utils/Log.h>
25 
26 #include <chrono>
27 #include <C2SurfaceSyncObj.h>
28 
29 const native_handle_t C2SurfaceSyncMemory::HandleSyncMem::cHeader = {
30     C2SurfaceSyncMemory::HandleSyncMem::version,
31     C2SurfaceSyncMemory::HandleSyncMem::numFds,
32     C2SurfaceSyncMemory::HandleSyncMem::numInts,
33     {}
34 };
35 
isValid(const native_handle_t * const o)36 bool C2SurfaceSyncMemory::HandleSyncMem::isValid(const native_handle_t * const o) {
37     if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
38         return false;
39     }
40 
41     const HandleSyncMem *other = static_cast<const HandleSyncMem*>(o);
42     return other->mInts.mMagic == kMagic;
43 }
44 
C2SurfaceSyncMemory()45 C2SurfaceSyncMemory::C2SurfaceSyncMemory()
46     : mInit(false), mHandle(nullptr), mMem(nullptr) {}
47 
~C2SurfaceSyncMemory()48 C2SurfaceSyncMemory::~C2SurfaceSyncMemory() {
49     if (mInit) {
50         if (mMem) {
51             munmap(static_cast<void *>(mMem), mHandle->size());
52         }
53         if (mHandle) {
54             native_handle_close(mHandle);
55             native_handle_delete(mHandle);
56         }
57     }
58 }
59 
Import(native_handle_t * handle)60 std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Import(
61         native_handle_t *handle) {
62     if (!HandleSyncMem::isValid(handle)) {
63         return nullptr;
64     }
65 
66     HandleSyncMem *o = static_cast<HandleSyncMem*>(handle);
67     if (o->size() < sizeof(C2SyncVariables)) {
68         android_errorWriteLog(0x534e4554, "240140929");
69         return nullptr;
70     }
71 
72     void *ptr = mmap(NULL, o->size(), PROT_READ | PROT_WRITE, MAP_SHARED, o->memFd(), 0);
73 
74     if (ptr == MAP_FAILED) {
75         native_handle_close(handle);
76         native_handle_delete(handle);
77         return nullptr;
78     }
79 
80     std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
81     syncMem->mInit = true;
82     syncMem->mHandle = o;
83     syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
84     return syncMem;
85 }
86 
Create(int fd,size_t size)87 std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Create(int fd, size_t size) {
88     if (fd < 0 || size == 0) {
89         return nullptr;
90     }
91     HandleSyncMem *handle = new HandleSyncMem(fd, size);
92 
93     void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
94     if (ptr == MAP_FAILED) {
95         native_handle_close(handle);
96         native_handle_delete(handle);
97         return nullptr;
98     }
99     memset(ptr, 0, size);
100 
101     std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
102     syncMem->mInit = true;
103     syncMem->mHandle = handle;
104     syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
105     return syncMem;
106 }
107 
handle()108 native_handle_t *C2SurfaceSyncMemory::handle() {
109     return !mInit ? nullptr : mHandle;
110 }
111 
mem()112 C2SyncVariables *C2SurfaceSyncMemory::mem() {
113     return !mInit ? nullptr : mMem;
114 }
115 
116 namespace {
117     constexpr int kSpinNumForLock = 0;
118     constexpr int kSpinNumForUnlock = 0;
119 
120     enum : uint32_t {
121         FUTEX_UNLOCKED = 0,
122         FUTEX_LOCKED_UNCONTENDED = 1,  // user-space locking
123         FUTEX_LOCKED_CONTENDED = 2,    // futex locking
124     };
125 }
126 
lock()127 int C2SyncVariables::lock() {
128     uint32_t old = FUTEX_UNLOCKED;
129 
130     // see if we can lock uncontended immediately (if previously unlocked)
131     if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
132         return 0;
133     }
134 
135     // spin to see if we can get it with a short wait without involving kernel
136     for (int i = 0; i < kSpinNumForLock; i++) {
137         sched_yield();
138 
139         old = FUTEX_UNLOCKED;
140         if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
141             return 0;
142         }
143     }
144 
145     // still locked, if other side thinks it was uncontended, now it is contended, so let them
146     // know that they need to wake us up.
147     if (old == FUTEX_LOCKED_UNCONTENDED) {
148         old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
149         // It is possible that the other holder released the lock at this very moment (and old
150         // becomes UNLOCKED), If so, we will not involve the kernel to wait for the lock to be
151         // released, but are still marking our lock contended (even though we are the only
152         // holders.)
153     }
154 
155     // while the futex is still locked by someone else
156     while (old != FUTEX_UNLOCKED) {
157         // wait until other side releases the lock (and still contented)
158         (void)syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
159         // try to relock
160         old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
161     }
162     return 0;
163 }
164 
unlock()165 int C2SyncVariables::unlock() {
166     // TRICKY: here we assume that we are holding this lock
167 
168     // unlock the lock immediately (since we were holding it)
169     // If it is (still) locked uncontested, we are done (no need to involve the kernel)
170     if (mLock.exchange(FUTEX_UNLOCKED) == FUTEX_LOCKED_UNCONTENDED) {
171         return 0;
172     }
173 
174     // We don't need to spin for unlock as here we know already we have a waiter who we need to
175     // wake up. This code was here in case someone just happened to lock this lock (uncontested)
176     // before we would wake up other waiters to avoid a syscall. It is unsure if this ever gets
177     // exercised or if this is the behavior we want. (Note that if this code is removed, the same
178     // situation is still handled in lock() by the woken up waiter that realizes that the lock is
179     // now taken.)
180     for (int i = 0; i < kSpinNumForUnlock; i++) {
181         // here we seem to check if someone relocked this lock, and if they relocked uncontested,
182         // we up it to contested (since there are other waiters.)
183         if (mLock.load() != FUTEX_UNLOCKED) {
184             uint32_t old = FUTEX_LOCKED_UNCONTENDED;
185             mLock.compare_exchange_strong(old, FUTEX_LOCKED_CONTENDED);
186             // this is always true here so we return immediately
187             if (old) {
188                 return 0;
189             }
190         }
191         sched_yield();
192     }
193 
194     // wake up one waiter
195     (void)syscall(__NR_futex, &mLock, FUTEX_WAKE, 1, NULL, NULL, 0);
196     return 0;
197 }
198 
setInitialDequeueCountLocked(int32_t maxDequeueCount,int32_t curDequeueCount)199 void C2SyncVariables::setInitialDequeueCountLocked(
200         int32_t maxDequeueCount, int32_t curDequeueCount) {
201     mMaxDequeueCount = maxDequeueCount;
202     mCurDequeueCount = curDequeueCount;
203 }
204 
getWaitIdLocked()205 uint32_t C2SyncVariables::getWaitIdLocked() {
206     return mCond.load();
207 }
208 
isDequeueableLocked(uint32_t * waitId)209 bool C2SyncVariables::isDequeueableLocked(uint32_t *waitId) {
210     if (mMaxDequeueCount <= mCurDequeueCount) {
211         if (waitId) {
212             *waitId = getWaitIdLocked();
213         }
214         return false;
215     }
216     return true;
217 }
218 
notifyQueuedLocked(uint32_t * waitId,bool notify)219 bool C2SyncVariables::notifyQueuedLocked(uint32_t *waitId, bool notify) {
220     // Note. thundering herds may occur. Edge trigged signalling.
221     // But one waiter will guarantee to dequeue. others may wait again.
222     // Minimize futex syscall(trap) for the main use case(one waiter case).
223     if (mMaxDequeueCount == mCurDequeueCount--) {
224         if (notify) {
225             broadcast();
226         }
227         return true;
228     }
229 
230     if (mCurDequeueCount >= mMaxDequeueCount) {
231         if (waitId) {
232             *waitId = getWaitIdLocked();
233         }
234         ALOGV("dequeue blocked %d/%d", mCurDequeueCount, mMaxDequeueCount);
235         return false;
236     }
237     return true;
238 }
239 
notifyDequeuedLocked()240 void C2SyncVariables::notifyDequeuedLocked() {
241     mCurDequeueCount++;
242     ALOGV("dequeue successful %d/%d", mCurDequeueCount, mMaxDequeueCount);
243 }
244 
setSyncStatusLocked(SyncStatus status)245 void C2SyncVariables::setSyncStatusLocked(SyncStatus status) {
246     mStatus = status;
247     if (mStatus == STATUS_ACTIVE) {
248         broadcast();
249     }
250 }
251 
getSyncStatusLocked()252 C2SyncVariables::SyncStatus C2SyncVariables::getSyncStatusLocked() {
253     return mStatus;
254 }
255 
updateMaxDequeueCountLocked(int32_t maxDequeueCount)256 void C2SyncVariables::updateMaxDequeueCountLocked(int32_t maxDequeueCount) {
257     mMaxDequeueCount = maxDequeueCount;
258     if (mStatus == STATUS_ACTIVE) {
259         broadcast();
260     }
261 }
262 
waitForChange(uint32_t waitId,c2_nsecs_t timeoutNs)263 c2_status_t C2SyncVariables::waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs) {
264     if (timeoutNs < 0) {
265         timeoutNs = 0;
266     }
267     struct timespec tv;
268     tv.tv_sec = timeoutNs / 1000000000;
269     tv.tv_nsec = timeoutNs % 1000000000;
270 
271     int ret =  syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0);
272     if (ret == 0 || errno == EAGAIN) {
273         return C2_OK;
274     }
275     if (errno == EINTR || errno == ETIMEDOUT) {
276         return C2_TIMED_OUT;
277     }
278     return C2_BAD_VALUE;
279 }
280 
notifyAll()281 void C2SyncVariables::notifyAll() {
282     this->lock();
283     this->broadcast();
284     this->unlock();
285 }
286 
signal()287 int C2SyncVariables::signal() {
288     mCond++;
289 
290     (void) syscall(__NR_futex, &mCond, FUTEX_WAKE, 1, NULL, NULL, 0);
291     return 0;
292 }
293 
broadcast()294 int C2SyncVariables::broadcast() {
295     mCond++;
296 
297     (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, 1, (void *)INT_MAX, &mLock, 0);
298     return 0;
299 }
300 
wait()301 int C2SyncVariables::wait() {
302     uint32_t old = mCond.load();
303     unlock();
304 
305     (void) syscall(__NR_futex, &mCond, FUTEX_WAIT, old, NULL, NULL, 0);
306     while (mLock.exchange(FUTEX_LOCKED_CONTENDED)) {
307         (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
308     }
309     return 0;
310 }
311