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