<commit_before>
#include <Rcpp.h>
#include <ncbi-vdb/NGS.hpp>
#include <ngs-bam/ngs-bam.hpp>
#include <ngs/ErrorMsg.hpp>
#include <ngs/ReadCollection.hpp>
#include <ngs/ReadIterator.hpp>
#include <ngs/Read.hpp>

#include <ngs/Reference.hpp>
#include <ngs/Alignment.hpp>
#include <ngs/PileupIterator.hpp>

#include <math.h>
#include <iostream>

using namespace ngs;
using namespace std;
using namespace Rcpp;

// This is a simple example of exporting a C++ function to R. You can
// source this function into an R session using the Rcpp::sourceCpp
// function (or via the Source button on the editor toolbar). Learn
// more about Rcpp at:
//
//   http://www.rcpp.org/
//   http://adv-r.had.co.nz/Rcpp.html
//   http://gallery.rcpp.org/
//s

//' The readCount in the read collection.
//'
//' This simply returns the full read count.
//'
//' @param acc An accession or a path to an actual SRA file (with .sra suffix)
//' @param refname Reference name for pile up
//' @param start An in for position of start of pileup
//' @param stop An in for position of stop of pileup
//' @param MinPileUpDepth Coverage required
//' @return the number of reads in the collection
//' @export
//' @examples
//' getPileUp('SRR390728')
// [[Rcpp::export]]
DataFrame getPileUp(Rcpp::String acc, Rcpp::String refname, int start = 1, int stop = 0, int MinPileUpDepth = 0 ) {

  // open requested accession using SRA implementation of the API
  ReadCollection run = ncbi::NGS::openReadCollection ( acc );

  // get requested reference
  ngs::Reference ref = run.getReference ( refname );
  if ( start == 1 && stop == 0 ){
    stop =  ref.getLength();
  }

  // start iterator on requested range
  long count = stop - start + 1;
  PileupIterator it = ref.getPileupSlice ( start-1 /*0-based*/, count);

  vector<std::string> RefSpec;
  vector<long> RefPos;
  vector<char> RefBase;
  vector<long> PileDepth;

  while ( it.nextPileup ())
  {
 if ( it.getPileupDepth () >= MinPileUpDepth ) {
        RefSpec.push_back(it.getReferenceSpec ());
        RefPos.push_back( it.getReferencePosition () + 1 );
        RefBase.push_back( it.getReferenceBase () );
        PileDepth.push_back(it.getPileupDepth ( ) );

 }
  }

  return DataFrame::create (
      _["ReferenceSpec"] = RefSpec, _["ReferencePosition"] = RefPos, _["ReferenceBase"] = RefBase, _["PileupDepth"] = PileDepth

  );

}

<commit_msg>aligned quality bases<commit_after>
#include <Rcpp.h>
#include <ncbi-vdb/NGS.hpp>
#include <ngs-bam/ngs-bam.hpp>
#include <ngs/ErrorMsg.hpp>
#include <ngs/ReadCollection.hpp>
#include <ngs/ReadIterator.hpp>
#include <ngs/Read.hpp>

#include <ngs/Reference.hpp>
#include <ngs/Alignment.hpp>
#include <ngs/PileupIterator.hpp>

#include <math.h>
#include <iostream>
#include <string>

using namespace ngs;
using namespace std;
using namespace Rcpp;

// This is a simple example of exporting a C++ function to R. You can
// source this function into an R session using the Rcpp::sourceCpp
// function (or via the Source button on the editor toolbar). Learn
// more about Rcpp at:
//
//   http://www.rcpp.org/
//   http://adv-r.had.co.nz/Rcpp.html
//   http://gallery.rcpp.org/
//s

//' The readCount in the read collection.
//'
//' This simply returns the full read count.
//'
//' @param acc An accession or a path to an actual SRA file (with .sra suffix)
//' @param refname Reference name for pile up
//' @param start An in for position of start of pileup
//' @param stop An in for position of stop of pileup
//' @param MinPileUpDepth Coverage required
//' @return the number of reads in the collection
//' @export
//' @examples
//' getPileUp('SRR390728')
// [[Rcpp::export]]
DataFrame getPileUp(Rcpp::String acc, Rcpp::String refname, int start = 1, int stop = 0, int MinPileUpDepth = 0 ) {

  // open requested accession using SRA implementation of the API
  ReadCollection run = ncbi::NGS::openReadCollection ( acc );

  // get requested reference
  ngs::Reference ref = run.getReference ( refname );
  if ( start == 1 && stop == 0 ){
    stop =  ref.getLength();
  }

  // start iterator on requested range
  long count = stop - start + 1;
  PileupIterator it = ref.getPileupSlice ( start-1 /*0-based*/, count);

  vector<std::string> RefSpec;
  vector<long> RefPos;
  vector<char> RefBase;
  vector<long> PileDepth;
  vector<char> AlignedQuality;
  vector<std::string>  AllAlignedQuality;

  while ( it.nextPileup ())
  {
 if ( it.getPileupDepth () >= MinPileUpDepth ) {
        RefSpec.push_back(it.getReferenceSpec ());
        RefPos.push_back( it.getReferencePosition () + 1 );
        RefBase.push_back( it.getReferenceBase () );
        PileDepth.push_back(it.getPileupDepth ( ) );
        AlignedQuality.clear();

        while ( it.nextPileupEvent() ){

        AlignedQuality.push_back( it.getAlignmentQuality() );

        }
        std::string str(AlignedQuality.begin(),AlignedQuality.end());
        AllAlignedQuality.push_back( str );

        }

 }

  return DataFrame::create (
      _["ReferenceSpec"] = RefSpec, _["ReferencePosition"] = RefPos, _["ReferenceBase"] = RefBase, _["PileupDepth"] = PileDepth, _["AllAlignedQuality"] = AllAlignedQuality

  );

}

<|endoftext|>


<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2015
Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with Abaclade. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#include <abaclade.hxx>
#include <abaclade/coroutine.hxx>

#if ABC_HOST_API_POSIX
   #include <errno.h> // EINTR errno
   #if ABC_HOST_API_BSD
      #include <sys/event.h>
      #include <sys/time.h>
      #include <sys/types.h>
   #elif ABC_HOST_API_LINUX
      #include <sys/epoll.h>
   #endif
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::coroutine

namespace abc {

class coroutine::context : public noncopyable {
public:
   context(std::function<void ()> fnMain) :
      m_fnInnerMain(std::move(fnMain)) {
   }

