• 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     void *ptr = mmap(NULL, o->size(), PROT_READ | PROT_WRITE, MAP_SHARED, o->memFd(), 0);
68 
69     if (ptr == MAP_FAILED) {
70         native_handle_close(handle);
71         native_handle_delete(handle);
72         return nullptr;
73     }
74 
75     std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
76     syncMem->mInit = true;
77     syncMem->mHandle = o;
78     syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
79     return syncMem;
80 }
81 
Create(int fd,size_t size)82 std::shared_ptr<C2SurfaceSyncMemory> C2SurfaceSyncMemory::Create(int fd, size_t size) {
83     if (fd < 0 || size == 0) {
84         return nullptr;
85     }
86     HandleSyncMem *handle = new HandleSyncMem(fd, size);
87 
88     void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
89     if (ptr == MAP_FAILED) {
90         native_handle_close(handle);
91         native_handle_delete(handle);
92         return nullptr;
93     }
94     memset(ptr, 0, size);
95 
96     std::shared_ptr<C2SurfaceSyncMemory> syncMem(new C2SurfaceSyncMemory);
97     syncMem->mInit = true;
98     syncMem->mHandle = handle;
99     syncMem->mMem = static_cast<C2SyncVariables*>(ptr);
100     return syncMem;
101 }
102 
handle()103 native_handle_t *C2SurfaceSyncMemory::handle() {
104     return !mInit ? nullptr : mHandle;
105 }
106 
mem()107 C2SyncVariables *C2SurfaceSyncMemory::mem() {
108     return !mInit ? nullptr : mMem;
109 }
110 
111 namespace {
112     constexpr int kSpinNumForLock = 100;
113     constexpr int kSpinNumForUnlock = 200;
114 
115     enum : uint32_t {
116         FUTEX_UNLOCKED = 0,
117         FUTEX_LOCKED_UNCONTENDED = 1,  // user-space locking
118         FUTEX_LOCKED_CONTENDED = 2,    // futex locking
119     };
120 }
121 
lock()122 int C2SyncVariables::lock() {
123     uint32_t old;
124     for (int i = 0; i < kSpinNumForLock; i++) {
125         old = 0;
126         if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
127             return 0;
128         }
129         sched_yield();
130     }
131 
132     if (old == FUTEX_LOCKED_UNCONTENDED)
133         old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
134 
135     while (old) {
136         (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
137         old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
138     }
139     return 0;
140 }
141 
unlock()142 int C2SyncVariables::unlock() {
143     if (mLock.exchange(FUTEX_UNLOCKED) == FUTEX_LOCKED_UNCONTENDED) return 0;
144 
145     for (int i = 0; i < kSpinNumForUnlock; i++) {
146         if (mLock.load()) {
147             uint32_t old = FUTEX_LOCKED_UNCONTENDED;
148             mLock.compare_exchange_strong(old, FUTEX_LOCKED_CONTENDED);
149             if (old) {
150                 return 0;
151             }
152         }
153         sched_yield();
154     }
155 
156     (void) syscall(__NR_futex, &mLock, FUTEX_WAKE, 1, NULL, NULL, 0);
157     return 0;
158 }
159 
setInitialDequeueCountLocked(int32_t maxDequeueCount,int32_t curDequeueCount)160 void C2SyncVariables::setInitialDequeueCountLocked(
161         int32_t maxDequeueCount, int32_t curDequeueCount) {
162     mMaxDequeueCount = maxDequeueCount;
163     mCurDequeueCount = curDequeueCount;
164 }
165 
getWaitIdLocked()166 uint32_t C2SyncVariables::getWaitIdLocked() {
167     return mCond.load();
168 }
169 
isDequeueableLocked(uint32_t * waitId)170 bool C2SyncVariables::isDequeueableLocked(uint32_t *waitId) {
171     if (mMaxDequeueCount <= mCurDequeueCount) {
172         if (waitId) {
173             *waitId = getWaitIdLocked();
174         }
175         return false;
176     }
177     return true;
178 }
179 
notifyQueuedLocked(uint32_t * waitId)180 bool C2SyncVariables::notifyQueuedLocked(uint32_t *waitId) {
181     // Note. thundering herds may occur. Edge trigged signalling.
182     // But one waiter will guarantee to dequeue. others may wait again.
183     // Minimize futex syscall(trap) for the main use case(one waiter case).
184     if (mMaxDequeueCount == mCurDequeueCount--) {
185         broadcast();
186         return true;
187     }
188 
189     if (mCurDequeueCount >= mMaxDequeueCount) {
190         if (waitId) {
191             *waitId = getWaitIdLocked();
192         }
193         ALOGV("dequeue blocked %d/%d", mCurDequeueCount, mMaxDequeueCount);
194         return false;
195     }
196     return true;
197 }
198 
notifyDequeuedLocked()199 void C2SyncVariables::notifyDequeuedLocked() {
200     mCurDequeueCount++;
201     ALOGV("dequeue successful %d/%d", mCurDequeueCount, mMaxDequeueCount);
202 }
203 
setSyncStatusLocked(SyncStatus status)204 void C2SyncVariables::setSyncStatusLocked(SyncStatus status) {
205     mStatus = status;
206     if (mStatus == STATUS_ACTIVE) {
207         broadcast();
208     }
209 }
210 
getSyncStatusLocked()211 C2SyncVariables::SyncStatus C2SyncVariables::getSyncStatusLocked() {
212     return mStatus;
213 }
214 
updateMaxDequeueCountLocked(int32_t maxDequeueCount)215 void C2SyncVariables::updateMaxDequeueCountLocked(int32_t maxDequeueCount) {
216     mMaxDequeueCount = maxDequeueCount;
217     if (mStatus == STATUS_ACTIVE) {
218         broadcast();
219     }
220 }
221 
waitForChange(uint32_t waitId,c2_nsecs_t timeoutNs)222 c2_status_t C2SyncVariables::waitForChange(uint32_t waitId, c2_nsecs_t timeoutNs) {
223     if (timeoutNs < 0) {
224         timeoutNs = 0;
225     }
226     struct timespec tv;
227     tv.tv_sec = timeoutNs / 1000000000;
228     tv.tv_nsec = timeoutNs % 1000000000;
229 
230     int ret =  syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0);
231     if (ret == 0 || ret == EAGAIN) {
232         return C2_OK;
233     }
234     if (ret == EINTR || ret == ETIMEDOUT) {
235         return C2_TIMED_OUT;
236     }
237     return C2_BAD_VALUE;
238 }
239 
signal()240 int C2SyncVariables::signal() {
241     mCond++;
242 
243     (void) syscall(__NR_futex, &mCond, FUTEX_WAKE, 1, NULL, NULL, 0);
244     return 0;
245 }
246 
broadcast()247 int C2SyncVariables::broadcast() {
248     mCond++;
249 
250     (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, 1, (void *)INT_MAX, &mLock, 0);
251     return 0;
252 }
253 
wait()254 int C2SyncVariables::wait() {
255     uint32_t old = mCond.load();
256     unlock();
257 
258     (void) syscall(__NR_futex, &mCond, FUTEX_WAIT, old, NULL, NULL, 0);
259     while (mLock.exchange(FUTEX_LOCKED_CONTENDED)) {
260         (void) syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, NULL, NULL, 0);
261     }
262     return 0;
263 }
264