• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SyncThread.h"
18 
19 #include "base/System.h"
20 #include "base/Thread.h"
21 #include "OpenGLESDispatch/OpenGLDispatchLoader.h"
22 #include "host-common/crash_reporter.h"
23 #include "host-common/sync_device.h"
24 
25 #ifndef _MSC_VER
26 #include <sys/time.h>
27 #endif
28 #include <memory>
29 
30 #define DEBUG 0
31 
32 #if DEBUG
33 
curr_ms()34 static uint64_t curr_ms() {
35     struct timeval tv;
36     gettimeofday(&tv, NULL);
37     return tv.tv_usec / 1000 + tv.tv_sec * 1000;
38 }
39 
40 #define DPRINT(fmt, ...) do { \
41     if (!VERBOSE_CHECK(syncthreads)) VERBOSE_ENABLE(syncthreads); \
42     VERBOSE_TID_FUNCTION_DPRINT(syncthreads, "@ time=%llu: " fmt, curr_ms(), ##__VA_ARGS__); \
43 } while(0)
44 
45 #else
46 
47 #define DPRINT(...)
48 
49 #endif
50 
51 // The single global sync thread instance.
52 class GlobalSyncThread {
53 public:
54     GlobalSyncThread() = default;
55 
syncThreadPtr()56     SyncThread* syncThreadPtr() { return &mSyncThread; }
57 
58 private:
59     SyncThread mSyncThread;
60 };
61 
sGlobalSyncThread()62 static GlobalSyncThread* sGlobalSyncThread() {
63     static GlobalSyncThread* t = new GlobalSyncThread;
64     return t;
65 }
66 
67 static const uint32_t kTimelineInterval = 1;
68 static const uint64_t kDefaultTimeoutNsecs = 5ULL * 1000ULL * 1000ULL * 1000ULL;
69 static const uint64_t kNumWorkerThreads = 4u;
70 
SyncThread()71 SyncThread::SyncThread()
72     : android::base::Thread(android::base::ThreadFlags::MaskSignals, 512 * 1024),
73       mWorkerThreadPool(kNumWorkerThreads, [this](SyncThreadCmd&& cmd) {
74           doSyncThreadCmd(&cmd);
75       }) {
76     this->start();
77     mWorkerThreadPool.start();
78     initSyncContext();
79 }
80 
~SyncThread()81 SyncThread::~SyncThread() {
82     cleanup();
83 }
84 
triggerWait(FenceSync * fenceSync,uint64_t timeline)85 void SyncThread::triggerWait(FenceSync* fenceSync,
86                              uint64_t timeline) {
87     DPRINT("fenceSyncInfo=0x%llx timeline=0x%lx ...",
88             fenceSync, timeline);
89     SyncThreadCmd to_send;
90     to_send.opCode = SYNC_THREAD_WAIT;
91     to_send.fenceSync = fenceSync;
92     to_send.timeline = timeline;
93     DPRINT("opcode=%u", to_send.opCode);
94     sendAsync(to_send);
95     DPRINT("exit");
96 }
97 
triggerWaitVk(VkFence vkFence,uint64_t timeline)98 void SyncThread::triggerWaitVk(VkFence vkFence, uint64_t timeline) {
99     DPRINT("fenceSyncInfo=0x%llx timeline=0x%lx ...", fenceSync, timeline);
100     SyncThreadCmd to_send;
101     to_send.opCode = SYNC_THREAD_WAIT_VK;
102     to_send.vkFence = vkFence;
103     to_send.timeline = timeline;
104     DPRINT("opcode=%u", to_send.opCode);
105     sendAsync(to_send);
106     DPRINT("exit");
107 }
108 
triggerBlockedWaitNoTimeline(FenceSync * fenceSync)109 void SyncThread::triggerBlockedWaitNoTimeline(FenceSync* fenceSync) {
110     DPRINT("fenceSyncInfo=0x%llx ...", fenceSync);
111     SyncThreadCmd to_send;
112     to_send.opCode = SYNC_THREAD_BLOCKED_WAIT_NO_TIMELINE;
113     to_send.fenceSync = fenceSync;
114     DPRINT("opcode=%u", to_send.opCode);
115     sendAndWaitForResult(to_send);
116     DPRINT("exit");
117 }
118 
cleanup()119 void SyncThread::cleanup() {
120     DPRINT("enter");
121     SyncThreadCmd to_send;
122     to_send.opCode = SYNC_THREAD_EXIT;
123     sendAndWaitForResult(to_send);
124     DPRINT("signal");
125     mLock.lock();
126     mExiting = true;
127     mCv.signalAndUnlock(&mLock);
128     DPRINT("exit");
129 }
130 
131 // Private methods below////////////////////////////////////////////////////////
132 
initSyncContext()133 void SyncThread::initSyncContext() {
134     DPRINT("enter");
135     // TODO(b/187082169, warty): The thread pool's command-assignment strategy
136     // is round-robin, so as a hack, create one command for each worker.
137     for (int i = 0; i < mWorkerThreadPool.numWorkers(); i++) {
138         SyncThreadCmd to_send;
139         to_send.opCode = SYNC_THREAD_INIT;
140         sendAndWaitForResult(to_send);
141     }
142     DPRINT("exit");
143 }
144 
main()145 intptr_t SyncThread::main() {
146     DPRINT("in sync thread");
147     mLock.lock();
148     mCv.wait(&mLock, [this] { return mExiting; });
149 
150     mWorkerThreadPool.done();
151     mWorkerThreadPool.join();
152     DPRINT("exited sync thread");
153     return 0;
154 }
155 
sendAndWaitForResult(SyncThreadCmd & cmd)156 int SyncThread::sendAndWaitForResult(SyncThreadCmd& cmd) {
157     DPRINT("send with opcode=%d", cmd.opCode);
158     android::base::Lock lock;
159     android::base::ConditionVariable cond;
160     android::base::Optional<int> result = android::base::kNullopt;
161     cmd.lock = &lock;
162     cmd.cond = &cond;
163     cmd.result = &result;
164 
165     lock.lock();
166     mWorkerThreadPool.enqueue(std::move(cmd));
167     cond.wait(&lock, [&result] { return result.hasValue(); });
168 
169     DPRINT("result=%d", *result);
170     return *result;
171 }
172 
sendAsync(SyncThreadCmd & cmd)173 void SyncThread::sendAsync(SyncThreadCmd& cmd) {
174     DPRINT("send with opcode=%u fenceSyncInfo=0x%llx",
175            cmd.opCode, cmd.fenceSync);
176     mWorkerThreadPool.enqueue(std::move(cmd));
177 }
178 
doSyncContextInit()179 void SyncThread::doSyncContextInit() {
180     const EGLDispatch* egl = emugl::LazyLoadedEGLDispatch::get();
181 
182     mDisplay = egl->eglGetDisplay(EGL_DEFAULT_DISPLAY);
183     int eglMaj, eglMin;
184     egl->eglInitialize(mDisplay, &eglMaj , &eglMin);
185 
186     const EGLint configAttribs[] = {
187         EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
188         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
189         EGL_RED_SIZE, 8,
190         EGL_GREEN_SIZE, 8,
191         EGL_BLUE_SIZE, 8,
192         EGL_NONE,
193     };
194 
195     EGLint nConfigs;
196     EGLConfig config;
197 
198     egl->eglChooseConfig(mDisplay, configAttribs, &config, 1, &nConfigs);
199 
200     const EGLint pbufferAttribs[] = {
201         EGL_WIDTH, 1,
202         EGL_HEIGHT, 1,
203         EGL_NONE,
204     };
205 
206     mSurface =
207         egl->eglCreatePbufferSurface(mDisplay, config, pbufferAttribs);
208 
209     const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
210     mContext = egl->eglCreateContext(mDisplay, config, EGL_NO_CONTEXT, contextAttribs);
211 
212     egl->eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
213 }
214 
doSyncWait(SyncThreadCmd * cmd)215 void SyncThread::doSyncWait(SyncThreadCmd* cmd) {
216     DPRINT("enter");
217 
218     FenceSync* fenceSync =
219         FenceSync::getFromHandle((uint64_t)(uintptr_t)cmd->fenceSync);
220 
221     if (!fenceSync) {
222         emugl::emugl_sync_timeline_inc(cmd->timeline, kTimelineInterval);
223         return;
224     }
225 
226     EGLint wait_result = 0x0;
227 
228     DPRINT("wait on sync obj: %p", cmd->fenceSync);
229     wait_result = cmd->fenceSync->wait(kDefaultTimeoutNsecs);
230 
231     DPRINT("done waiting, with wait result=0x%x. "
232            "increment timeline (and signal fence)",
233            wait_result);
234 
235     if (wait_result != EGL_CONDITION_SATISFIED_KHR) {
236         DPRINT("error: eglClientWaitSync abnormal exit 0x%x. sync handle 0x%llx\n",
237                wait_result, (unsigned long long)cmd->fenceSync);
238     }
239 
240     DPRINT("issue timeline increment");
241 
242     // We always unconditionally increment timeline at this point, even
243     // if the call to eglClientWaitSync returned abnormally.
244     // There are three cases to consider:
245     // - EGL_CONDITION_SATISFIED_KHR: either the sync object is already
246     //   signaled and we need to increment this timeline immediately, or
247     //   we have waited until the object is signaled, and then
248     //   we increment the timeline.
249     // - EGL_TIMEOUT_EXPIRED_KHR: the fence command we put in earlier
250     //   in the OpenGL stream is not actually ever signaled, and we
251     //   end up blocking in the above eglClientWaitSyncKHR call until
252     //   our timeout runs out. In this case, provided we have waited
253     //   for |kDefaultTimeoutNsecs|, the guest will have received all
254     //   relevant error messages about fence fd's not being signaled
255     //   in time, so we are properly emulating bad behavior even if
256     //   we now increment the timeline.
257     // - EGL_FALSE (error): chances are, the underlying EGL implementation
258     //   on the host doesn't actually support fence objects. In this case,
259     //   we should fail safe: 1) It must be only very old or faulty
260     //   graphics drivers / GPU's that don't support fence objects.
261     //   2) The consequences of signaling too early are generally, out of
262     //   order frames and scrambled textures in some apps. But, not
263     //   incrementing the timeline means that the app's rendering freezes.
264     //   So, despite the faulty GPU driver, not incrementing is too heavyweight a response.
265 
266     emugl::emugl_sync_timeline_inc(cmd->timeline, kTimelineInterval);
267     FenceSync::incrementTimelineAndDeleteOldFences();
268 
269     DPRINT("done timeline increment");
270 
271     DPRINT("exit");
272 }
273 
doSyncWaitVk(SyncThreadCmd * cmd)274 int SyncThread::doSyncWaitVk(SyncThreadCmd* cmd) {
275     DPRINT("enter");
276 
277     auto decoder = goldfish_vk::VkDecoderGlobalState::get();
278     auto result = decoder->waitForFence(cmd->vkFence, kDefaultTimeoutNsecs);
279     if (result == VK_TIMEOUT) {
280         fprintf(stderr, "SyncThread::%s: SYNC_WAIT_VK timeout: vkFence=%p\n",
281                 __func__, cmd->vkFence);
282     } else if (result != VK_SUCCESS) {
283         fprintf(stderr, "SyncThread::%s: SYNC_WAIT_VK error: %d vkFence=%p\n",
284                 __func__, result, cmd->vkFence);
285     }
286 
287     DPRINT("issue timeline increment");
288 
289     // We always unconditionally increment timeline at this point, even
290     // if the call to vkWaitForFences returned abnormally.
291     // See comments in |doSyncWait| about the rationale.
292     emugl::emugl_sync_timeline_inc(cmd->timeline, kTimelineInterval);
293 
294     DPRINT("done timeline increment");
295 
296     DPRINT("exit");
297     return result;
298 }
299 
doSyncBlockedWaitNoTimeline(SyncThreadCmd * cmd)300 void SyncThread::doSyncBlockedWaitNoTimeline(SyncThreadCmd* cmd) {
301     DPRINT("enter");
302 
303     FenceSync* fenceSync =
304         FenceSync::getFromHandle((uint64_t)(uintptr_t)cmd->fenceSync);
305 
306     if (!fenceSync) {
307         return;
308     }
309 
310     EGLint wait_result = 0x0;
311 
312     DPRINT("wait on sync obj: %p", cmd->fenceSync);
313     wait_result = cmd->fenceSync->wait(kDefaultTimeoutNsecs);
314 
315     DPRINT("done waiting, with wait result=0x%x. "
316            "increment timeline (and signal fence)",
317            wait_result);
318 
319     if (wait_result != EGL_CONDITION_SATISFIED_KHR) {
320         EGLint error = s_egl.eglGetError();
321         fprintf(stderr, "error: eglClientWaitSync abnormal exit 0x%x %p %#x\n",
322                 wait_result, cmd->fenceSync, error);
323     }
324 }
325 
doExit()326 void SyncThread::doExit() {
327 
328     if (mContext == EGL_NO_CONTEXT) return;
329 
330     const EGLDispatch* egl = emugl::LazyLoadedEGLDispatch::get();
331 
332     egl->eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
333     egl->eglDestroyContext(mDisplay, mContext);
334     egl->eglDestroySurface(mDisplay, mContext);
335     mContext = EGL_NO_CONTEXT;
336     mSurface = EGL_NO_SURFACE;
337 }
338 
doSyncThreadCmd(SyncThreadCmd * cmd)339 int SyncThread::doSyncThreadCmd(SyncThreadCmd* cmd) {
340 #if DEBUG
341     thread_local static auto threadId = android::base::getCurrentThreadId();
342     thread_local static size_t numCommands = 0U;
343     DPRINT("threadId = %lu numCommands = %lu cmd = %p", threadId, ++numCommands,
344            cmd);
345 #endif  // DEBUG
346 
347     int result = 0;
348     switch (cmd->opCode) {
349     case SYNC_THREAD_INIT:
350         DPRINT("exec SYNC_THREAD_INIT");
351         doSyncContextInit();
352         break;
353     case SYNC_THREAD_WAIT:
354         DPRINT("exec SYNC_THREAD_WAIT");
355         doSyncWait(cmd);
356         break;
357     case SYNC_THREAD_WAIT_VK:
358         DPRINT("exec SYNC_THREAD_WAIT_VK");
359         result = doSyncWaitVk(cmd);
360         break;
361     case SYNC_THREAD_EXIT:
362         DPRINT("exec SYNC_THREAD_EXIT");
363         doExit();
364         break;
365     case SYNC_THREAD_BLOCKED_WAIT_NO_TIMELINE:
366         DPRINT("exec SYNC_THREAD_BLOCKED_WAIT_NO_TIMELINE");
367         doSyncBlockedWaitNoTimeline(cmd);
368         break;
369     }
370 
371     bool need_reply = cmd->lock != nullptr && cmd->cond != nullptr;
372     if (need_reply) {
373         cmd->lock->lock();
374         *cmd->result = android::base::makeOptional(result);
375         cmd->cond->signalAndUnlock(cmd->lock);
376     }
377     return result;
378 }
379 
380 /* static */
get()381 SyncThread* SyncThread::get() {
382     return sGlobalSyncThread()->syncThreadPtr();
383 }
384