   void reset(::ucontext_t * puctxReturn) {
      if (::getcontext(&m_uctx) < 0) {
         exception::throw_os_error();
      }
      m_uctx.uc_stack.ss_sp = &m_aiStack;
      m_uctx.uc_stack.ss_size = sizeof m_aiStack;
      m_uctx.uc_link = puctxReturn;
      ::makecontext(&m_uctx, reinterpret_cast<void (*)()>(&outer_main), 1, this);
   }

private:
   static void outer_main(void * p) {
      context * pctx = static_cast<context *>(p);
      try {
         pctx->m_fnInnerMain();
      } catch (std::exception const & x) {
         exception::write_with_scope_trace(nullptr, &x);
         // TODO: maybe support “moving” the exception to the return coroutine context?
      } catch (...) {
         exception::write_with_scope_trace();
         // TODO: maybe support “moving” the exception to the return coroutine context?
      }
   }

public:
   ::ucontext_t m_uctx;
private:
   std::function<void ()> m_fnInnerMain;
   // TODO: use MINSIGSTKSZ.
   abc::max_align_t m_aiStack[1024];
};

coroutine::coroutine() {
}
/*explicit*/ coroutine::coroutine(std::function<void ()> fnMain) :
   m_pctx(std::make_shared<coroutine::context>(std::move(fnMain))) {
}

coroutine::~coroutine() {
}

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::coroutine_scheduler

namespace abc {

thread_local_value<std::shared_ptr<coroutine_scheduler>> coroutine_scheduler::sm_pcorosched;

coroutine_scheduler::coroutine_scheduler() :
   m_pcoroctxActive(nullptr),
   m_fdEpoll(::epoll_create1(EPOLL_CLOEXEC)) {
   if (!m_fdEpoll) {
      exception::throw_os_error();
   }
}

coroutine_scheduler::~coroutine_scheduler() {
   // TODO: verify that m_listStartingCoros and m_mapBlockedCoros are empty.
}

/*static*/ coroutine_scheduler & coroutine_scheduler::attach_to_current_thread(
   std::shared_ptr<coroutine_scheduler> pcorosched /*= nullptr*/
) {
   if (sm_pcorosched.operator std::shared_ptr<coroutine_scheduler> const &()) {
      // The current thread already has a coroutine scheduler.
      // TODO: use a better exception class.
      ABC_THROW(generic_error, ());
   }
   if (!pcorosched) {
      pcorosched = std::make_shared<coroutine_scheduler>();
   }
   return *(
      sm_pcorosched = std::move(pcorosched)
   ).operator std::shared_ptr<coroutine_scheduler> const &();
}

std::shared_ptr<coroutine::context> coroutine_scheduler::find_coroutine_to_activate() {
   // This loop will only repeat in case of EINTR during ::epoll_wait().
   for (;;) {
      if (m_listStartingCoros) {
         // There are coroutines that haven’t had a chance to run; remove and schedule the first.
         auto pcoroctx(m_listStartingCoros.pop_front());
         // TODO: verify how this behaves in a multithreaded scenario.
         pcoroctx->reset(&m_uctxReturn);
         return std::move(pcoroctx);
      } else if (m_mapBlockedCoros) {
         // There are blocked coroutines; wait for the first one to become ready again.
         ::epoll_event eeReady;
         int cReadyFds = ::epoll_wait(m_fdEpoll.get(), &eeReady, 1, -1);
         // 0 won’t really be returned; possible values are either 1 or -1.
         if (cReadyFds < 0) {
            int iErr = errno;
            if (iErr == EINTR) {
               continue;
            }
            exception::throw_os_error(iErr);
         }
         /* Remove this event source from the epoll. Ignore errors, since we wouldn’t know what to
         do aobut them. */
         ::epoll_ctl(m_fdEpoll.get(), EPOLL_CTL_DEL, eeReady.data.fd, nullptr);
         // Find which coroutine was waiting for eeReady and remove it from m_mapBlockedCoros.
         // TODO: need map::pop().
         auto itBlockedCoro(m_mapBlockedCoros.find(eeReady.data.fd));
         std::shared_ptr<coroutine::context> pcoroctxToActivate(std::move(itBlockedCoro->value));
         m_mapBlockedCoros.remove(itBlockedCoro);
         return std::move(pcoroctxToActivate);
      } else {
         return nullptr;
      }
   }
}

void coroutine_scheduler::run() {
   while ((m_pcoroctxActive = find_coroutine_to_activate())) {
      if (::swapcontext(&m_uctxReturn, &m_pcoroctxActive->m_uctx) < 0) {
         /* TODO: in case of errors, which should never happen here since all coroutines have the
         same stack size, inject a stack overflow exception in *m_pcoroctxActive. */
      }
   }
   // Release the last coroutine.
   m_pcoroctxActive.reset();
}

void coroutine_scheduler::yield_while_async_pending(io::filedesc const & fd, bool bWrite) {
   // Add fd to the epoll as a new event source.
   ::epoll_event ee;
   ee.data.fd = fd.get();
   /* Use EPOLLONESHOT to avoid waking up multiple threads for the same fd becoming ready. This
   means we’d need to then rearm it in find_coroutine_to_activate() when it becomes ready, but we’ll
   remove it instead. */
   ee.events = EPOLLONESHOT | (bWrite ? EPOLLOUT : EPOLLIN) | EPOLLPRI;
   if (::epoll_ctl(m_fdEpoll.get(), EPOLL_CTL_ADD, ee.data.fd, &ee) < 0) {
      exception::throw_os_error();
   }
   // Deactivate the current coroutine and find one to activate instead.
   ::ucontext_t * puctxActive = &m_pcoroctxActive->m_uctx;
   auto itBlockedCoro(
      m_mapBlockedCoros.add_or_assign(ee.data.fd, std::move(m_pcoroctxActive)).first
   );
   try {
      m_pcoroctxActive = find_coroutine_to_activate();
   } catch (...) {
      // If anything went wrong, restore the coroutine that was active.
      // TODO: need map::pop().
      m_pcoroctxActive = std::move(itBlockedCoro->value);
      m_mapBlockedCoros.remove(itBlockedCoro);
      throw;
   }
   /* If the context changed, i.e. the coroutine that’s ready to run is not the one that was active,
   switch to it. */
   if (&m_pcoroctxActive->m_uctx != puctxActive) {
      if (::swapcontext(puctxActive, &m_pcoroctxActive->m_uctx) < 0) {
         /* TODO: in case of errors, which should never happen here since all coroutines have the
         same stack size, inject a stack overflow exception in *m_pcoroctxActive. */
      }
   }
}

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
<commit_msg>Compare abc::coroutine::context pointers, not ::ucontext_t pointers<commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2015
Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with Abaclade. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#include <abaclade.hxx>
#include <abaclade/coroutine.hxx>

#if ABC_HOST_API_POSIX
   #include <errno.h> // EINTR errno
   #if ABC_HOST_API_BSD
      #include <sys/event.h>
      #include <sys/time.h>
      #include <sys/types.h>
   #elif ABC_HOST_API_LINUX
      #include <sys/epoll.h>
   #endif
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::coroutine

namespace abc {

class coroutine::context : public noncopyable {
public:
   context(std::function<void ()> fnMain) :
      m_fnInnerMain(std::move(fnMain)) {
   }

