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 #pragma once
18 
19 #include <atomic>
20 #include <condition_variable>
21 #include <cstdint>
22 #include <functional>
23 #include <mutex>
24 
25 #include <fruit/fruit.h>
26 
27 #include "common/libs/confui/confui.h"
28 #include "host/libs/confui/host_utils.h"
29 
30 namespace cuttlefish {
31 /**
32  * mechanism to orchestrate concurrent executions of threads
33  * that work for screen connector
34  *
35  * Within WebRTC service, it tells when it is now in the Android Mode or
36  * Confirmation UI mode
37  */
38 class HostModeCtrl {
39  public:
40   enum class ModeType : std::uint8_t { kAndroidMode = 55, kConfUI_Mode = 77 };
INJECT(HostModeCtrl ())41   INJECT(HostModeCtrl()) : atomic_mode_(ModeType::kAndroidMode) {}
42   /**
43    * The thread that enqueues Android frames will call this to wait until
44    * the mode is kAndroidMode
45    *
46    * Logically, using atomic_mode_ alone is not sufficient. Using mutex alone
47    * is logically complete but slow.
48    *
49    * Note that most of the time, the mode is kAndroidMode. Also, note that
50    * this method is called at every single frame.
51    *
52    * As an optimization, we check atomic_mode_ first. If failed, we wait for
53    * kAndroidMode with mutex-based lock
54    *
55    * The actual synchronization is not at the and_mode_cv_.wait line but at
56    * this line:
57    *     if (atomic_mode_ == ModeType::kAndroidMode) {
58    *
59    * This trick reduces the flag checking delays by 70+% on a Gentoo based
60    * amd64 desktop, with Linux 5.10
61    */
WaitAndroidMode()62   void WaitAndroidMode() {
63     ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
64                      << "checking atomic Android mode";
65     if (atomic_mode_ == ModeType::kAndroidMode) {
66       ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
67                        << "returns as it is already Android mode";
68       return;
69     }
70     auto check = [this]() -> bool {
71       return atomic_mode_ == ModeType::kAndroidMode;
72     };
73     std::unique_lock<std::mutex> lock(mode_mtx_);
74     and_mode_cv_.wait(lock, check);
75     ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
76                      << "awakes from cond var waiting for Android mode";
77   }
78 
SetMode(const ModeType mode)79   void SetMode(const ModeType mode) {
80     ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
81                      << " tries to acquire the lock in SetMode";
82     std::lock_guard<std::mutex> lock(mode_mtx_);
83     ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
84                      << " acquired the lock in SetMode";
85     atomic_mode_ = mode;
86     if (atomic_mode_ == ModeType::kAndroidMode) {
87       ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
88                        << " signals kAndroidMode in SetMode";
89       and_mode_cv_.notify_all();
90       return;
91     }
92     ConfUiLog(DEBUG) << cuttlefish::confui::thread::GetName()
93                      << "signals kConfUI_Mode in SetMode";
94     confui_mode_cv_.notify_all();
95   }
96 
GetMode()97   auto GetMode() {
98     ModeType ret_val = atomic_mode_;
99     return ret_val;
100   }
101 
IsConfirmatioUiMode()102   auto IsConfirmatioUiMode() {
103     return (atomic_mode_ == ModeType::kConfUI_Mode);
104   }
105 
IsAndroidMode()106   auto IsAndroidMode() { return (atomic_mode_ == ModeType::kAndroidMode); }
107 
Get()108   static HostModeCtrl& Get() {
109     static HostModeCtrl host_mode_controller;
110     return host_mode_controller;
111   }
112 
113  private:
114   std::mutex mode_mtx_;
115   std::condition_variable and_mode_cv_;
116   std::condition_variable confui_mode_cv_;
117   std::atomic<ModeType> atomic_mode_;
118 };
119 }  // end of namespace cuttlefish
120