tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

VRShMem.cpp (23206B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "VRShMem.h"
      8 
      9 #ifdef MOZILLA_INTERNAL_API
     10 #  include "nsString.h"
     11 #  include "nsXULAppAPI.h"
     12 #endif
     13 
     14 #include "gfxVRMutex.h"
     15 
     16 #if defined(XP_MACOSX)
     17 #  include <sys/mman.h>
     18 #  include <sys/stat.h> /* For mode constants */
     19 #  include <fcntl.h>    /* For O_* constants */
     20 #elif defined(MOZ_WIDGET_ANDROID)
     21 #  include "GeckoVRManager.h"
     22 #endif
     23 
     24 #if !defined(XP_WIN)
     25 #  include <unistd.h>  // for ::sleep
     26 #endif
     27 
     28 using namespace mozilla::gfx;
     29 
     30 #ifdef XP_WIN
     31 static const char* kShmemName = "moz.gecko.vr_ext." SHMEM_VERSION;
     32 static LPCTSTR kMutexName = TEXT("mozilla::vr::ShmemMutex" SHMEM_VERSION);
     33 #elif defined(XP_MACOSX)
     34 static const char* kShmemName = "/moz.gecko.vr_ext." SHMEM_VERSION;
     35 #endif  //  XP_WIN
     36 
     37 #if !defined(MOZ_WIDGET_ANDROID)
     38 namespace {
     39 void YieldThread() {
     40 #  if defined(XP_WIN)
     41  ::Sleep(0);
     42 #  else
     43  ::sleep(0);
     44 #  endif
     45 }
     46 }  // anonymous namespace
     47 #endif  // !defined(MOZ_WIDGET_ANDROID)
     48 
     49 VRShMem::VRShMem(volatile VRExternalShmem* aShmem, bool aRequiresMutex)
     50    : mExternalShmem(aShmem),
     51      mIsSharedExternalShmem(aShmem != nullptr)
     52 #if defined(XP_WIN)
     53      ,
     54      mRequiresMutex(aRequiresMutex)
     55 #endif
     56 #if defined(XP_MACOSX)
     57      ,
     58      mShmemFD(0)
     59 #elif defined(XP_WIN)
     60      ,
     61      mShmemFile(nullptr),
     62      mMutex(nullptr)
     63 #endif
     64 {
     65  // Regarding input parameters,
     66  // - aShmem is null for VRManager or for VRService in multi-proc
     67  // - aShmem is !null for VRService in-proc (i.e., no VR proc)
     68 }
     69 
     70 // Note: This function should only be called for in-proc scenarios, where the
     71 // shared memory is only shared within the same proc (rather than across
     72 // processes). Also, this local heap memory's lifetime is tied to the class.
     73 // Callers to this must ensure that its reference doesn't outlive the owning
     74 // VRShMem instance.
     75 volatile VRExternalShmem* VRShMem::GetExternalShmem() const {
     76 #if defined(XP_MACOSX)
     77  MOZ_ASSERT(mShmemFD == 0);
     78 #elif defined(XP_WIN)
     79  MOZ_ASSERT(mShmemFile == nullptr);
     80 #endif
     81  return mExternalShmem;
     82 }
     83 
     84 bool VRShMem::IsDisplayStateShutdown() const {
     85  // adapted from VRService::Refresh
     86  // Does this need the mutex for getting .shutdown?
     87  return mExternalShmem != nullptr &&
     88         mExternalShmem->state.displayState.shutdown;
     89 }
     90 
     91 // This method returns true when there is a Shmem struct allocated and
     92 // when there is a shmem handle from the OS. This implies that the struct
     93 // is mapped to shared memory rather than being allocated on the heap by
     94 // this process.
     95 bool VRShMem::IsCreatedOnSharedMemory() const {
     96 #if defined(XP_MACOSX)
     97  return HasExternalShmem() && (mShmemFD != 0);
     98 #elif defined(XP_WIN)
     99  return HasExternalShmem() && (mShmemFile != nullptr);
    100 #else
    101  // VRShMem does not support system shared memory on remaining platformss
    102  return false;
    103 #endif
    104 }
    105 
    106 // CreateShMem allocates system shared memory for mExternalShmem and
    107 // synchronization primitives to protect it.
    108 // Callers/Processes to CreateShMem should followup with CloseShMem
    109 void VRShMem::CreateShMem(bool aCreateOnSharedMemory) {
    110  if (HasExternalShmem()) {
    111    MOZ_ASSERT(mIsSharedExternalShmem && !IsCreatedOnSharedMemory());
    112    return;
    113  }
    114 #if defined(XP_WIN)
    115  if (mMutex == nullptr) {
    116    mMutex = CreateMutex(nullptr,      // default security descriptor
    117                         false,        // mutex not owned
    118                         kMutexName);  // object name
    119    if (mMutex == nullptr) {
    120 #  ifdef MOZILLA_INTERNAL_API
    121      nsAutoCString msg;
    122      msg.AppendPrintf("VRManager CreateMutex error \"%lu\".", GetLastError());
    123      NS_WARNING(msg.get());
    124 #  endif
    125      MOZ_ASSERT(false);
    126      return;
    127    }
    128    // At xpcshell extension tests, it creates multiple VRManager
    129    // instances in plug-contrainer.exe. It causes GetLastError() return
    130    // `ERROR_ALREADY_EXISTS`. However, even though `ERROR_ALREADY_EXISTS`, it
    131    // still returns the same mutex handle.
    132    //
    133    // https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createmutexa
    134    MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS);
    135  }
    136 #endif  // XP_WIN
    137 #if !defined(MOZ_WIDGET_ANDROID)
    138  // The VR Service accesses all hardware from a separate process
    139  // and replaces the other VRManager when enabled.
    140  // If the VR process is not enabled, create an in-process VRService.
    141  if (!aCreateOnSharedMemory) {
    142    MOZ_ASSERT(mExternalShmem == nullptr);
    143    // If the VR process is disabled, attempt to create a
    144    // VR service within the current process on the heap
    145    mExternalShmem = new VRExternalShmem();
    146    ClearShMem();
    147    return;
    148  }
    149 #endif
    150 
    151  MOZ_ASSERT(aCreateOnSharedMemory);
    152 
    153 #if defined(XP_MACOSX)
    154  if (mShmemFD == 0) {
    155    mShmemFD =
    156        shm_open(kShmemName, O_RDWR, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
    157  }
    158  if (mShmemFD <= 0) {
    159    mShmemFD = 0;
    160    return;
    161  }
    162 
    163  struct stat sb;
    164  fstat(mShmemFD, &sb);
    165  off_t length = sb.st_size;
    166  if (length < (off_t)sizeof(VRExternalShmem)) {
    167    // TODO - Implement logging (Bug 1558912)
    168    CloseShMem();
    169    return;
    170  }
    171 
    172  mExternalShmem = (VRExternalShmem*)mmap(NULL, length, PROT_READ | PROT_WRITE,
    173                                          MAP_SHARED, mShmemFD, 0);
    174  if (mExternalShmem == MAP_FAILED) {
    175    // TODO - Implement logging (Bug 1558912)
    176    mExternalShmem = NULL;
    177    CloseShMem();
    178    return;
    179  }
    180 
    181 #elif defined(XP_WIN)
    182  if (mShmemFile == nullptr) {
    183    mShmemFile =
    184        CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
    185                           sizeof(VRExternalShmem), kShmemName);
    186    MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS);
    187    MOZ_ASSERT(mShmemFile);
    188    if (mShmemFile == nullptr) {
    189      // TODO - Implement logging (Bug 1558912)
    190      CloseShMem();
    191      return;
    192    }
    193  }
    194 
    195  LARGE_INTEGER length;
    196  length.QuadPart = sizeof(VRExternalShmem);
    197  mExternalShmem = (VRExternalShmem*)MapViewOfFile(
    198      mShmemFile,           // handle to map object
    199      FILE_MAP_ALL_ACCESS,  // read/write permission
    200      0, 0, length.QuadPart);
    201 
    202  if (mExternalShmem == nullptr) {
    203    // TODO - Implement logging (Bug 1558912)
    204    CloseShMem();
    205    return;
    206  }
    207 #elif defined(MOZ_WIDGET_ANDROID)
    208  MOZ_ASSERT(false,
    209             "CreateShMem should not be called for Android. Use "
    210             "CreateShMemForAndroid instead");
    211 #endif
    212 }
    213 
    214 // This function sets mExternalShmem in the Android/GeckoView
    215 // scenarios where the host creates it in-memory and VRShMem
    216 // accesses it via GeckVRManager.
    217 void VRShMem::CreateShMemForAndroid() {
    218 #if defined(MOZ_WIDGET_ANDROID) && defined(MOZILLA_INTERNAL_API)
    219  mExternalShmem =
    220      (VRExternalShmem*)mozilla::GeckoVRManager::GetExternalContext();
    221  if (!mExternalShmem) {
    222    return;
    223  } else {
    224    mIsSharedExternalShmem = true;
    225  }
    226 
    227  int32_t version = -1;
    228  int32_t size = 0;
    229  if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) ==
    230      0) {
    231    version = mExternalShmem->version;
    232    size = mExternalShmem->size;
    233    pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
    234  } else {
    235    return;
    236  }
    237  if (version != kVRExternalVersion) {
    238    mExternalShmem = nullptr;
    239    return;
    240  }
    241  if (size != sizeof(VRExternalShmem)) {
    242    mExternalShmem = nullptr;
    243    return;
    244  }
    245 #endif
    246 }
    247 
    248 void VRShMem::ClearShMem() {
    249  if (mExternalShmem != nullptr) {
    250 #ifdef MOZILLA_INTERNAL_API
    251    // VRExternalShmem is asserted to be POD
    252    mExternalShmem->Clear();
    253 #else
    254    memset((void*)mExternalShmem, 0, sizeof(VRExternalShmem));
    255 #endif
    256  }
    257 }
    258 
    259 // The cleanup corresponding to CreateShMem
    260 void VRShMem::CloseShMem() {
    261 #if !defined(MOZ_WIDGET_ANDROID)
    262  if (!IsCreatedOnSharedMemory()) {
    263    MOZ_ASSERT(!mIsSharedExternalShmem);
    264    if (mExternalShmem) {
    265      delete mExternalShmem;
    266      mExternalShmem = nullptr;
    267    }
    268    return;
    269  }
    270 #endif
    271 #if defined(XP_MACOSX)
    272  if (mExternalShmem) {
    273    munmap((void*)mExternalShmem, sizeof(VRExternalShmem));
    274    mExternalShmem = NULL;
    275  }
    276  if (mShmemFD) {
    277    close(mShmemFD);
    278    mShmemFD = 0;
    279  }
    280 #elif defined(XP_WIN)
    281  if (mExternalShmem) {
    282    UnmapViewOfFile((void*)mExternalShmem);
    283    mExternalShmem = nullptr;
    284  }
    285  if (mShmemFile) {
    286    CloseHandle(mShmemFile);
    287    mShmemFile = nullptr;
    288  }
    289 #elif defined(MOZ_WIDGET_ANDROID)
    290  mExternalShmem = NULL;
    291 #endif
    292 
    293 #if defined(XP_WIN)
    294  if (mMutex) {
    295    MOZ_ASSERT(mRequiresMutex);
    296    CloseHandle(mMutex);
    297    mMutex = nullptr;
    298  }
    299 #endif
    300 }
    301 
    302 // Called to use an existing shmem instance created by another process
    303 // Callers to JoinShMem should call LeaveShMem for cleanup
    304 bool VRShMem::JoinShMem() {
    305 #if defined(XP_WIN)
    306  if (!mMutex && mRequiresMutex) {
    307    // Check that there are no errors before making system calls
    308    MOZ_ASSERT(GetLastError() == 0);
    309 
    310    mMutex = OpenMutex(MUTEX_ALL_ACCESS,  // request full access
    311                       false,             // handle not inheritable
    312                       kMutexName);       // object name
    313 
    314    if (mMutex == nullptr) {
    315 #  ifdef MOZILLA_INTERNAL_API
    316      nsAutoCString msg;
    317      msg.AppendPrintf("VRService OpenMutex error \"%lu\".", GetLastError());
    318      NS_WARNING(msg.get());
    319 #  endif
    320      return false;
    321    }
    322    MOZ_ASSERT(GetLastError() == 0);
    323  }
    324 #endif
    325 
    326  if (HasExternalShmem()) {
    327    // An ExternalShmem is already set. No need to override and rejoin
    328    return true;
    329  }
    330 
    331 #if defined(XP_WIN)
    332  // Opening a file-mapping object by name
    333  base::ProcessHandle targetHandle =
    334      OpenFileMappingA(FILE_MAP_ALL_ACCESS,  // read/write access
    335                       FALSE,                // do not inherit the name
    336                       kShmemName);          // name of mapping object
    337 
    338  MOZ_ASSERT(GetLastError() == 0);
    339 
    340  LARGE_INTEGER length;
    341  length.QuadPart = sizeof(VRExternalShmem);
    342  mExternalShmem = (VRExternalShmem*)MapViewOfFile(
    343      reinterpret_cast<base::ProcessHandle>(
    344          targetHandle),    // handle to map object
    345      FILE_MAP_ALL_ACCESS,  // read/write permission
    346      0, 0, length.QuadPart);
    347  MOZ_ASSERT(GetLastError() == 0);
    348 
    349  // TODO - Implement logging (Bug 1558912)
    350  mShmemFile = targetHandle;
    351  if (!mExternalShmem) {
    352    MOZ_ASSERT(mExternalShmem);
    353    return false;
    354  }
    355 #else
    356  // TODO: Implement shmem for other platforms.
    357  //
    358  // TODO: ** Does this mean that ShMem only works in Windows for now? If so,
    359  // let's delete the code from other platforms (Bug 1563234)
    360  MOZ_ASSERT(false, "JoinShMem not implemented");
    361 #endif
    362  return true;
    363 }
    364 
    365 // The cleanup corresponding to JoinShMem
    366 void VRShMem::LeaveShMem() {
    367 #if defined(XP_WIN)
    368  // Check that there are no errors before making system calls
    369  MOZ_ASSERT(GetLastError() == 0);
    370 
    371  if (mShmemFile) {
    372    ::CloseHandle(mShmemFile);
    373    mShmemFile = nullptr;
    374  }
    375 #endif
    376 
    377  if (mExternalShmem != nullptr) {
    378 #if defined(XP_WIN)
    379    if (IsCreatedOnSharedMemory()) {
    380      UnmapViewOfFile((void*)mExternalShmem);
    381      MOZ_ASSERT(GetLastError() == 0);
    382    }
    383    // Otherwise, if not created on shared memory, simply null the shared
    384    // reference to the heap object. The call to CloseShMem will appropriately
    385    // free the allocation.
    386 #endif
    387    mExternalShmem = nullptr;
    388  }
    389 #if defined(XP_WIN)
    390  if (mMutex) {
    391    MOZ_ASSERT(mRequiresMutex);
    392    CloseHandle(mMutex);
    393    mMutex = nullptr;
    394  }
    395 #endif
    396 }
    397 
    398 void VRShMem::PushBrowserState(VRBrowserState& aBrowserState,
    399                               bool aNotifyCond) {
    400  if (!mExternalShmem) {
    401    return;
    402  }
    403 #if defined(MOZ_WIDGET_ANDROID)
    404  if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) ==
    405      0) {
    406    memcpy((void*)&(mExternalShmem->geckoState), (void*)&aBrowserState,
    407           sizeof(VRBrowserState));
    408    if (aNotifyCond) {
    409      pthread_cond_signal((pthread_cond_t*)&(mExternalShmem->geckoCond));
    410    }
    411    pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex));
    412  }
    413 #else
    414  bool status = true;
    415 
    416 #  if defined(XP_WIN)
    417  WaitForMutex lock(mMutex);
    418  status = lock.GetStatus();
    419 #  endif  // defined(XP_WIN)
    420 
    421  if (status) {
    422    mExternalShmem->geckoGenerationA = mExternalShmem->geckoGenerationA + 1;
    423    memcpy((void*)&(mExternalShmem->geckoState), (void*)&aBrowserState,
    424           sizeof(VRBrowserState));
    425    mExternalShmem->geckoGenerationB = mExternalShmem->geckoGenerationB + 1;
    426  }
    427 #endif    // defined(MOZ_WIDGET_ANDROID)
    428 }
    429 
    430 void VRShMem::PullBrowserState(mozilla::gfx::VRBrowserState& aState) {
    431  if (!mExternalShmem) {
    432    return;
    433  }
    434  // Copying the browser state from the shmem is non-blocking
    435  // on x86/x64 architectures.  Arm requires a mutex that is
    436  // locked for the duration of the memcpy to and from shmem on
    437  // both sides.
    438  // On x86/x64 It is fallable -- If a dirty copy is detected by
    439  // a mismatch of geckoGenerationA and geckoGenerationB,
    440  // the copy is discarded and will not replace the last known
    441  // browser state.
    442 
    443 #if defined(MOZ_WIDGET_ANDROID)
    444  // TODO: This code is out-of-date and fails to compile, as
    445  // VRService isn't compiled for Android (Bug 1563234)
    446  /*
    447  if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) ==
    448      0) {
    449    memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState));
    450    pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex));
    451  }
    452  */
    453  MOZ_ASSERT(false, "PullBrowserState not implemented");
    454 #else
    455  bool status = true;
    456 #  if defined(XP_WIN)
    457  if (mRequiresMutex) {
    458    // TODO: Is this scoped lock okay? Seems like it should allow some
    459    // race condition (Bug 1563234)
    460    WaitForMutex lock(mMutex);
    461    status = lock.GetStatus();
    462  }
    463 #  endif  // defined(XP_WIN)
    464  if (status) {
    465    VRExternalShmem tmp;
    466    if (mExternalShmem->geckoGenerationA != mBrowserGeneration) {
    467      // TODO - (void *) cast removes volatile semantics.
    468      // The memcpy is not likely to be optimized out, but is theoretically
    469      // possible.  Suggest refactoring to either explicitly enforce memory
    470      // order or to use locks.
    471      memcpy(&tmp, (void*)mExternalShmem, sizeof(VRExternalShmem));
    472      if (tmp.geckoGenerationA == tmp.geckoGenerationB &&
    473          tmp.geckoGenerationA != 0) {
    474        memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState));
    475        mBrowserGeneration = tmp.geckoGenerationA;
    476      }
    477    }
    478  }
    479 #endif    // defined(MOZ_WIDGET_ANDROID)
    480 }
    481 
    482 void VRShMem::PushSystemState(const mozilla::gfx::VRSystemState& aState) {
    483  if (!mExternalShmem) {
    484    return;
    485  }
    486  // Copying the VR service state to the shmem is atomic, infallable,
    487  // and non-blocking on x86/x64 architectures.  Arm requires a mutex
    488  // that is locked for the duration of the memcpy to and from shmem on
    489  // both sides.
    490 
    491 #if defined(MOZ_WIDGET_ANDROID)
    492  // TODO: This code is out-of-date and fails to compile, as
    493  // VRService isn't compiled for Android (Bug 1563234)
    494  MOZ_ASSERT(false, "JoinShMem not implemented");
    495  /*
    496  if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) ==
    497      0) {
    498    // We are casting away the volatile keyword, which is not accepted by
    499    // memcpy.  It is possible (although very unlikely) that the compiler
    500    // may optimize out the memcpy here as memcpy isn't explicitly safe for
    501    // volatile memory in the C++ standard.
    502    memcpy((void*)&mExternalShmem->state, &aState, sizeof(VRSystemState));
    503    pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
    504  }
    505  */
    506 #else
    507  bool lockState = true;
    508 
    509 #  if defined(XP_WIN)
    510  if (mRequiresMutex) {
    511    // TODO: Is this scoped lock okay? Seems like it should allow some
    512    // race condition (Bug 1563234)
    513    WaitForMutex lock(mMutex);
    514    lockState = lock.GetStatus();
    515  }
    516 #  endif  // defined(XP_WIN)
    517 
    518  if (lockState) {
    519    mExternalShmem->generationA = mExternalShmem->generationA + 1;
    520    memcpy((void*)&mExternalShmem->state, &aState, sizeof(VRSystemState));
    521    mExternalShmem->generationB = mExternalShmem->generationB + 1;
    522  }
    523 #endif    // defined(MOZ_WIDGET_ANDROID)
    524 }
    525 
    526 #if defined(MOZ_WIDGET_ANDROID)
    527 void VRShMem::PullSystemState(
    528    VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState,
    529    std::array<VRControllerState, kVRControllerMaxCount>* const
    530        aControllerState,
    531    bool& aEnumerationCompleted,
    532    const std::function<bool()>& aWaitCondition /* = nullptr */) {
    533  if (!mExternalShmem) {
    534    return;
    535  }
    536  bool done = false;
    537  while (!done) {
    538    if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) ==
    539        0) {
    540      while (true) {
    541        memcpy(&aDisplayState, (void*)&(mExternalShmem->state.displayState),
    542               sizeof(VRDisplayState));
    543        memcpy(&aSensorState, (void*)&(mExternalShmem->state.sensorState),
    544               sizeof(VRHMDSensorState));
    545        memcpy(aControllerState->data(),
    546               (void*)&(mExternalShmem->state.controllerState),
    547               sizeof(aControllerState->at(0)) * aControllerState->size());
    548        aEnumerationCompleted = mExternalShmem->state.enumerationCompleted;
    549        if (!aWaitCondition || aWaitCondition()) {
    550          done = true;
    551          break;
    552        }
    553        // Block current thead using the condition variable until data
    554        // changes
    555        pthread_cond_wait((pthread_cond_t*)&mExternalShmem->systemCond,
    556                          (pthread_mutex_t*)&mExternalShmem->systemMutex);
    557      }  // while (true)
    558      pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
    559    } else if (!aWaitCondition) {
    560      // pthread_mutex_lock failed and we are not waiting for a condition to
    561      // exit from PullState call.
    562      return;
    563    }
    564  }  // while (!done) {
    565 }
    566 #else
    567 void VRShMem::PullSystemState(
    568    VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState,
    569    std::array<VRControllerState, kVRControllerMaxCount>* const
    570        aControllerState,
    571    bool& aEnumerationCompleted,
    572    const std::function<bool()>& aWaitCondition /* = nullptr */) {
    573  MOZ_ASSERT(mExternalShmem);
    574  if (!mExternalShmem) {
    575    return;
    576  }
    577  while (true) {
    578    {  // Scope for WaitForMutex
    579 #  if defined(XP_WIN)
    580      bool status = true;
    581      WaitForMutex lock(mMutex);
    582      status = lock.GetStatus();
    583      if (status) {
    584 #  endif  // defined(XP_WIN)
    585        VRExternalShmem tmp;
    586        memcpy(&tmp, (void*)mExternalShmem, sizeof(VRExternalShmem));
    587        bool isCleanCopy =
    588            tmp.generationA == tmp.generationB && tmp.generationA != 0;
    589        if (isCleanCopy) {
    590          memcpy(&aDisplayState, &tmp.state.displayState,
    591                 sizeof(VRDisplayState));
    592          memcpy(&aSensorState, &tmp.state.sensorState,
    593                 sizeof(VRHMDSensorState));
    594          *aControllerState = tmp.state.controllerState;
    595          aEnumerationCompleted = tmp.state.enumerationCompleted;
    596          // Check for wait condition
    597          if (!aWaitCondition || aWaitCondition()) {
    598            return;
    599          }
    600        } else if (!aWaitCondition) {
    601          // We did not get a clean copy, and we are not waiting for a condition
    602          // to exit from PullState call.
    603          return;
    604        }
    605        // Yield the thread while polling
    606        YieldThread();
    607 #  if defined(XP_WIN)
    608      } else if (!aWaitCondition) {
    609        // WaitForMutex failed and we are not waiting for a condition to
    610        // exit from PullState call.
    611        return;
    612      }
    613 #  endif  // defined(XP_WIN)
    614    }  // End: Scope for WaitForMutex
    615    // Yield the thread while polling
    616    YieldThread();
    617  }  // while (!true)
    618 }
    619 #endif    // defined(MOZ_WIDGET_ANDROID)
    620 
    621 void VRShMem::PushWindowState(VRWindowState& aState) {
    622 #if defined(XP_WIN)
    623  if (!mExternalShmem) {
    624    return;
    625  }
    626 
    627  bool status = true;
    628  WaitForMutex lock(mMutex);
    629  status = lock.GetStatus();
    630  if (status) {
    631    memcpy((void*)&(mExternalShmem->windowState), (void*)&aState,
    632           sizeof(VRWindowState));
    633  }
    634 #endif  // defined(XP_WIN)
    635 }
    636 
    637 void VRShMem::PullWindowState(VRWindowState& aState) {
    638 #if defined(XP_WIN)
    639  if (!mExternalShmem) {
    640    return;
    641  }
    642 
    643  bool status = true;
    644  WaitForMutex lock(mMutex);
    645  status = lock.GetStatus();
    646  if (status) {
    647    memcpy((void*)&aState, (void*)&(mExternalShmem->windowState),
    648           sizeof(VRWindowState));
    649  }
    650 #endif  // defined(XP_WIN)
    651 }
    652 
    653 void VRShMem::PushTelemetryState(VRTelemetryState& aState) {
    654 #if defined(XP_WIN)
    655  if (!mExternalShmem) {
    656    return;
    657  }
    658 
    659  bool status = true;
    660  WaitForMutex lock(mMutex);
    661  status = lock.GetStatus();
    662  if (status) {
    663    memcpy((void*)&(mExternalShmem->telemetryState), (void*)&aState,
    664           sizeof(VRTelemetryState));
    665  }
    666 #endif  // defined(XP_WIN)
    667 }
    668 void VRShMem::PullTelemetryState(VRTelemetryState& aState) {
    669 #if defined(XP_WIN)
    670  if (!mExternalShmem) {
    671    return;
    672  }
    673 
    674  bool status = true;
    675  WaitForMutex lock(mMutex);
    676  status = lock.GetStatus();
    677  if (status) {
    678    memcpy((void*)&aState, (void*)&(mExternalShmem->telemetryState),
    679           sizeof(VRTelemetryState));
    680  }
    681 #endif  // defined(XP_WIN)
    682 }
    683 
    684 void VRShMem::SendEvent(uint64_t aWindowID,
    685                        mozilla::gfx::VRFxEventType aEventType,
    686                        mozilla::gfx::VRFxEventState aEventState) {
    687  MOZ_ASSERT(!HasExternalShmem());
    688  if (JoinShMem()) {
    689    mozilla::gfx::VRWindowState windowState = {0};
    690    PullWindowState(windowState);
    691    windowState.windowID = aWindowID;
    692    windowState.eventType = aEventType;
    693    windowState.eventState = aEventState;
    694    PushWindowState(windowState);
    695    LeaveShMem();
    696 
    697 #if defined(XP_WIN)
    698    // Notify the waiting host process that the data is now available
    699    HANDLE hSignal = ::OpenEventA(EVENT_ALL_ACCESS,       // dwDesiredAccess
    700                                  FALSE,                  // bInheritHandle
    701                                  windowState.signalName  // lpName
    702    );
    703    ::SetEvent(hSignal);
    704    ::CloseHandle(hSignal);
    705 #endif  // defined(XP_WIN)
    706  }
    707 }
    708 
    709 void VRShMem::SendIMEState(uint64_t aWindowID,
    710                           mozilla::gfx::VRFxEventState aEventState) {
    711  SendEvent(aWindowID, mozilla::gfx::VRFxEventType::IME, aEventState);
    712 }
    713 
    714 void VRShMem::SendFullscreenState(uint64_t aWindowID, bool aFullscreen) {
    715  SendEvent(aWindowID, mozilla::gfx::VRFxEventType::FULLSCREEN,
    716            aFullscreen ? mozilla::gfx::VRFxEventState::FULLSCREEN_ENTER
    717                        : mozilla::gfx::VRFxEventState::FULLSCREEN_EXIT);
    718 }
    719 
    720 // Note: this should be called from the VRShMem instance that created
    721 // the external shmem rather than joined it.
    722 void VRShMem::SendShutdowmState(uint64_t aWindowID) {
    723  MOZ_ASSERT(HasExternalShmem());
    724 
    725  mozilla::gfx::VRWindowState windowState = {0};
    726  PullWindowState(windowState);
    727  windowState.windowID = aWindowID;
    728  windowState.eventType = mozilla::gfx::VRFxEventType::SHUTDOWN;
    729  PushWindowState(windowState);
    730 
    731 #if defined(XP_WIN)
    732  // Notify the waiting host process that the data is now available
    733  HANDLE hSignal = ::OpenEventA(EVENT_ALL_ACCESS,       // dwDesiredAccess
    734                                FALSE,                  // bInheritHandle
    735                                windowState.signalName  // lpName
    736  );
    737  ::SetEvent(hSignal);
    738  ::CloseHandle(hSignal);
    739 #endif  // defined(XP_WIN)
    740 }