   void reset(::ucontext_t * puctxReturn) {
      if (::getcontext(&m_uctx) < 0) {
         exception::throw_os_error();
      }
      m_uctx.uc_stack.ss_sp = &m_aiStack;
      m_uctx.uc_stack.ss_size = sizeof m_aiStack;
      m_uctx.uc_link = puctxReturn;
      ::makecontext(&m_uctx, reinterpret_cast<void (*)()>(&outer_main), 1, this);
   }

private:
   static void outer_main(void * p) {
      context * pctx = static_cast<context *>(p);
      try {
         pctx->m_fnInnerMain();
      } catch (std::exception const & x) {
         exception::write_with_scope_trace(nullptr, &x);
         // TODO: maybe support “moving” the exception to the return coroutine context?
      } catch (...) {
         exception::write_with_scope_trace();
         // TODO: maybe support “moving” the exception to the return coroutine context?
      }
   }

public:
   ::ucontext_t m_uctx;
private:
   std::function<void ()> m_fnInnerMain;
   // TODO: use MINSIGSTKSZ.
   abc::max_align_t m_aiStack[1024];
};

coroutine::coroutine() {
}
/*explicit*/ coroutine::coroutine(std::function<void ()> fnMain) :
   m_pctx(std::make_shared<coroutine::context>(std::move(fnMain))) {
}

coroutine::~coroutine() {
}

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::coroutine_scheduler

namespace abc {

thread_local_value<std::shared_ptr<coroutine_scheduler>> coroutine_scheduler::sm_pcorosched;

coroutine_scheduler::coroutine_scheduler() :
   m_pcoroctxActive(nullptr),
   m_fdEpoll(::epoll_create1(EPOLL_CLOEXEC)) {
   if (!m_fdEpoll) {
      exception::throw_os_error();
   }
}

coroutine_scheduler::~coroutine_scheduler() {
   // TODO: verify that m_listStartingCoros and m_mapBlockedCoros are empty.
}

/*static*/ coroutine_scheduler & coroutine_scheduler::attach_to_current_thread(
   std::shared_ptr<coroutine_scheduler> pcorosched /*= nullptr*/
) {
   if (sm_pcorosched.operator std::shared_ptr<coroutine_scheduler> const &()) {
      // The current thread already has a coroutine scheduler.
      // TODO: use a better exception class.
      ABC_THROW(generic_error, ());
   }
   if (!pcorosched) {
      pcorosched = std::make_shared<coroutine_scheduler>();
   }
   return *(
      sm_pcorosched = std::move(pcorosched)
   ).operator std::shared_ptr<coroutine_scheduler> const &();
}

std::shared_ptr<coroutine::context> coroutine_scheduler::find_coroutine_to_activate() {
   // This loop will only repeat in case of EINTR during ::epoll_wait().
   for (;;) {
      if (m_listStartingCoros) {
         // There are coroutines that haven’t had a chance to run; remove and schedule the first.
         auto pcoroctx(m_listStartingCoros.pop_front());
         // TODO: verify how this behaves in a multithreaded scenario.
         pcoroctx->reset(&m_uctxReturn);
         return std::move(pcoroctx);
      } else if (m_mapBlockedCoros) {
         // There are blocked coroutines; wait for the first one to become ready again.
         ::epoll_event eeReady;
         int cReadyFds = ::epoll_wait(m_fdEpoll.get(), &eeReady, 1, -1);
         // 0 won’t really be returned; possible values are either 1 or -1.
         if (cReadyFds < 0) {
            int iErr = errno;
            if (iErr == EINTR) {
               continue;
            }
            exception::throw_os_error(iErr);
         }
         /* Remove this event source from the epoll. Ignore errors, since we wouldn’t know what to
         do aobut them. */
         ::epoll_ctl(m_fdEpoll.get(), EPOLL_CTL_DEL, eeReady.data.fd, nullptr);
         // Find which coroutine was waiting for eeReady and remove it from m_mapBlockedCoros.
         // TODO: need map::pop().
         auto itBlockedCoro(m_mapBlockedCoros.find(eeReady.data.fd));
         std::shared_ptr<coroutine::context> pcoroctxToActivate(std::move(itBlockedCoro->value));
         m_mapBlockedCoros.remove(itBlockedCoro);
         return std::move(pcoroctxToActivate);
      } else {
         return nullptr;
      }
   }
}

void coroutine_scheduler::run() {
   while ((m_pcoroctxActive = find_coroutine_to_activate())) {
      if (::swapcontext(&m_uctxReturn, &m_pcoroctxActive->m_uctx) < 0) {
         /* TODO: in case of errors, which should never happen here since all coroutines have the
         same stack size, inject a stack overflow exception in *m_pcoroctxActive. */
      }
   }
   // Release the last coroutine.
   m_pcoroctxActive.reset();
}

void coroutine_scheduler::yield_while_async_pending(io::filedesc const & fd, bool bWrite) {
   // Add fd to the epoll as a new event source.
   ::epoll_event ee;
   ee.data.fd = fd.get();
   /* Use EPOLLONESHOT to avoid waking up multiple threads for the same fd becoming ready. This
   means we’d need to then rearm it in find_coroutine_to_activate() when it becomes ready, but we’ll
   remove it instead. */
   ee.events = EPOLLONESHOT | (bWrite ? EPOLLOUT : EPOLLIN) | EPOLLPRI;
   if (::epoll_ctl(m_fdEpoll.get(), EPOLL_CTL_ADD, ee.data.fd, &ee) < 0) {
      exception::throw_os_error();
   }
   // Deactivate the current coroutine and find one to activate instead.
   coroutine::context * pcoroctxActive = m_pcoroctxActive.get();
   auto itBlockedCoro(
      m_mapBlockedCoros.add_or_assign(ee.data.fd, std::move(m_pcoroctxActive)).first
   );
   try {
      m_pcoroctxActive = find_coroutine_to_activate();
   } catch (...) {
      // If anything went wrong, restore the coroutine that was active.
      // TODO: need map::pop().
      m_pcoroctxActive = std::move(itBlockedCoro->value);
      m_mapBlockedCoros.remove(itBlockedCoro);
      throw;
   }
   /* If the context changed, i.e. the coroutine that’s ready to run is not the one that was active,
   switch to it. */
   if (m_pcoroctxActive.get() != pcoroctxActive) {
      if (::swapcontext(&pcoroctxActive->m_uctx, &m_pcoroctxActive->m_uctx) < 0) {
         /* TODO: in case of errors, which should never happen here since all coroutines have the
         same stack size, inject a stack overflow exception in *m_pcoroctxActive. */
      }
   }
}

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/ui/views/root_view.h"

#include "atom/browser/native_window.h"
#include "atom/browser/ui/views/menu_bar.h"
#include "content/public/browser/native_web_keyboard_event.h"

namespace atom {

namespace {

// The menu bar height in pixels.
#if defined(OS_WIN)
const int kMenuBarHeight = 20;
#else
const int kMenuBarHeight = 25;
#endif

bool IsAltKey(const content::NativeWebKeyboardEvent& event) {
  return event.windows_key_code == ui::VKEY_MENU;
}

bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
  typedef content::NativeWebKeyboardEvent::Modifiers Modifiers;
  int modifiers = event.GetModifiers();
  modifiers &= ~Modifiers::kNumLockOn;
  modifiers &= ~Modifiers::kCapsLockOn;
  return (modifiers == Modifiers::kAltKey) ||
         (modifiers == (Modifiers::kAltKey | Modifiers::kIsLeft)) ||
         (modifiers == (Modifiers::kAltKey | Modifiers::kIsRight));
}

}  // namespace

RootView::RootView(NativeWindow* window)
    : window_(window),
      last_focused_view_tracker_(std::make_unique<views::ViewTracker>()) {
  set_owned_by_client();
}

RootView::~RootView() {}

void RootView::SetMenu(AtomMenuModel* menu_model) {
  if (menu_model == nullptr) {
    // Remove accelerators
    UnregisterAcceleratorsWithFocusManager();
    // and menu bar.
    SetMenuBarVisibility(false);
    menu_bar_.reset();
    return;
  }

  RegisterAcceleratorsWithFocusManager(menu_model);

  // Do not show menu bar in frameless window.
  if (!window_->has_frame())
    return;

  if (!menu_bar_) {
    menu_bar_.reset(new MenuBar(this));
    menu_bar_->set_owned_by_client();
    if (!menu_bar_autohide_)
      SetMenuBarVisibility(true);
  }

  menu_bar_->SetMenu(menu_model);
  Layout();
}

bool RootView::HasMenu() const {
  return !!menu_bar_;
}

int RootView::GetMenuBarHeight() const {
  return kMenuBarHeight;
}

void RootView::SetAutoHideMenuBar(bool auto_hide) {
  menu_bar_autohide_ = auto_hide;
}

bool RootView::IsMenuBarAutoHide() const {
  return menu_bar_autohide_;
}

void RootView::SetMenuBarVisibility(bool visible) {
  if (!window_->content_view() || !menu_bar_ || menu_bar_visible_ == visible)
    return;

  menu_bar_visible_ = visible;
  if (visible) {
    DCHECK_EQ(child_count(), 1);
    AddChildView(menu_bar_.get());
  } else {
    DCHECK_EQ(child_count(), 2);
    RemoveChildView(menu_bar_.get());
  }

  Layout();
}

bool RootView::IsMenuBarVisible() const {
  return menu_bar_visible_;
}

void RootView::HandleKeyEvent(const content::NativeWebKeyboardEvent& event) {
  if (!menu_bar_)
    return;

  // Show accelerator when "Alt" is pressed.
  if (menu_bar_visible_ && IsAltKey(event))
    menu_bar_->SetAcceleratorVisibility(event.GetType() ==
                                        blink::WebInputEvent::kRawKeyDown);

  // Show the submenu when "Alt+Key" is pressed.
  if (event.GetType() == blink::WebInputEvent::kRawKeyDown &&
      !IsAltKey(event) && IsAltModifier(event)) {
    if (menu_bar_->HasAccelerator(event.windows_key_code)) {
      if (!menu_bar_visible_) {
        SetMenuBarVisibility(true);

        View* focused_view = GetFocusManager()->GetFocusedView();
        last_focused_view_tracker_->SetView(focused_view);
        menu_bar_->RequestFocus();
      }

      menu_bar_->ActivateAccelerator(event.windows_key_code);
    }
    return;
  }

  // Toggle the menu bar only when a single Alt is released.
  if (event.GetType() == blink::WebInputEvent::kRawKeyDown && IsAltKey(event)) {
    // When a single Alt is pressed:
    menu_bar_alt_pressed_ = true;
  } else if (event.GetType() == blink::WebInputEvent::kKeyUp &&
             IsAltKey(event) && menu_bar_alt_pressed_) {
    // When a single Alt is released right after a Alt is pressed:
    menu_bar_alt_pressed_ = false;
    if (menu_bar_autohide_)
      SetMenuBarVisibility(!menu_bar_visible_);

    View* focused_view = GetFocusManager()->GetFocusedView();
    last_focused_view_tracker_->SetView(focused_view);
    menu_bar_->RequestFocus();
    // Show accelerators when menu bar is focused
    menu_bar_->SetAcceleratorVisibility(true);
  } else {
    // When any other keys except single Alt have been pressed/released:
    menu_bar_alt_pressed_ = false;
  }
}

void RootView::RestoreFocus() {
  View* last_focused_view = last_focused_view_tracker_->view();
  if (last_focused_view) {
    GetFocusManager()->SetFocusedViewWithReason(
        last_focused_view, views::FocusManager::kReasonFocusRestore);
  }
  if (menu_bar_autohide_)
    SetMenuBarVisibility(false);
}

void RootView::ResetAltState() {
  menu_bar_alt_pressed_ = false;
}

void RootView::Layout() {
  if (!window_->content_view())  // Not ready yet.
    return;

  const auto menu_bar_bounds =
      menu_bar_visible_
          ? gfx::Rect(insets_.left(), insets_.top(),
                      size().width() - insets_.width(), kMenuBarHeight)
          : gfx::Rect();
  if (menu_bar_)
    menu_bar_->SetBoundsRect(menu_bar_bounds);

  window_->content_view()->SetBoundsRect(
      gfx::Rect(insets_.left(),
                menu_bar_visible_ ? menu_bar_bounds.bottom() : insets_.top(),
                size().width() - insets_.width(),
                size().height() - menu_bar_bounds.height() - insets_.height()));
}

gfx::Size RootView::GetMinimumSize() const {
  return window_->GetMinimumSize();
}

gfx::Size RootView::GetMaximumSize() const {
  return window_->GetMaximumSize();
}

bool RootView::AcceleratorPressed(const ui::Accelerator& accelerator) {
  return accelerator_util::TriggerAcceleratorTableCommand(&accelerator_table_,
                                                          accelerator);
}

void RootView::RegisterAcceleratorsWithFocusManager(AtomMenuModel* menu_model) {
  if (!menu_model)
    return;
  // Clear previous accelerators.
  UnregisterAcceleratorsWithFocusManager();

  views::FocusManager* focus_manager = GetFocusManager();
  // Register accelerators with focus manager.
  accelerator_util::GenerateAcceleratorTable(&accelerator_table_, menu_model);
  for (const auto& iter : accelerator_table_) {
    focus_manager->RegisterAccelerator(
        iter.first, ui::AcceleratorManager::kNormalPriority, this);
  }
}

void RootView::UnregisterAcceleratorsWithFocusManager() {
  views::FocusManager* focus_manager = GetFocusManager();
  accelerator_table_.clear();
  focus_manager->UnregisterAccelerators(this);
}

void RootView::SetInsets(const gfx::Insets& insets) {
  if (insets != insets_) {
    insets_ = insets;
    Layout();
  }
}

}  // namespace atom
<commit_msg>fix: crash when alt key pressed with falsy menu bar visiblity (#17766)<commit_after>// Copyright (c) 2018 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/ui/views/root_view.h"

#include "atom/browser/native_window.h"
#include "atom/browser/ui/views/menu_bar.h"
#include "content/public/browser/native_web_keyboard_event.h"

namespace atom {

namespace {

// The menu bar height in pixels.
#if defined(OS_WIN)
const int kMenuBarHeight = 20;
#else
const int kMenuBarHeight = 25;
#endif

bool IsAltKey(const content::NativeWebKeyboardEvent& event) {
  return event.windows_key_code == ui::VKEY_MENU;
}

bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
  typedef content::NativeWebKeyboardEvent::Modifiers Modifiers;
  int modifiers = event.GetModifiers();
  modifiers &= ~Modifiers::kNumLockOn;
  modifiers &= ~Modifiers::kCapsLockOn;
  return (modifiers == Modifiers::kAltKey) ||
         (modifiers == (Modifiers::kAltKey | Modifiers::kIsLeft)) ||
         (modifiers == (Modifiers::kAltKey | Modifiers::kIsRight));
}

}  // namespace

RootView::RootView(NativeWindow* window)
    : window_(window),
      last_focused_view_tracker_(std::make_unique<views::ViewTracker>()) {
  set_owned_by_client();
}

RootView::~RootView() {}

void RootView::SetMenu(AtomMenuModel* menu_model) {
  if (menu_model == nullptr) {
    // Remove accelerators
    UnregisterAcceleratorsWithFocusManager();
    // and menu bar.
    SetMenuBarVisibility(false);
    menu_bar_.reset();
    return;
  }

  RegisterAcceleratorsWithFocusManager(menu_model);

  // Do not show menu bar in frameless window.
  if (!window_->has_frame())
    return;

  if (!menu_bar_) {
    menu_bar_.reset(new MenuBar(this));
    menu_bar_->set_owned_by_client();
    if (!menu_bar_autohide_)
      SetMenuBarVisibility(true);
  }

  menu_bar_->SetMenu(menu_model);
  Layout();
}

bool RootView::HasMenu() const {
  return !!menu_bar_;
}

int RootView::GetMenuBarHeight() const {
  return kMenuBarHeight;
}

void RootView::SetAutoHideMenuBar(bool auto_hide) {
  menu_bar_autohide_ = auto_hide;
}

bool RootView::IsMenuBarAutoHide() const {
  return menu_bar_autohide_;
}

void RootView::SetMenuBarVisibility(bool visible) {
  if (!window_->content_view() || !menu_bar_ || menu_bar_visible_ == visible)
    return;

  menu_bar_visible_ = visible;
  if (visible) {
    DCHECK_EQ(child_count(), 1);
    AddChildView(menu_bar_.get());
  } else {
    DCHECK_EQ(child_count(), 2);
    RemoveChildView(menu_bar_.get());
  }

  Layout();
}

bool RootView::IsMenuBarVisible() const {
  return menu_bar_visible_;
}

void RootView::HandleKeyEvent(const content::NativeWebKeyboardEvent& event) {
  if (!menu_bar_)
    return;

  // Show accelerator when "Alt" is pressed.
  if (menu_bar_visible_ && IsAltKey(event))
    menu_bar_->SetAcceleratorVisibility(event.GetType() ==
                                        blink::WebInputEvent::kRawKeyDown);

  // Show the submenu when "Alt+Key" is pressed.
  if (event.GetType() == blink::WebInputEvent::kRawKeyDown &&
      !IsAltKey(event) && IsAltModifier(event)) {
    if (menu_bar_->HasAccelerator(event.windows_key_code)) {
      if (!menu_bar_visible_) {
        SetMenuBarVisibility(true);

        View* focused_view = GetFocusManager()->GetFocusedView();
        last_focused_view_tracker_->SetView(focused_view);
        menu_bar_->RequestFocus();
      }

      menu_bar_->ActivateAccelerator(event.windows_key_code);
    }
    return;
  }

  // Toggle the menu bar only when a single Alt is released.
  if (event.GetType() == blink::WebInputEvent::kRawKeyDown && IsAltKey(event)) {
    // When a single Alt is pressed:
    menu_bar_alt_pressed_ = true;
  } else if (event.GetType() == blink::WebInputEvent::kKeyUp &&
             IsAltKey(event) && menu_bar_alt_pressed_) {
    // When a single Alt is released right after a Alt is pressed:
    menu_bar_alt_pressed_ = false;
    if (menu_bar_autohide_)
      SetMenuBarVisibility(!menu_bar_visible_);

    View* focused_view = GetFocusManager()->GetFocusedView();
    last_focused_view_tracker_->SetView(focused_view);
    if (menu_bar_visible_) {
      menu_bar_->RequestFocus();
      // Show accelerators when menu bar is focused
      menu_bar_->SetAcceleratorVisibility(true);
    }
  } else {
    // When any other keys except single Alt have been pressed/released:
    menu_bar_alt_pressed_ = false;
  }
}

void RootView::RestoreFocus() {
  View* last_focused_view = last_focused_view_tracker_->view();
  if (last_focused_view) {
    GetFocusManager()->SetFocusedViewWithReason(
        last_focused_view, views::FocusManager::kReasonFocusRestore);
  }
  if (menu_bar_autohide_)
    SetMenuBarVisibility(false);
}

void RootView::ResetAltState() {
  menu_bar_alt_pressed_ = false;
}

void RootView::Layout() {
  if (!window_->content_view())  // Not ready yet.
    return;

  const auto menu_bar_bounds =
      menu_bar_visible_
          ? gfx::Rect(insets_.left(), insets_.top(),
                      size().width() - insets_.width(), kMenuBarHeight)
          : gfx::Rect();
  if (menu_bar_)
    menu_bar_->SetBoundsRect(menu_bar_bounds);

  window_->content_view()->SetBoundsRect(
      gfx::Rect(insets_.left(),
                menu_bar_visible_ ? menu_bar_bounds.bottom() : insets_.top(),
                size().width() - insets_.width(),
                size().height() - menu_bar_bounds.height() - insets_.height()));
}

gfx::Size RootView::GetMinimumSize() const {
  return window_->GetMinimumSize();
}

gfx::Size RootView::GetMaximumSize() const {
  return window_->GetMaximumSize();
}

bool RootView::AcceleratorPressed(const ui::Accelerator& accelerator) {
  return accelerator_util::TriggerAcceleratorTableCommand(&accelerator_table_,
                                                          accelerator);
}

void RootView::RegisterAcceleratorsWithFocusManager(AtomMenuModel* menu_model) {
  if (!menu_model)
    return;
  // Clear previous accelerators.
  UnregisterAcceleratorsWithFocusManager();

  views::FocusManager* focus_manager = GetFocusManager();
  // Register accelerators with focus manager.
  accelerator_util::GenerateAcceleratorTable(&accelerator_table_, menu_model);
  for (const auto& iter : accelerator_table_) {
    focus_manager->RegisterAccelerator(
        iter.first, ui::AcceleratorManager::kNormalPriority, this);
  }
}

void RootView::UnregisterAcceleratorsWithFocusManager() {
  views::FocusManager* focus_manager = GetFocusManager();
  accelerator_table_.clear();
  focus_manager->UnregisterAccelerators(this);
}

void RootView::SetInsets(const gfx::Insets& insets) {
  if (insets != insets_) {
    insets_ = insets;
    Layout();
  }
}

}  // namespace atom
<|endoftext|>


<commit_before>#if defined(__cplusplus) && (__cplusplus >= 201103L)

#include <iostream>
#include <iomanip>

#include <atomic>

#include <chrono>

#ifdef SEQUENTIAL_CONSISTENCY
auto update_model = std::memory_order_seq_cst;
#else
auto update_model = std::memory_order_relaxed;
#endif

#ifdef _OPENMP
# include <omp.h>
#define OMP_PARALLEL            _Pragma("omp parallel")
#define OMP_BARRIER             _Pragma("omp barrier")
#define OMP_CRITICAL            _Pragma("omp critical")
#ifdef SEQUENTIAL_CONSISTENCY
#define OMP_ATOMIC              _Pragma("omp atomic seq_cst")
#define OMP_ATOMIC_CAPTURE      _Pragma("omp atomic capture seq_cst")
#else
#define OMP_ATOMIC              _Pragma("omp atomic")
#define OMP_ATOMIC_CAPTURE      _Pragma("omp atomic capture")
#endif
#else
# error No OpenMP support!
#endif

int main(int argc, char * argv[])
{
    int iterations = (argc>1) ? atoi(argv[1]) : 10000000;

    std::cout << "thread counter benchmark\n";
    std::cout << "num threads  = " << omp_get_max_threads() << "\n";
    std::cout << "iterations   = " << iterations << "\n";
#ifdef SEQUENTIAL_CONSISTENCY
    std::cout << "memory model = " << "seq_cst";
#else
    std::cout << "memory model = " << "relaxed";
#endif
    std::cout << std::endl;

    std::cout << "1) std::atomic_fetch_add(&counter, 1)\n";

    std::atomic<int> counter = {0};
    int omp_counter = 0;

    OMP_PARALLEL
    {
        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            std::atomic_fetch_add(&counter, 1);
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
        }
    }

