blob: 055f7219cff64395900db4781847fa1d41008a4f [file] [log] [blame]
#pragma once
* Copyright (C) 2017 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and
* limitations under the License.
// Object that represents a region on the Host
#include <stdlib.h>
#include <sys/mman.h>
#include <atomic>
#include <cstdint>
#include <functional>
#include <map>
#include <thread>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/glog/logging.h"
#include "common/vsoc/lib/lock_guard.h"
#include "common/vsoc/lib/region_control.h"
#include "common/vsoc/lib/region_signaling_interface.h"
#include "common/vsoc/shm/base.h"
#include "uapi/vsoc_shm.h"
namespace vsoc {
class RegionControl;
class RegionView;
* Represents a task that is tied to a RegionView.
* This is currently used for the task that forwards futexes across the
* shared memory window.
class RegionWorker {
RegionWorker(RegionView* region, std::shared_ptr<RegionControl> control);
void start();
void Work();
std::shared_ptr<RegionControl> control_;
RegionView* region_;
std::thread thread_;
volatile bool stopping_;
* Base class to access a mapped region in VSoC shared memory.
* This class holds the methods that depends on the region's memory having an
* address. The RegionControl class holds the methods that can be invoked
* without mapping the region.
class RegionView : public RegionSignalingInterface {
virtual ~RegionView();
#if defined(CUTTLEFISH_HOST)
bool Open(const char* region_name, const char* domain);
bool Open(const char* region_name);
// Returns a pointer to the table that will be scanned for signals
const vsoc_signal_table_layout& incoming_signal_table();
// Returns a pointer to the table that will be used to post signals
const vsoc_signal_table_layout& outgoing_signal_table();
// Returns true iff an interrupt is queued in the signal table
bool HasIncomingInterrupt() {
return *region_offset_to_pointer<std::atomic<uint32_t>>(
// Wake any threads waiting for an interrupt. This is generally used during
// shutdown.
void InterruptSelf() { control_->InterruptSelf(); }
// Interrupt our peer if an interrupt is not already on the way.
// Returns true if the interrupt was sent, false if an interrupt was already
// pending.
bool MaybeInterruptPeer();
// Scan in the incoming signal table, issuing futex calls for any posted
// signals and then reposting them to the peer if they were round-trip
// signals.
// signal_handler: An action to perform on every offset signalled by our
// peer, usually a FUTEX_WAKE call, but can be customized for other
// purposes.
void ProcessSignalsFromPeer(
std::function<void(uint32_t)> signal_handler);
// Post a signal to the guest, the host, or both.
// See futex(2) FUTEX_WAKE for details.
// sides_to_signal: controls where the signal is sent
// signal_addr: the memory location to signal. Must be within the region.
void SendSignal(layout::Sides sides_to_signal,
std::atomic<uint32_t>* signal_addr);
// Post a signal to our peer for a specific memeory location.
// See futex(2) FUTEX_WAKE for details.
// signal_addr: the memory location to signal. Must be within the region.
// round_trip: true if there may be waiters on both sides of the shared
// memory.
void SendSignalToPeer(std::atomic<uint32_t>* signal_addr, bool round_trip);
// Waits until an interrupt appears on this region, then clears the
// interrupted flag and returns.
void WaitForInterrupt();
// This implements the following:
// if (*signal_addr == last_observed_value)
// wait_for_signal_at(signal_addr);
// Note: the caller still needs to check the value at signal_addr because
// this function may return early for reasons that are implementation-defined.
// See futex(2) FUTEX_WAIT for details.
// signal_addr: the memory that will be signaled. Must be within the region.
// last_observed_value: the value that motivated the calling code to wait.
// The return value is -1 on error. On the guest positive values give the
// number of false wakes.
int WaitForSignal(std::atomic<uint32_t>* signal_addr,
uint32_t last_observed_value) override;
// Starts the signal table scanner. This must be invoked by subclasses, which
// MUST store the returned unique_ptr as a class member.
std::unique_ptr<RegionWorker> StartWorker();
// Returns a pointer to the start of region data that is cast to the given
// type. Initializers that run in the launcher use this to get a typed view
// of the region. Most other cases should be handled via TypedRegionView.
template <typename LayoutType>
LayoutType* GetLayoutPointer() {
return this->region_offset_to_pointer<LayoutType>(
// Helper functions for lock guards. This approach has two advantages:
// o Callers don't have to be refactored when lock types change
// o The region pointer can be provided automatically
template <typename T>
LockGuard<T> make_lock_guard(T* lock) {
return LockGuard<T>(lock);
LockGuard<::vsoc::layout::GuestAndHostLock> make_lock_guard(
::vsoc::layout::GuestAndHostLock* l) {
return LockGuard<::vsoc::layout::GuestAndHostLock>(l, this);
template <typename T>
T* region_offset_to_pointer(uint32_t offset) {
return control_->region_offset_to_pointer<T>(offset);
template <typename T>
const T& region_offset_to_reference(uint32_t offset) const {
if (offset > control_->region_size()) {
LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @"
<< region_base_;
return *reinterpret_cast<const T*>(
reinterpret_cast<uintptr_t>(region_base_) + offset);
// Calculates an offset based on a pointer in the region. Crashes if the
// pointer isn't in the region.
// This is mostly for the RegionView's internal plumbing. Use TypedRegionView
// and RegionLayout to avoid this in most cases.
template <typename T>
uint32_t pointer_to_region_offset(T* ptr) const {
uint32_t rval = reinterpret_cast<uintptr_t>(ptr) -
if (rval > control_->region_size()) {
LOG(FATAL) << __FUNCTION__ << ": " << ptr << " not in region @"
<< region_base_;
return rval;
std::shared_ptr<RegionControl> control_;
void* region_base_{};
} // namespace vsoc