1 #pragma once 2 3 /* 4 * Copyright (C) 2017 The Android Open Source Project 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 #include <linux/futex.h> 19 #include <sys/syscall.h> 20 #include <sys/types.h> 21 #include <unistd.h> 22 23 #include <chrono> 24 #include <condition_variable> 25 #include <mutex> 26 #include <thread> 27 #include <unordered_map> 28 29 #include "common/vsoc/lib/region_signaling_interface.h" 30 31 namespace vsoc { 32 namespace test { 33 34 /** 35 * MockRegionView mocks a region in the shared memory window by calloc and 36 * futex. It supports only one-sided signal as in it doesn't do anything on 37 * sending or waiting interrupt. This is to test if a particular layout 38 * behaves correctly when there are multiple threads accessing it. 39 */ 40 template <typename Layout> 41 class MockRegionView : public vsoc::RegionSignalingInterface { 42 public: 43 explicit MockRegionView(){}; 44 virtual ~MockRegionView() { 45 if (region_base_) { 46 free(region_base_); 47 region_base_ = nullptr; 48 } 49 }; 50 51 bool Open() { 52 region_base_ = calloc(sizeof(Layout), 1); 53 return !region_base_; 54 }; 55 56 virtual void SendSignal(vsoc::layout::Sides /* sides_to_signal */, 57 std::atomic<uint32_t>* uaddr) { 58 syscall(SYS_futex, reinterpret_cast<int32_t*>(uaddr), FUTEX_WAKE, -1, 59 nullptr, nullptr, 0); 60 } 61 62 virtual int WaitForSignal(std::atomic<uint32_t>* uaddr, 63 uint32_t expected_value) { 64 { 65 std::lock_guard<std::mutex> guard(mutex_); 66 if (tid_to_addr_.find(std::this_thread::get_id()) != tid_to_addr_.end()) { 67 // Same thread tries to wait 68 return 0; 69 } 70 tid_to_addr_.emplace(std::this_thread::get_id(), uaddr); 71 map_changed_.notify_one(); 72 } 73 74 syscall(SYS_futex, uaddr, FUTEX_WAIT, expected_value, nullptr, nullptr, 0); 75 76 { 77 std::lock_guard<std::mutex> guard(mutex_); 78 tid_to_addr_.erase(std::this_thread::get_id()); 79 } 80 return 0; 81 } 82 83 // Mock methods from TypedRegionView 84 Layout* data() { return reinterpret_cast<Layout*>(region_base_); }; 85 86 // Check wait status on a specificy thread 87 bool IsBlocking(std::thread::id tid) { 88 while (1) { 89 std::unique_lock<std::mutex> lock(mutex_); 90 if (tid_to_addr_.find(tid) != tid_to_addr_.end()) { 91 return true; 92 } 93 // Allow some time as tid map might not be updated yet 94 while (tid_to_addr_.find(tid) == tid_to_addr_.end()) { 95 if (map_changed_.wait_for(lock, 96 std::chrono::seconds(kWaitTimeoutInSec)) == 97 std::cv_status::timeout) { 98 return false; 99 } 100 } 101 return true; 102 } 103 } 104 105 private: 106 // Timeout to avoid a race on checking if a thread is blocked 107 static constexpr int kWaitTimeoutInSec = 5; 108 109 void* region_base_{}; 110 std::mutex mutex_; 111 std::condition_variable map_changed_; 112 std::unordered_map<std::thread::id, std::atomic<uint32_t>*> tid_to_addr_; 113 }; 114 115 template <typename Layout> 116 constexpr int MockRegionView<Layout>::kWaitTimeoutInSec; 117 118 } // namespace test 119 } // namespace vsoc 120