    std::cout << "2) ++counter\n";

    counter = 0;

    OMP_PARALLEL
    {
        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            ++counter;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
        }
    }

    std::cout << "3) counter++\n";

    counter = 0;

    OMP_PARALLEL
    {
        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            counter++;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
        }
    }

    std::cout << "4) output = counter++\n";

    counter = 0;

    OMP_PARALLEL
    {
        int output = -1;

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            output = counter++;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
            std::cout << output << std::endl;
        }
    }

    std::cout << "5) output = std::atomic_fetch_add(&counter, 1)\n";

    counter = 0;

    OMP_PARALLEL
    {
        int output = -1;

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            output = std::atomic_fetch_add(&counter, 1);
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
            std::cout << output << std::endl;
        }
    }

    std::cout << "6) #pragma omp atomic\n";

    omp_counter = 0;

    OMP_PARALLEL
    {
        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            OMP_ATOMIC
            omp_counter++;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
        }
    }

    std::cout << "7) #pragma omp atomic capture\n";

    counter = 0;

    OMP_PARALLEL
    {
        int output = -1;

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            OMP_ATOMIC_CAPTURE
            output = omp_counter++;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
            std::cout << output << std::endl;
        }
    }

    return 0;
}

#else  // C++11
#error You need C++11 for this test!
#endif // C++11
<commit_msg>bugfix in printout (omp_counter)<commit_after>#if defined(__cplusplus) && (__cplusplus >= 201103L)

