blob: f9c3006a397393bd2b3452f59b2178af7ee7a0e1 [file] [log] [blame]
Cody Schuffelen134ff032019-11-22 00:25:32 -08001#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
31namespace vsoc {
32namespace 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 */
40template <typename Layout>
41class 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
115template <typename Layout>
116constexpr int MockRegionView<Layout>::kWaitTimeoutInSec;
117
118} // namespace test
119} // namespace vsoc