Use a separate class to manage virtio-gpu fence signaling

Use a separate class with queues to record the virtio-gpu fences and
their payloads, and call the callback of fences once all previous tasks
on the timeline is marked complted.

Test: run the emulator, VirtioGputTimelines_unittests

Change-Id: I57a2ce5cee1781a2a3b511a43e96b347208571cc
diff --git a/stream-servers/VirtioGpuTimelines.cpp b/stream-servers/VirtioGpuTimelines.cpp
new file mode 100644
index 0000000..8ba729f
--- /dev/null
+++ b/stream-servers/VirtioGpuTimelines.cpp
@@ -0,0 +1,107 @@
+// Copyright 2021 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 "VirtioGpuTimelines.h"
+
+#include <cinttypes>
+#include <cstdio>
+
+#define VGT_ERROR(fmt, ...)                                                   \
+    do {                                                                      \
+        fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, \
+                ##__VA_ARGS__);                                               \
+        fflush(stderr);                                                       \
+    } while (0)
+
+using TaskId = VirtioGpuTimelines::TaskId;
+using CtxId = VirtioGpuTimelines::CtxId;
+using FenceId = VirtioGpuTimelines::FenceId;
+using AutoLock = android::base::AutoLock;
+
+VirtioGpuTimelines::VirtioGpuTimelines() : mNextId(0) {}
+
+TaskId VirtioGpuTimelines::enqueueTask(CtxId ctxId) {
+    AutoLock lock(mLock);
+
+    TaskId id = mNextId++;
+    auto task = std::make_shared<Task>(id, ctxId);
+    mTaskIdToTask[id] = task;
+    mTimelineQueues[ctxId].emplace_back(std::move(task));
+    return id;
+}
+
+void VirtioGpuTimelines::enqueueFence(
+    CtxId ctxId, FenceId, FenceCompletionCallback fenceCompletionCallback) {
+    AutoLock lock(mLock);
+
+    auto fence = std::make_unique<Fence>(fenceCompletionCallback);
+    mTimelineQueues[ctxId].emplace_back(std::move(fence));
+    poll_locked(ctxId);
+}
+
+void VirtioGpuTimelines::notifyTaskCompletion(TaskId taskId) {
+    AutoLock lock(mLock);
+    auto iTask = mTaskIdToTask.find(taskId);
+    if (iTask == mTaskIdToTask.end()) {
+        VGT_ERROR("Task(id = %" PRIu64 ") can't be found.",
+                  static_cast<uint64_t>(taskId));
+        ::std::abort();
+    }
+    std::shared_ptr<Task> task = iTask->second.lock();
+    if (task == nullptr) {
+        VGT_ERROR("Task(id = %" PRIu64 ") has been destroyed.",
+                  static_cast<uint64_t>(taskId));
+        ::std::abort();
+    }
+    if (task->mId != taskId) {
+        VGT_ERROR("Task id mismatch. Expected %" PRIu64 ". Actual %" PRIu64,
+                  static_cast<uint64_t>(taskId),
+                  static_cast<uint64_t>(task->mId));
+        ::std::abort();
+    }
+    if (task->mHasCompleted) {
+        VGT_ERROR("Task(id = %" PRIu64 ") has been set to completed.",
+                  static_cast<uint64_t>(taskId));
+        ::std::abort();
+    }
+    task->mHasCompleted = true;
+    poll_locked(task->mCtxId);
+}
+
+void VirtioGpuTimelines::poll_locked(CtxId ctxId) {
+    auto iTimelineQueue = mTimelineQueues.find(ctxId);
+    if (iTimelineQueue == mTimelineQueues.end()) {
+        VGT_ERROR("Context(id = %" PRIu64 ") doesn't exist.",
+                  static_cast<uint64_t>(ctxId));
+        ::std::abort();
+    }
+    std::list<TimelineItem> &timelineQueue = iTimelineQueue->second;
+    auto i = timelineQueue.begin();
+    for (; i != timelineQueue.end(); i++) {
+        // This visitor will signal the fence and return whether the timeline
+        // item is an incompleted task.
+        struct {
+            bool operator()(std::unique_ptr<Fence> &fence) {
+                (*fence->mCompletionCallback)();
+                return false;
+            }
+            bool operator()(std::shared_ptr<Task> &task) {
+                return !task->mHasCompleted;
+            }
+        } visitor;
+        if (std::visit(visitor, *i)) {
+            break;
+        }
+    }
+    timelineQueue.erase(timelineQueue.begin(), i);
+}
\ No newline at end of file