#include <iostream>
#include <iomanip>

#include <atomic>

#include <chrono>

#ifdef SEQUENTIAL_CONSISTENCY
auto update_model = std::memory_order_seq_cst;
#else
auto update_model = std::memory_order_relaxed;
#endif

#ifdef _OPENMP
# include <omp.h>
#define OMP_PARALLEL            _Pragma("omp parallel")
#define OMP_BARRIER             _Pragma("omp barrier")
#define OMP_CRITICAL            _Pragma("omp critical")
#ifdef SEQUENTIAL_CONSISTENCY
#define OMP_ATOMIC              _Pragma("omp atomic seq_cst")
#define OMP_ATOMIC_CAPTURE      _Pragma("omp atomic capture seq_cst")
#else
#define OMP_ATOMIC              _Pragma("omp atomic")
#define OMP_ATOMIC_CAPTURE      _Pragma("omp atomic capture")
#endif
#else
# error No OpenMP support!
#endif

int main(int argc, char * argv[])
{
    int iterations = (argc>1) ? atoi(argv[1]) : 10000000;

    std::cout << "thread counter benchmark\n";
    std::cout << "num threads  = " << omp_get_max_threads() << "\n";
    std::cout << "iterations   = " << iterations << "\n";
#ifdef SEQUENTIAL_CONSISTENCY
    std::cout << "memory model = " << "seq_cst";
#else
    std::cout << "memory model = " << "relaxed";
#endif
    std::cout << std::endl;

    std::cout << "1) std::atomic_fetch_add(&counter, 1)\n";

    std::atomic<int> counter = {0};
    int omp_counter = 0;

    OMP_PARALLEL
    {
        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            std::atomic_fetch_add(&counter, 1);
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
        }
    }

    std::cout << "2) ++counter\n";

    counter = 0;

    OMP_PARALLEL
    {
        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            ++counter;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
        }
    }

    std::cout << "3) counter++\n";

    counter = 0;

    OMP_PARALLEL
    {
        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            counter++;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
        }
    }

    std::cout << "4) output = counter++\n";

    counter = 0;

    OMP_PARALLEL
    {
        int output = -1;

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            output = counter++;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
            std::cout << output << std::endl;
        }
    }

    std::cout << "5) output = std::atomic_fetch_add(&counter, 1)\n";

    counter = 0;

    OMP_PARALLEL
    {
        int output = -1;

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            output = std::atomic_fetch_add(&counter, 1);
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        std::atomic_thread_fence(std::memory_order_seq_cst);

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << counter << std::endl;
            std::cout << output << std::endl;
        }
    }

    std::cout << "6) #pragma omp atomic\n";

    omp_counter = 0;

    OMP_PARALLEL
    {
        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            OMP_ATOMIC
            omp_counter++;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << omp_counter << std::endl;
        }
    }

    std::cout << "7) #pragma omp atomic capture\n";

    counter = 0;

    OMP_PARALLEL
    {
        int output = -1;

        /// START TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

        for (int i=0; i<iterations; ++i) {
            OMP_ATOMIC_CAPTURE
            output = omp_counter++;
        }

        /// STOP TIME
        OMP_BARRIER
        std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

        /// PRINT TIME
        std::chrono::duration<double> dt = std::chrono::duration_cast<std::chrono::duration<double>>(t1-t0);
        OMP_CRITICAL
        {
            std::cout << "total time elapsed = " << dt.count() << "\n";
            std::cout << "time per iteration = " << dt.count()/iterations  << "\n";
            std::cout << omp_counter << std::endl;
            std::cout << output << std::endl;
        }
    }

    return 0;
}

#else  // C++11
#error You need C++11 for this test!
#endif // C++11
<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "Device.h"
#include "Directory.h"
#include "utils/Filename.h"
#include "logging/Logger.h"

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <dirent.h>
#include <limits.h>
#include <mntent.h>
#include <unistd.h>
#include <sys/types.h>

namespace
{
    // Allow private ctors to be used from make_shared
    struct DeviceBuilder : public fs::Device
    {
        template <typename... Args>
        DeviceBuilder( Args&&... args ) : Device( std::forward<Args>( args )... ) {}
    };
}

