Home | History | Annotate | Download | only in lib
      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