namespace fs
{

Device::DeviceMap Device::Devices;
Device::MountpointMap Device::Mountpoints;
Device::DeviceCacheMap Device::DeviceCache;

Device::Device( const std::string& uuid, const std::string& mountpoint, bool isRemovable )
    : m_uuid( uuid )
    , m_mountpoint( mountpoint )
    , m_present( true )
    , m_removable( isRemovable )
{
    if ( *m_mountpoint.crbegin() != '/' )
        m_mountpoint += '/';
}

const std::string& Device::uuid() const
{
    return m_uuid;
}

bool Device::isRemovable() const
{
    return m_removable;
}

bool Device::isPresent() const
{
    return m_present;
}

const std::string&Device::mountpoint() const
{
    return m_mountpoint;
}

std::shared_ptr<IDevice> Device::fromPath( const std::string& path )
{
    std::shared_ptr<IDevice> res;
    for ( const auto& p : DeviceCache )
    {
        if ( path.find( p.second->mountpoint() ) == 0 )
        {
            if ( res == nullptr || res->mountpoint().length() < p.second->mountpoint().length() )
                res = p.second;
        }
    }
    return res;
}

std::shared_ptr<IDevice> Device::fromUuid( const std::string& uuid )
{
    auto it = DeviceCache.find( uuid );
    if ( it != end( DeviceCache ) )
        return it->second;
    return nullptr;
}

bool Device::populateCache()
{
    Devices = listDevices();
    Mountpoints = listMountpoints();
    DeviceCache = populateDeviceCache();
    return true;
}

Device::DeviceMap Device::listDevices()
{

    static const std::vector<std::string> deviceBlacklist = { "loop", "dm-" };
    const std::string devPath = "/dev/disk/by-uuid/";
    // Don't use fs::Directory to iterate, as it resolves the symbolic links automatically.
    // We need the link name & what it points to.
    std::unique_ptr<DIR, int(*)(DIR*)> dir( opendir( devPath.c_str() ), &closedir );
    if ( dir == nullptr )
    {
        std::stringstream err;
        err << "Failed to open /dev/disk/by-uuid: " << strerror(errno);
        throw std::runtime_error( err.str() );
    }
    DeviceMap res;
    dirent* result = nullptr;

    while ( ( result = readdir( dir.get() ) ) != nullptr )
    {
        if ( strcmp( result->d_name, "." ) == 0 ||
             strcmp( result->d_name, ".." ) == 0 )
        {
            continue;
        }
        std::string path = devPath + result->d_name;
        char linkPath[PATH_MAX] = {};
        if ( readlink( path.c_str(), linkPath, PATH_MAX ) < 0 )
        {
            std::stringstream err;
            err << "Failed to resolve uuid -> device link: "
                << result->d_name << " (" << strerror(errno) << ')';
            throw std::runtime_error( err.str() );
        }
        auto deviceName = utils::file::fileName( linkPath );
        if ( std::find_if( begin( deviceBlacklist ), end( deviceBlacklist ), [&deviceName]( const std::string& pattern ) {
                return deviceName.length() >= pattern.length() && deviceName.find( pattern ) == 0;
            }) != end( deviceBlacklist ) )
            continue;
        auto uuid = result->d_name;
        LOG_INFO( "Discovered device ", deviceName, " -> {", uuid, '}' );
        res[deviceName] = uuid;
    }
    return res;
}

Device::MountpointMap Device::listMountpoints()
{
    static const std::vector<std::string> allowedFsType = { "vfat", "exfat", "sdcardfs", "fuse",
                                                            "ntfs", "fat32", "ext3", "ext4", "esdfs" };
    MountpointMap res;
    char buff[512];
    errno = 0;
    mntent* s;
#ifndef __ANDROID__
    mntent smnt;
    FILE* f = setmntent("/etc/mtab", "r");
    if ( f == nullptr )
        throw std::runtime_error( "Failed to read /etc/mtab" );
    std::unique_ptr<FILE, int(*)(FILE*)> fPtr( f, &endmntent );
    while ( getmntent_r( f, &smnt, buff, sizeof(buff) ) != nullptr )
    {
        s = &smnt;
#else
    FILE* f = fopen( "/proc/mounts", "r" );
    std::unique_ptr<FILE, int(*)(FILE*)> fPtr( f, &fclose );
    while ( s = getmntent( f ) )
    {
#endif
        if ( std::find( begin( allowedFsType ), end( allowedFsType ), s->mnt_type ) == end( allowedFsType ) )
            continue;
        auto deviceName = s->mnt_fsname;
        LOG_INFO( "Discovered mountpoint ", deviceName, " mounted on ", s->mnt_dir, " (", s->mnt_type, ')' );
        res[deviceName] = s->mnt_dir;
        errno = 0;
    }
    if ( errno != 0 )
    {
        LOG_ERROR( "Failed to read mountpoints: ", strerror( errno ) );
    }
    return res;
}

Device::DeviceCacheMap Device::populateDeviceCache()
{
    Device::DeviceCacheMap res;
    for ( const auto& p : Mountpoints )
    {
        const auto& devicePath = p.first;
        auto deviceName = utils::file::fileName( devicePath );
        const auto& mountpoint = p.second;
        auto it = Devices.find( deviceName );
        std::string uuid;
        if ( it != end( Devices ) )
            uuid = it->second;
        else
        {
            deviceName = deviceFromDeviceMapper( devicePath );
            it = Devices.find( deviceName );
            if ( it != end( Devices ) )
                uuid = it->second;
            else
            {
                LOG_ERROR( "Failed to resolve mountpoint ", mountpoint, " to any known device" );
                continue;
            }
        }
        auto removable = isRemovable( deviceName, mountpoint );
        LOG_INFO( "Adding device to cache: {", uuid, "} mounted on ", mountpoint, " Removable: ", removable );
        res[uuid] = std::make_shared<DeviceBuilder>( uuid, mountpoint, removable );
    }
    return res;
}

std::string Device::deviceFromDeviceMapper( const std::string& devicePath )
{
    if ( devicePath.find( "/dev/mapper" ) != 0 )
        return {};
    char linkPath[PATH_MAX];
    if ( readlink( devicePath.c_str(), linkPath, PATH_MAX ) < 0 )
    {
        std::stringstream err;
        err << "Failed to resolve device -> mapper link: "
            << devicePath << " (" << strerror(errno) << ')';
        throw std::runtime_error( err.str() );
    }
    const auto dmName = utils::file::fileName( linkPath );
    std::string dmSlavePath = "/sys/block/" + dmName + "/slaves";
    std::unique_ptr<DIR, int(*)(DIR*)> dir( opendir( dmSlavePath.c_str() ), &closedir );
    std::string res;
    if ( dir == nullptr )
        return {};
    dirent* result;
    while ( ( result = readdir( dir.get() ) ) != nullptr )
    {
        if ( strcmp( result->d_name, "." ) == 0 ||
             strcmp( result->d_name, ".." ) == 0 )
        {
            continue;
        }
        if ( res.empty() == true )
            res = result->d_name;
        else
            LOG_WARN( "More than one slave for device mapper ", linkPath );
    }
    LOG_INFO( "Device mapper ", dmName, " maps to ", res );
    return res;
}

bool Device::isRemovable( const std::string& deviceName, const std::string& mountpoint )
{
#ifndef TIZEN
    (void)mountpoint;
    std::stringstream removableFilePath;
    removableFilePath << "/sys/block/" << deviceName << "/removable";
    std::unique_ptr<FILE, int(*)(FILE*)> removableFile( fopen( removableFilePath.str().c_str(), "r" ), &fclose );
    // Assume the file isn't removable by default
    if ( removableFile != nullptr )
    {
        char buff;
        if ( fread(&buff, sizeof(buff), 1, removableFile.get() ) != 1 )
            return buff == '1';
        return false;
    }
#else
    (void)deviceName;
    static const std::vector<std::string> SDMountpoints = { "/opt/storage/sdcard" };
    auto it = std::find_if( begin( SDMountpoints ), end( SDMountpoints ), [mountpoint]( const std::string& pattern ) {
        return mountpoint.length() >= pattern.length() && mountpoint.find( pattern ) == 0;
    });
    if ( it != end( SDMountpoints ) )
    {
        LOG_INFO( "Considering mountpoint ", mountpoint, " a removable SDCard" );
        return true;
    }
#endif
    return false;
}

}
<commit_msg>fs: Device: Fix warning<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "Device.h"
#include "Directory.h"
#include "utils/Filename.h"
#include "logging/Logger.h"

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <dirent.h>
#include <limits.h>
#include <mntent.h>
#include <unistd.h>
#include <sys/types.h>

namespace
{
    // Allow private ctors to be used from make_shared
    struct DeviceBuilder : public fs::Device
    {
        template <typename... Args>
        DeviceBuilder( Args&&... args ) : Device( std::forward<Args>( args )... ) {}
    };
}

namespace fs
{

Device::DeviceMap Device::Devices;
Device::MountpointMap Device::Mountpoints;
Device::DeviceCacheMap Device::DeviceCache;

Device::Device( const std::string& uuid, const std::string& mountpoint, bool isRemovable )
    : m_uuid( uuid )
    , m_mountpoint( mountpoint )
    , m_present( true )
    , m_removable( isRemovable )
{
    if ( *m_mountpoint.crbegin() != '/' )
        m_mountpoint += '/';
}

const std::string& Device::uuid() const
{
    return m_uuid;
}

bool Device::isRemovable() const
{
    return m_removable;
}

bool Device::isPresent() const
{
    return m_present;
}

const std::string&Device::mountpoint() const
{
    return m_mountpoint;
}

std::shared_ptr<IDevice> Device::fromPath( const std::string& path )
{
    std::shared_ptr<IDevice> res;
    for ( const auto& p : DeviceCache )
    {
        if ( path.find( p.second->mountpoint() ) == 0 )
        {
            if ( res == nullptr || res->mountpoint().length() < p.second->mountpoint().length() )
                res = p.second;
        }
    }
    return res;
}

std::shared_ptr<IDevice> Device::fromUuid( const std::string& uuid )
{
    auto it = DeviceCache.find( uuid );
    if ( it != end( DeviceCache ) )
        return it->second;
    return nullptr;
}

bool Device::populateCache()
{
    Devices = listDevices();
    Mountpoints = listMountpoints();
    DeviceCache = populateDeviceCache();
    return true;
}

Device::DeviceMap Device::listDevices()
{

    static const std::vector<std::string> deviceBlacklist = { "loop", "dm-" };
    const std::string devPath = "/dev/disk/by-uuid/";
    // Don't use fs::Directory to iterate, as it resolves the symbolic links automatically.
    // We need the link name & what it points to.
    std::unique_ptr<DIR, int(*)(DIR*)> dir( opendir( devPath.c_str() ), &closedir );
    if ( dir == nullptr )
    {
        std::stringstream err;
        err << "Failed to open /dev/disk/by-uuid: " << strerror(errno);
        throw std::runtime_error( err.str() );
    }
    DeviceMap res;
    dirent* result = nullptr;

    while ( ( result = readdir( dir.get() ) ) != nullptr )
    {
        if ( strcmp( result->d_name, "." ) == 0 ||
             strcmp( result->d_name, ".." ) == 0 )
        {
            continue;
        }
        std::string path = devPath + result->d_name;
        char linkPath[PATH_MAX] = {};
        if ( readlink( path.c_str(), linkPath, PATH_MAX ) < 0 )
        {
            std::stringstream err;
            err << "Failed to resolve uuid -> device link: "
                << result->d_name << " (" << strerror(errno) << ')';
            throw std::runtime_error( err.str() );
        }
        auto deviceName = utils::file::fileName( linkPath );
        if ( std::find_if( begin( deviceBlacklist ), end( deviceBlacklist ), [&deviceName]( const std::string& pattern ) {
                return deviceName.length() >= pattern.length() && deviceName.find( pattern ) == 0;
            }) != end( deviceBlacklist ) )
            continue;
        auto uuid = result->d_name;
        LOG_INFO( "Discovered device ", deviceName, " -> {", uuid, '}' );
        res[deviceName] = uuid;
    }
    return res;
}

Device::MountpointMap Device::listMountpoints()
{
    static const std::vector<std::string> allowedFsType = { "vfat", "exfat", "sdcardfs", "fuse",
                                                            "ntfs", "fat32", "ext3", "ext4", "esdfs" };
    MountpointMap res;
    char buff[512];
    errno = 0;
    mntent* s;
#ifndef __ANDROID__
    mntent smnt;
    FILE* f = setmntent("/etc/mtab", "r");
    if ( f == nullptr )
        throw std::runtime_error( "Failed to read /etc/mtab" );
    std::unique_ptr<FILE, int(*)(FILE*)> fPtr( f, &endmntent );
    while ( getmntent_r( f, &smnt, buff, sizeof(buff) ) != nullptr )
    {
        s = &smnt;
#else
    FILE* f = fopen( "/proc/mounts", "r" );
    std::unique_ptr<FILE, int(*)(FILE*)> fPtr( f, &fclose );
    while ( ( s = getmntent( f ) ) )
    {
#endif
        if ( std::find( begin( allowedFsType ), end( allowedFsType ), s->mnt_type ) == end( allowedFsType ) )
            continue;
        auto deviceName = s->mnt_fsname;
        LOG_INFO( "Discovered mountpoint ", deviceName, " mounted on ", s->mnt_dir, " (", s->mnt_type, ')' );
        res[deviceName] = s->mnt_dir;
        errno = 0;
    }
    if ( errno != 0 )
    {
        LOG_ERROR( "Failed to read mountpoints: ", strerror( errno ) );
    }
    return res;
}

Device::DeviceCacheMap Device::populateDeviceCache()
{
    Device::DeviceCacheMap res;
    for ( const auto& p : Mountpoints )
    {
        const auto& devicePath = p.first;
        auto deviceName = utils::file::fileName( devicePath );
        const auto& mountpoint = p.second;
        auto it = Devices.find( deviceName );
        std::string uuid;
        if ( it != end( Devices ) )
            uuid = it->second;
        else
        {
            deviceName = deviceFromDeviceMapper( devicePath );
            it = Devices.find( deviceName );
            if ( it != end( Devices ) )
                uuid = it->second;
            else
            {
                LOG_ERROR( "Failed to resolve mountpoint ", mountpoint, " to any known device" );
                continue;
            }
        }
        auto removable = isRemovable( deviceName, mountpoint );
        LOG_INFO( "Adding device to cache: {", uuid, "} mounted on ", mountpoint, " Removable: ", removable );
        res[uuid] = std::make_shared<DeviceBuilder>( uuid, mountpoint, removable );
    }
    return res;
}

std::string Device::deviceFromDeviceMapper( const std::string& devicePath )
{
    if ( devicePath.find( "/dev/mapper" ) != 0 )
        return {};
    char linkPath[PATH_MAX];
    if ( readlink( devicePath.c_str(), linkPath, PATH_MAX ) < 0 )
    {
        std::stringstream err;
        err << "Failed to resolve device -> mapper link: "
            << devicePath << " (" << strerror(errno) << ')';
        throw std::runtime_error( err.str() );
    }
    const auto dmName = utils::file::fileName( linkPath );
    std::string dmSlavePath = "/sys/block/" + dmName + "/slaves";
    std::unique_ptr<DIR, int(*)(DIR*)> dir( opendir( dmSlavePath.c_str() ), &closedir );
    std::string res;
    if ( dir == nullptr )
        return {};
    dirent* result;
    while ( ( result = readdir( dir.get() ) ) != nullptr )
    {
        if ( strcmp( result->d_name, "." ) == 0 ||
             strcmp( result->d_name, ".." ) == 0 )
        {
            continue;
        }
        if ( res.empty() == true )
            res = result->d_name;
        else
            LOG_WARN( "More than one slave for device mapper ", linkPath );
    }
    LOG_INFO( "Device mapper ", dmName, " maps to ", res );
    return res;
}

bool Device::isRemovable( const std::string& deviceName, const std::string& mountpoint )
{
#ifndef TIZEN
    (void)mountpoint;
    std::stringstream removableFilePath;
    removableFilePath << "/sys/block/" << deviceName << "/removable";
    std::unique_ptr<FILE, int(*)(FILE*)> removableFile( fopen( removableFilePath.str().c_str(), "r" ), &fclose );
    // Assume the file isn't removable by default
    if ( removableFile != nullptr )
    {
        char buff;
        if ( fread(&buff, sizeof(buff), 1, removableFile.get() ) != 1 )
            return buff == '1';
        return false;
    }
#else
    (void)deviceName;
    static const std::vector<std::string> SDMountpoints = { "/opt/storage/sdcard" };
    auto it = std::find_if( begin( SDMountpoints ), end( SDMountpoints ), [mountpoint]( const std::string& pattern ) {
        return mountpoint.length() >= pattern.length() && mountpoint.find( pattern ) == 0;
    });
    if ( it != end( SDMountpoints ) )
    {
        LOG_INFO( "Considering mountpoint ", mountpoint, " a removable SDCard" );
        return true;
    }
#endif
    return false;
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2011 Timur Iskhodzhanov and MIPT students. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <map>
#include <set>

#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "base/common.h"
#include "base/random.h"

using namespace testing;

// Dependency injection
// http://en.wikipedia.org/wiki/Dependency_injection

class UdpChannel {
 public:
  virtual void Send(const char *buffer, size_t size) = 0;
  virtual bool Receive(char *buffer, size_t *size) = 0;
};

class MockUdpChannel : public UdpChannel {
 public:
  MOCK_METHOD2(Send, void(const char *buffer, size_t size));
  MOCK_METHOD2(Receive, bool(char *buffer, size_t *size));
};

class ReliableUdpChannel {
 public:
  ReliableUdpChannel(UdpChannel *chan) : channel_(chan), next_receive_(0) { }

  void Heartbeat() {
    const size_t MAX_BUFFER = 1024;

    char local_buffer[MAX_BUFFER];
    size_t local_size = MAX_BUFFER;

    if (!retry_ids_.empty()) {
      // TODO(timurrrr): throttle to avoid excessive traffic
      local_buffer[0] = 'R';
      local_buffer[1] = '0' + *retry_ids_.begin();
      local_buffer[2] = '\0';
      channel_->Send(local_buffer, 3);
    }

    // TODO(timurrrr): check return value
    channel_->Receive(local_buffer, &local_size);

    // TODO(timurrrr): the header should be int or larger...
    CHECK(*local_buffer >= '0' && *local_buffer <= '9');
    size_t index = *local_buffer - '0';
    if (received_.find(index) != received_.end() || index < next_receive_) {
      // Hm, packet received twice?.. Drop it.
      return;
    }

    // TODO(timurrrr): drop packets if received_.size() is too large.

    char * payload = new char[local_size - 1];
    memcpy(payload, local_buffer + 1, local_size - 1);

    received_[index] = std::pair<size_t,char*>(local_size - 1, payload);
  }

  void Send(const char *buffer, size_t size) { NOT_IMPLEMENTED; }

  bool Receive(char *buffer, size_t *size) {
    if (received_.find(next_receive_) == received_.end())
      Heartbeat();

    if (received_.find(next_receive_) == received_.end()) {
      if (!received_.empty()) {
        retry_ids_.insert(next_receive_);
      }
      return false;
    }

    size_t data_size = received_[next_receive_].first;
    char *data = received_[next_receive_].second;

    if (*size < data_size)
      return false;

    *size = data_size;
    memcpy(buffer, data, data_size);
    next_receive_++;
    return true;
  }

 private:
  UdpChannel *channel_;
  size_t next_receive_;
  std::map<size_t, std::pair<size_t, char*> > received_;
  std::set<size_t> retry_ids_;
};

ACTION_P(MockReceive, str) {
  size_t required_len = strlen(str) + 1;
  if (required_len > *arg1)
    return false;
  strcpy(arg0, str);
  *arg1 = required_len;
  return true;
}

TEST(ReliableUdpChannel, SimpleSequence) {
  MockUdpChannel mock;
  EXPECT_CALL(mock, Receive(_,_))
        .WillOnce(MockReceive("0H"))
        .WillOnce(MockReceive("1E"))
        .WillOnce(MockReceive("2L"))
        .WillOnce(MockReceive("3L"))
        .WillOnce(MockReceive("4O"));

  char buff[10] = {};
  size_t size;
  ReliableUdpChannel channel(&mock);

  const char *expectations[] = {"H", "E", "L", "L", "O"};
  for (size_t i = 0; i < ARRAY_SIZE(expectations); i++) {
    size = sizeof(buff);
    ASSERT_TRUE(channel.Receive(buff, &size));
    ASSERT_STREQ(expectations[i], buff);
  }
}

TEST(ReliableUdpChannel, OnePacketLost) {
  MockUdpChannel mock;
  // Default action. See
  // http://code.google.com/p/googlemock/wiki/ForDummies#All_Expectations_Are_Sticky_(Unless_Said_Otherwise)
  EXPECT_CALL(mock, Send(StrEq("R1"), Eq(3u))).Times(AnyNumber());

  // http://code.google.com/p/googlemock/wiki/CheatSheet#Sequences
  Sequence main, retries /* and before retries */;
  EXPECT_CALL(mock, Receive(_,_))
      .InSequence(main, retries)
      .WillOnce(MockReceive("0H"))
      // .WillOnce(MockReceive("1E"))  <-- lost!
      .WillOnce(MockReceive("2L"));
  EXPECT_CALL(mock, Send(StrEq("R1"), Eq(3u)))
      .InSequence(retries)
      .RetiresOnSaturation();
  EXPECT_CALL(mock, Receive(_,_))
      .InSequence(main)
      .WillOnce(MockReceive("3L"))
      .WillOnce(MockReceive("1E"))  // <-- answer to R1
      .WillOnce(MockReceive("4O"));

  char buff[10] = {};
  size_t size;
  ReliableUdpChannel channel(&mock);

  const char *expectations[] = {"H", "E", "L", "L", "O"};
  for (size_t i = 0; i < ARRAY_SIZE(expectations); i++) {
    size = sizeof(buff);
    int attempts = 20;

    while(!channel.Receive(buff, &size) && --attempts > 0)
      ;
    ASSERT_GT(attempts, 0) << "Too many Receive attempts, aborting";
    ASSERT_STREQ(expectations[i], buff);
  }
}

// TODO(timurrrr): random Receive==false tests
// TODO(timurrrr): packets reordered test

// TODO(timurrrr): Tests for Send()

class FakeUdpChannel : public UdpChannel {
 public:
  virtual void Send(const char *buffer, size_t size) {
    // TODO: make packet loss adjustable
    if (random_.Generate<int>() % 6 == 0)
      return;
    NOT_IMPLEMENTED;
  }
  virtual bool Receive(char *buffer, size_t *size) {
    NOT_IMPLEMENTED;
  }

 private:
  RandomGenerator random_;
  /* TODO:
     std::queue<>
     */
};

// TODO: write a test sending 100K of data over a lossy FakeUdpChannel
// and make sure the other end receives it all.
<commit_msg>Plug a memory leak found by Valgrind<commit_after>// Copyright (c) 2011 Timur Iskhodzhanov and MIPT students. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <map>
#include <set>

#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "base/common.h"
#include "base/random.h"

using namespace testing;

// Dependency injection
// http://en.wikipedia.org/wiki/Dependency_injection

class UdpChannel {
 public:
  virtual void Send(const char *buffer, size_t size) = 0;
  virtual bool Receive(char *buffer, size_t *size) = 0;
};

class MockUdpChannel : public UdpChannel {
 public:
  MOCK_METHOD2(Send, void(const char *buffer, size_t size));
  MOCK_METHOD2(Receive, bool(char *buffer, size_t *size));
};

class ReliableUdpChannel {
 public:
  ReliableUdpChannel(UdpChannel *chan) : channel_(chan), next_receive_(0) { }

  ~ReliableUdpChannel() {
    // TODO(timurrrr): free messages not taken from received_.
  }

  void Heartbeat() {
    const size_t MAX_BUFFER = 1024;

    char local_buffer[MAX_BUFFER];
    size_t local_size = MAX_BUFFER;

    if (!retry_ids_.empty()) {
      // TODO(timurrrr): throttle to avoid excessive traffic
      local_buffer[0] = 'R';
      local_buffer[1] = '0' + *retry_ids_.begin();
      local_buffer[2] = '\0';
      channel_->Send(local_buffer, 3);
    }

    // TODO(timurrrr): check return value
    channel_->Receive(local_buffer, &local_size);

    // TODO(timurrrr): the header should be int or larger...
    CHECK(*local_buffer >= '0' && *local_buffer <= '9');
    size_t index = *local_buffer - '0';
    if (received_.find(index) != received_.end() || index < next_receive_) {
      // Hm, packet received twice?.. Drop it.
      return;
    }

    // TODO(timurrrr): drop packets if received_.size() is too large.

    char * payload = new char[local_size - 1];
    memcpy(payload, local_buffer + 1, local_size - 1);

    received_[index] = std::pair<size_t,char*>(local_size - 1, payload);
  }

  void Send(const char *buffer, size_t size) { NOT_IMPLEMENTED; }

  bool Receive(char *buffer, size_t *size) {
    if (received_.find(next_receive_) == received_.end())
      Heartbeat();

    if (received_.find(next_receive_) == received_.end()) {
      if (!received_.empty()) {
        retry_ids_.insert(next_receive_);
      }
      return false;
    }

    size_t data_size = received_[next_receive_].first;
    char *data = received_[next_receive_].second;

    if (*size < data_size)
      return false;

    *size = data_size;
    memcpy(buffer, data, data_size);
    delete [] data;
    next_receive_++;
    return true;
  }

 private:
  UdpChannel *channel_;
  size_t next_receive_;
  std::map<size_t, std::pair<size_t, char*> > received_;
  std::set<size_t> retry_ids_;
};

ACTION_P(MockReceive, str) {
  size_t required_len = strlen(str) + 1;
  if (required_len > *arg1)
    return false;
  strcpy(arg0, str);
  *arg1 = required_len;
  return true;
}

TEST(ReliableUdpChannel, SimpleSequence) {
  MockUdpChannel mock;
  EXPECT_CALL(mock, Receive(_,_))
        .WillOnce(MockReceive("0H"))
        .WillOnce(MockReceive("1E"))
        .WillOnce(MockReceive("2L"))
        .WillOnce(MockReceive("3L"))
        .WillOnce(MockReceive("4O"));

  char buff[10] = {};
  size_t size;
  ReliableUdpChannel channel(&mock);

  const char *expectations[] = {"H", "E", "L", "L", "O"};
  for (size_t i = 0; i < ARRAY_SIZE(expectations); i++) {
    size = sizeof(buff);
    ASSERT_TRUE(channel.Receive(buff, &size));
    ASSERT_STREQ(expectations[i], buff);
  }
}

TEST(ReliableUdpChannel, OnePacketLost) {
  MockUdpChannel mock;
  // Default action. See
  // http://code.google.com/p/googlemock/wiki/ForDummies#All_Expectations_Are_Sticky_(Unless_Said_Otherwise)
  EXPECT_CALL(mock, Send(StrEq("R1"), Eq(3u))).Times(AnyNumber());

  // http://code.google.com/p/googlemock/wiki/CheatSheet#Sequences
  Sequence main, retries /* and before retries */;
  EXPECT_CALL(mock, Receive(_,_))
      .InSequence(main, retries)
      .WillOnce(MockReceive("0H"))
      // .WillOnce(MockReceive("1E"))  <-- lost!
      .WillOnce(MockReceive("2L"));
  EXPECT_CALL(mock, Send(StrEq("R1"), Eq(3u)))
      .InSequence(retries)
      .RetiresOnSaturation();
  EXPECT_CALL(mock, Receive(_,_))
      .InSequence(main)
      .WillOnce(MockReceive("3L"))
      .WillOnce(MockReceive("1E"))  // <-- answer to R1
      .WillOnce(MockReceive("4O"));

  char buff[10] = {};
  size_t size;
  ReliableUdpChannel channel(&mock);

  const char *expectations[] = {"H", "E", "L", "L", "O"};
  for (size_t i = 0; i < ARRAY_SIZE(expectations); i++) {
    size = sizeof(buff);
    int attempts = 20;

    while(!channel.Receive(buff, &size) && --attempts > 0)
      ;
    ASSERT_GT(attempts, 0) << "Too many Receive attempts, aborting";
    ASSERT_STREQ(expectations[i], buff);
  }
}

// TODO(timurrrr): random Receive==false tests
// TODO(timurrrr): packets reordered test

// TODO(timurrrr): Tests for Send()

class FakeUdpChannel : public UdpChannel {
 public:
  virtual void Send(const char *buffer, size_t size) {
    // TODO: make packet loss adjustable
    if (random_.Generate<int>() % 6 == 0)
      return;
    NOT_IMPLEMENTED;
  }
  virtual bool Receive(char *buffer, size_t *size) {
    NOT_IMPLEMENTED;
  }

 private:
  RandomGenerator random_;
  /* TODO:
     std::queue<>
     */
};

// TODO: write a test sending 100K of data over a lossy FakeUdpChannel
// and make sure the other end receives it all.
<|endoftext|>