<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 MapQuest
 *
 * This library 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 library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>

#include <mapnik/label_collision_detector.hpp>
#include <mapnik/map.hpp>

#include <list>

using mapnik::label_collision_detector4;
using mapnik::box2d;
using mapnik::Map;

namespace
{

boost::shared_ptr<label_collision_detector4>
create_label_collision_detector_from_extent(box2d<double> const &extent)
{
   return boost::shared_ptr<label_collision_detector4>(
      new label_collision_detector4(extent));
}

boost::shared_ptr<label_collision_detector4>
create_label_collision_detector_from_map(Map const &m)
{
   double buffer = m.buffer_size();
   box2d<double> extent(-buffer, -buffer, m.width() + buffer, m.height() + buffer);
   return boost::shared_ptr<label_collision_detector4>(
      new label_collision_detector4(extent));
}

boost::python::list
make_label_boxes(boost::shared_ptr<label_collision_detector4> det)
{
   boost::python::list boxes;

   for (label_collision_detector4::query_iterator jtr = det->begin();
        jtr != det->end(); ++jtr)
   {
      boxes.append<box2d<double> >(jtr->box);
   }

   return boxes;
}

}

void export_label_collision_detector()
{
   using namespace boost::python;

   // for overload resolution
   void (label_collision_detector4::*insert_box)(box2d<double> const &) = &label_collision_detector4::insert;

   class_<label_collision_detector4, boost::shared_ptr<label_collision_detector4>, boost::noncopyable>
      ("LabelCollisionDetector",
       "Object to detect collisions between labels, used in the rendering process.",
       no_init)

      .def("__init__", make_constructor(create_label_collision_detector_from_extent),
           "Creates an empty collision detection object with a given extent. Note "
           "that the constructor from Map objects is a sensible default and usually "
           "what you want to do.\n"
           "\n"
           "Example:\n"
           ">>> m = Map(size_x, size_y)\n"
           ">>> buf_sz = m.buffer_size\n"
           ">>> extent = mapnik.Box2d(-buf_sz, -buf_sz, m.width + buf_sz, m.height + buf_sz)\n"
           ">>> detector = mapnik.LabelCollisionDetector(extent)")

      .def("__init__", make_constructor(create_label_collision_detector_from_map),
           "Creates an empty collision detection object matching the given Map object. "
           "The created detector will have the same size, including the buffer, as the "
           "map object. This is usually what you want to do.\n"
           "\n"
           "Example:\n"
           ">>> m = Map(size_x, size_y)\n"
           ">>> detector = mapnik.LabelCollisionDetector(m)")

      .def("extent", &label_collision_detector4::extent, return_value_policy<copy_const_reference>(),
           "Returns the total extent (bounding box) of all labels inside the detector.\n"
           "\n"
           "Example:\n"
           ">>> detector.extent()\n"
           "Box2d(573.252589209,494.789179821,584.261023823,496.83610261)")

      .def("boxes", &make_label_boxes,
           "Returns a list of all the label boxes inside the detector.")

      .def("insert", insert_box,
           "Insert a 2d box into the collision detector. This can be used to ensure that "
           "some space is left clear on the map for later overdrawing, for example by "
           "non-Mapnik processes.\n"
           "\n"
           "Example:\n"
           ">>> m = Map(size_x, size_y)\n"
           ">>> detector = mapnik.LabelCollisionDetector(m)"
           ">>> detector.insert(mapnik.Box2d(196, 254, 291, 389))")
      ;
}
<commit_msg>Changed copyright notice in line with project convention.<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library 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 library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>

#include <mapnik/label_collision_detector.hpp>
#include <mapnik/map.hpp>

#include <list>

using mapnik::label_collision_detector4;
using mapnik::box2d;
using mapnik::Map;

namespace
{

boost::shared_ptr<label_collision_detector4>
create_label_collision_detector_from_extent(box2d<double> const &extent)
{
   return boost::shared_ptr<label_collision_detector4>(
      new label_collision_detector4(extent));
}

boost::shared_ptr<label_collision_detector4>
create_label_collision_detector_from_map(Map const &m)
{
   double buffer = m.buffer_size();
   box2d<double> extent(-buffer, -buffer, m.width() + buffer, m.height() + buffer);
   return boost::shared_ptr<label_collision_detector4>(
      new label_collision_detector4(extent));
}

boost::python::list
make_label_boxes(boost::shared_ptr<label_collision_detector4> det)
{
   boost::python::list boxes;

   for (label_collision_detector4::query_iterator jtr = det->begin();
        jtr != det->end(); ++jtr)
   {
      boxes.append<box2d<double> >(jtr->box);
   }

   return boxes;
}

}

void export_label_collision_detector()
{
   using namespace boost::python;

   // for overload resolution
   void (label_collision_detector4::*insert_box)(box2d<double> const &) = &label_collision_detector4::insert;

   class_<label_collision_detector4, boost::shared_ptr<label_collision_detector4>, boost::noncopyable>
      ("LabelCollisionDetector",
       "Object to detect collisions between labels, used in the rendering process.",
       no_init)

      .def("__init__", make_constructor(create_label_collision_detector_from_extent),
           "Creates an empty collision detection object with a given extent. Note "
           "that the constructor from Map objects is a sensible default and usually "
           "what you want to do.\n"
           "\n"
           "Example:\n"
           ">>> m = Map(size_x, size_y)\n"
           ">>> buf_sz = m.buffer_size\n"
           ">>> extent = mapnik.Box2d(-buf_sz, -buf_sz, m.width + buf_sz, m.height + buf_sz)\n"
           ">>> detector = mapnik.LabelCollisionDetector(extent)")

      .def("__init__", make_constructor(create_label_collision_detector_from_map),
           "Creates an empty collision detection object matching the given Map object. "
           "The created detector will have the same size, including the buffer, as the "
           "map object. This is usually what you want to do.\n"
           "\n"
           "Example:\n"
           ">>> m = Map(size_x, size_y)\n"
           ">>> detector = mapnik.LabelCollisionDetector(m)")

      .def("extent", &label_collision_detector4::extent, return_value_policy<copy_const_reference>(),
           "Returns the total extent (bounding box) of all labels inside the detector.\n"
           "\n"
           "Example:\n"
           ">>> detector.extent()\n"
           "Box2d(573.252589209,494.789179821,584.261023823,496.83610261)")

      .def("boxes", &make_label_boxes,
           "Returns a list of all the label boxes inside the detector.")

      .def("insert", insert_box,
           "Insert a 2d box into the collision detector. This can be used to ensure that "
           "some space is left clear on the map for later overdrawing, for example by "
           "non-Mapnik processes.\n"
           "\n"
           "Example:\n"
           ">>> m = Map(size_x, size_y)\n"
           ">>> detector = mapnik.LabelCollisionDetector(m)"
           ">>> detector.insert(mapnik.Box2d(196, 254, 291, 389))")
      ;
}
<|endoftext|>


<commit_before>// Copyright 2014 The Brave Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>

#include <memory>
#include <string>
#include <utility>

#include "brave/browser/guest_view/tab_view/tab_view_guest.h"

#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/api/event.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/extensions/atom_browser_client_extensions_part.h"
#include "atom/browser/extensions/tab_helper.h"
#include "atom/browser/native_window.h"
#include "atom/browser/web_contents_preferences.h"
#include "atom/common/native_mate_converters/content_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "base/memory/ptr_util.h"
#include "brave/browser/brave_browser_context.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "components/guest_view/browser/guest_view_event.h"
#include "components/guest_view/browser/guest_view_manager.h"
#include "components/guest_view/common/guest_view_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/guest_host.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/site_instance.h"
#include "extensions/browser/guest_view/web_view/web_view_constants.h"
#include "native_mate/dictionary.h"
#include "ui/base/page_transition_types.h"

using content::RenderFrameHost;
using content::RenderProcessHost;
using content::WebContents;
using extensions::AtomBrowserClientExtensionsPart;
using guest_view::GuestViewBase;
using guest_view::GuestViewEvent;
using guest_view::GuestViewManager;

namespace brave {

// static
GuestViewBase* TabViewGuest::Create(WebContents* owner_web_contents) {
  return new TabViewGuest(owner_web_contents);
}

// static
const char TabViewGuest::Type[] = "webview";

void TabViewGuest::SetCanRunInDetachedState(bool can_run_detached) {
  can_run_detached_ = can_run_detached;
  if (!can_run_detached_ && !attached())
    Destroy();
}

bool TabViewGuest::CanRunInDetachedState() const {
  return can_run_detached_;
}

void TabViewGuest::GuestDestroyed() {
  if (api_web_contents_) {
    api_web_contents_->guest_delegate_ = nullptr;
  }
}

void TabViewGuest::WebContentsCreated(WebContents* source_contents,
                                      int opener_render_process_id,
                                      int opener_render_frame_id,
                                      const std::string& frame_name,
                                      const GURL& target_url,
                                      WebContents* new_contents) {
  CHECK_EQ(source_contents, web_contents());

  auto* guest = TabViewGuest::FromWebContents(new_contents);
  CHECK(guest);

  guest->SetOpener(this);
  guest->name_ = frame_name;

  NewWindowInfo new_window_info(target_url, frame_name);
  // if the browser instance changes the old window won't be able to
  // load the url so let ApplyAttributes handle it
  bool changed =
      AtomBrowserClientExtensionsPart::ShouldSwapBrowsingInstancesForNavigation(
          source_contents->GetSiteInstance(),
          source_contents->GetURL(),
          target_url);
  new_window_info.changed = changed;
  pending_new_windows_.insert(std::make_pair(guest, new_window_info));
}

WebContents* TabViewGuest::OpenURLFromTab(
    WebContents* source,
    const content::OpenURLParams& params) {
  if (!attached()) {
    TabViewGuest* opener = GetOpener();
    // If the guest wishes to navigate away prior to attachment then we save the
    // navigation to perform upon attachment. Navigation initializes a lot of
    // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
    // Navigation also resumes resource loading. If we were created using
    // newwindow (i.e. we have an opener), we don't allow navigation until
    // attachment.
    if (opener) {
      auto it = opener->pending_new_windows_.find(this);
      if (it == opener->pending_new_windows_.end())
        return nullptr;
      const NewWindowInfo& info = it->second;
      NewWindowInfo new_window_info(params.url, info.name);
      new_window_info.changed = info.changed || new_window_info.url != info.url;
      it->second = new_window_info;
      return nullptr;
    }
  }

  // let the api_web_contents navigate
  return web_contents();
}

void TabViewGuest::LoadURLWithParams(
    const GURL& url,
    const content::Referrer& referrer,
    ui::PageTransition transition_type,
    bool force_navigation) {
  if (!force_navigation && (src_ == url))
    return;

  GURL validated_url(url);

  content::NavigationController::LoadURLParams load_url_params(validated_url);
  load_url_params.referrer = referrer;
  load_url_params.transition_type = transition_type;
  GuestViewBase::LoadURLWithParams(load_url_params);

  src_ = validated_url;
}

void TabViewGuest::Load() {
  if (web_contents()->GetController().IsInitialNavigation())
    NavigateGuest(src_.spec(), true);
}

void TabViewGuest::NavigateGuest(const std::string& src,
                                 bool force_navigation) {
  // auto tab_helper = extensions::TabHelper::FromWebContents(web_contents());
  if (src.empty())  // || tab_helper->is_placeholder())
    return;

  LoadURLWithParams(GURL(src), content::Referrer(),
      ui::PAGE_TRANSITION_AUTO_TOPLEVEL, force_navigation);
}

void TabViewGuest::DidCommitProvisionalLoadForFrame(
    content::RenderFrameHost* render_frame_host,
    const GURL& url,
    ui::PageTransition transition_type) {
  if (!render_frame_host->GetParent()) {
    src_ = web_contents()->GetURL();
  }

  std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
  args->SetString(guest_view::kUrl, src_.spec());
  args->SetBoolean(guest_view::kIsTopLevel, (!render_frame_host->GetParent() &&
                    ui::PageTransitionCoreTypeIs(
                      transition_type,
                      ui::PAGE_TRANSITION_AUTO_TOPLEVEL)));
  args->SetInteger(webview::kInternalCurrentEntryIndex,
                   web_contents()->GetController().GetCurrentEntryIndex());
  args->SetInteger(webview::kInternalEntryCount,
                   web_contents()->GetController().GetEntryCount());
  args->SetInteger(webview::kInternalProcessId,
                   web_contents()->GetRenderProcessHost()->GetID());
  DispatchEventToView(base::MakeUnique<GuestViewEvent>(
      webview::kEventLoadCommit, std::move(args)));

  // find_helper_.CancelAllFindSessions();
}

void TabViewGuest::AttachGuest(int guestInstanceId) {
  std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
  args->SetInteger("guestInstanceId", guestInstanceId);
  DispatchEventToView(base::MakeUnique<GuestViewEvent>(
      "webViewInternal.onAttachGuest", std::move(args)));
}

void TabViewGuest::DetachGuest() {
  api_web_contents_->Emit("will-detach",
      extensions::TabHelper::IdForTab(web_contents()));
  std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
  DispatchEventToView(base::MakeUnique<GuestViewEvent>(
      "webViewInternal.onDetachGuest", std::move(args)));
}

void TabViewGuest::TabIdChanged() {
  std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
  args->SetInteger("tabID", extensions::TabHelper::IdForTab(web_contents()));
  DispatchEventToView(base::MakeUnique<GuestViewEvent>(
      "webViewInternal.onTabIdChanged", std::move(args)));
}

void TabViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Locker locker(isolate);
  v8::HandleScope handle_scope(isolate);

  api_web_contents_ = atom::api::WebContents::CreateFrom(isolate,
      web_contents(), atom::api::WebContents::Type::WEB_VIEW).get();
  api_web_contents_->guest_delegate_ = this;
  web_contents()->SetDelegate(api_web_contents_);

  ApplyAttributes(create_params);
}

void TabViewGuest::CreateWebContents(
    const base::DictionaryValue& params,
    const WebContentsCreatedCallback& callback) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Locker locker(isolate);
  v8::HandleScope handle_scope(isolate);

  mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);

  std::string partition;
  params.GetString("partition", &partition);
  base::DictionaryValue partition_options;
  std::string parent_partition;
  if (params.GetString("parent_partition", &parent_partition)) {
    partition_options.SetString("parent_partition", parent_partition);
  } else {
    std::string session_tab_prefix = "persist:partition-";
    if (partition.compare(0, session_tab_prefix.length(),
          session_tab_prefix) == 0) {
      partition_options.SetString("parent_partition", "");
    }
  }
  atom::AtomBrowserContext* browser_context =
      brave::BraveBrowserContext::FromPartition(partition, partition_options);

  bool pinned = false;
  if (params.GetBoolean("pinned", &pinned)) {
    options.Set("pinned", pinned);
  }

  content::WebContents::CreateParams create_params(browser_context);
  create_params.guest_delegate = this;

  mate::Handle<atom::api::WebContents> new_api_web_contents =
      atom::api::WebContents::CreateWithParams(isolate, options, create_params);

  content::WebContents* web_contents = new_api_web_contents->web_contents();
  callback.Run(web_contents);
}

void TabViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
  bool is_pending_new_window = false;
  if (GetOpener()) {
    // We need to do a navigation here if the target URL has changed between
    // the time the WebContents was created and the time it was attached.
    // We also need to do an initial navigation if a RenderView was never
    // created for the new window in cases where there is no referrer.
    auto it = GetOpener()->pending_new_windows_.find(this);
    if (it != GetOpener()->pending_new_windows_.end()) {
      const NewWindowInfo& new_window_info = it->second;
      if (attached()) {
        if (new_window_info.changed || !web_contents()->HasOpener()) {
          NavigateGuest(
              new_window_info.url.spec(), false /* force_navigation */);
        }

        // Once a new guest is attached to the DOM of the embedder page the
        // lifetime of the new guest is no longer managed by the opener guest.
        GetOpener()->pending_new_windows_.erase(this);
      }
      is_pending_new_window = true;
    } else if (attached() && clone_) {
      clone_ = false;
      web_contents()->GetController().CopyStateFrom(
          GetOpener()->web_contents()->GetController());
    }
  }

  // handle navigation for src attribute changes
  if (!is_pending_new_window) {
    bool clone = false;
    if (params.GetBoolean("clone", &clone) && clone) {
      clone_ = true;
    } else {
      std::string src;
      if (params.GetString("src", &src)) {
        src_ = GURL(src);
      }

      if (attached() &&
          web_contents()->GetController().IsInitialNavigation()) {
        NavigateGuest(src_.spec(), true);
      }
    }
  }
}

void TabViewGuest::DidAttachToEmbedder() {
  DCHECK(api_web_contents_);

  auto tab_helper = extensions::TabHelper::FromWebContents(web_contents());

  if (!tab_helper->IsDiscarded()) {
    api_web_contents_->ResumeLoadingCreatedWebContents();

    web_contents()->WasHidden();
    web_contents()->WasShown();
  }

  ApplyAttributes(*attach_params());

  if (web_contents()->GetController().IsInitialNavigation()) {
    web_contents()->GetController().LoadIfNecessary();
  }

  api_web_contents_->Emit("did-attach",
      extensions::TabHelper::IdForTab(web_contents()));

  tab_helper->DidAttach();
}

void TabViewGuest::DidDetachFromEmbedder() {
  if (api_web_contents_) {
    api_web_contents_->Emit("did-detach",
        extensions::TabHelper::IdForTab(web_contents()));
  }
}

bool TabViewGuest::ZoomPropagatesFromEmbedderToGuest() const {
  return false;
}

const char* TabViewGuest::GetAPINamespace() const {
  return "webViewInternal";
}

int TabViewGuest::GetTaskPrefix() const {
  // return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
  return -1;
}

void TabViewGuest::GuestReady() {
  // we don't use guest only processes and don't want those limitations
  CHECK(!web_contents()->GetRenderProcessHost()->IsForGuestsOnly());

  web_contents()
      ->GetRenderViewHost()
      ->GetWidget()
      ->GetView()
      ->SetBackgroundColorToDefault();

  api_web_contents_->Emit("guest-ready",
      extensions::TabHelper::IdForTab(web_contents()), guest_instance_id());
}

void TabViewGuest::WillDestroy() {
  if (api_web_contents_)
    api_web_contents_->WebContentsDestroyed();
  api_web_contents_ = nullptr;

  if (!attached() && GetOpener())
    GetOpener()->pending_new_windows_.erase(this);
}

void TabViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
                                                 const gfx::Size& new_size) {
  if (api_web_contents_)
    api_web_contents_->Emit("size-changed",
                            old_size.width(), old_size.height(),
                            new_size.width(), new_size.height());
}

bool TabViewGuest::IsAutoSizeSupported() const {
  return true;
}

TabViewGuest::TabViewGuest(WebContents* owner_web_contents)
    : GuestView<TabViewGuest>(owner_web_contents),
      api_web_contents_(nullptr),
      clone_(false),
      can_run_detached_(true) {
}

TabViewGuest::~TabViewGuest() {
}

void TabViewGuest::WillAttachToEmbedder() {
  DCHECK(api_web_contents_);
  api_web_contents_->Emit("will-attach", owner_web_contents());

  // update the owner window
  atom::NativeWindow* owner_window = nullptr;
  auto relay = atom::NativeWindowRelay::FromWebContents(
      owner_web_contents());
  if (relay)
    owner_window = relay->window.get();

  if (owner_window) {
    TabIdChanged();
    api_web_contents_->SetOwnerWindow(owner_window);
  }
}

}  // namespace brave
<commit_msg>don’t send messages to webcontents when they are detached auditors @bbondy @darkdh<commit_after>// Copyright 2014 The Brave Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>

#include <memory>
#include <string>
#include <utility>

#include "brave/browser/guest_view/tab_view/tab_view_guest.h"

#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/api/event.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/extensions/atom_browser_client_extensions_part.h"
#include "atom/browser/extensions/tab_helper.h"
#include "atom/browser/native_window.h"
#include "atom/browser/web_contents_preferences.h"
#include "atom/common/native_mate_converters/content_converter.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "base/memory/ptr_util.h"
#include "brave/browser/brave_browser_context.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "components/guest_view/browser/guest_view_event.h"
#include "components/guest_view/browser/guest_view_manager.h"
#include "components/guest_view/common/guest_view_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/guest_host.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/site_instance.h"
#include "extensions/browser/guest_view/web_view/web_view_constants.h"
#include "native_mate/dictionary.h"
#include "ui/base/page_transition_types.h"

using content::RenderFrameHost;
using content::RenderProcessHost;
using content::WebContents;
using extensions::AtomBrowserClientExtensionsPart;
using guest_view::GuestViewBase;
using guest_view::GuestViewEvent;
using guest_view::GuestViewManager;

namespace brave {

// static
GuestViewBase* TabViewGuest::Create(WebContents* owner_web_contents) {
  return new TabViewGuest(owner_web_contents);
}

// static
const char TabViewGuest::Type[] = "webview";

void TabViewGuest::SetCanRunInDetachedState(bool can_run_detached) {
  can_run_detached_ = can_run_detached;
  if (!can_run_detached_ && !attached())
    Destroy();
}

bool TabViewGuest::CanRunInDetachedState() const {
  return can_run_detached_;
}

void TabViewGuest::GuestDestroyed() {
  if (api_web_contents_) {
    api_web_contents_->guest_delegate_ = nullptr;
  }
}

void TabViewGuest::WebContentsCreated(WebContents* source_contents,
                                      int opener_render_process_id,
                                      int opener_render_frame_id,
                                      const std::string& frame_name,
                                      const GURL& target_url,
                                      WebContents* new_contents) {
  CHECK_EQ(source_contents, web_contents());

  auto* guest = TabViewGuest::FromWebContents(new_contents);
  CHECK(guest);

  guest->SetOpener(this);
  guest->name_ = frame_name;

  NewWindowInfo new_window_info(target_url, frame_name);
  // if the browser instance changes the old window won't be able to
  // load the url so let ApplyAttributes handle it
  bool changed =
      AtomBrowserClientExtensionsPart::ShouldSwapBrowsingInstancesForNavigation(
          source_contents->GetSiteInstance(),
          source_contents->GetURL(),
          target_url);
  new_window_info.changed = changed;
  pending_new_windows_.insert(std::make_pair(guest, new_window_info));
}

WebContents* TabViewGuest::OpenURLFromTab(
    WebContents* source,
    const content::OpenURLParams& params) {
  if (!attached()) {
    TabViewGuest* opener = GetOpener();
    // If the guest wishes to navigate away prior to attachment then we save the
    // navigation to perform upon attachment. Navigation initializes a lot of
    // state that assumes an embedder exists, such as RenderWidgetHostViewGuest.
    // Navigation also resumes resource loading. If we were created using
    // newwindow (i.e. we have an opener), we don't allow navigation until
    // attachment.
    if (opener) {
      auto it = opener->pending_new_windows_.find(this);
      if (it == opener->pending_new_windows_.end())
        return nullptr;
      const NewWindowInfo& info = it->second;
      NewWindowInfo new_window_info(params.url, info.name);
      new_window_info.changed = info.changed || new_window_info.url != info.url;
      it->second = new_window_info;
      return nullptr;
    }
  }

  // let the api_web_contents navigate
  return web_contents();
}

void TabViewGuest::LoadURLWithParams(
    const GURL& url,
    const content::Referrer& referrer,
    ui::PageTransition transition_type,
    bool force_navigation) {
  if (!force_navigation && (src_ == url))
    return;

  GURL validated_url(url);

  content::NavigationController::LoadURLParams load_url_params(validated_url);
  load_url_params.referrer = referrer;
  load_url_params.transition_type = transition_type;
  GuestViewBase::LoadURLWithParams(load_url_params);

  src_ = validated_url;
}

void TabViewGuest::Load() {
  if (web_contents()->GetController().IsInitialNavigation())
    NavigateGuest(src_.spec(), true);
}

void TabViewGuest::NavigateGuest(const std::string& src,
                                 bool force_navigation) {
  // auto tab_helper = extensions::TabHelper::FromWebContents(web_contents());
  if (src.empty())  // || tab_helper->is_placeholder())
    return;

  LoadURLWithParams(GURL(src), content::Referrer(),
      ui::PAGE_TRANSITION_AUTO_TOPLEVEL, force_navigation);
}

void TabViewGuest::DidCommitProvisionalLoadForFrame(
    content::RenderFrameHost* render_frame_host,
    const GURL& url,
    ui::PageTransition transition_type) {
  if (!render_frame_host->GetParent()) {
    src_ = web_contents()->GetURL();
  }

  std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
  args->SetString(guest_view::kUrl, src_.spec());
  args->SetBoolean(guest_view::kIsTopLevel, (!render_frame_host->GetParent() &&
                    ui::PageTransitionCoreTypeIs(
                      transition_type,
                      ui::PAGE_TRANSITION_AUTO_TOPLEVEL)));
  args->SetInteger(webview::kInternalCurrentEntryIndex,
                   web_contents()->GetController().GetCurrentEntryIndex());
  args->SetInteger(webview::kInternalEntryCount,
                   web_contents()->GetController().GetEntryCount());
  args->SetInteger(webview::kInternalProcessId,
                   web_contents()->GetRenderProcessHost()->GetID());
  DispatchEventToView(base::MakeUnique<GuestViewEvent>(
      webview::kEventLoadCommit, std::move(args)));

  // find_helper_.CancelAllFindSessions();
}

void TabViewGuest::AttachGuest(int guestInstanceId) {
  std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
  args->SetInteger("guestInstanceId", guestInstanceId);
  DispatchEventToView(base::MakeUnique<GuestViewEvent>(
      "webViewInternal.onAttachGuest", std::move(args)));
}

void TabViewGuest::DetachGuest() {
  api_web_contents_->Emit("will-detach",
      extensions::TabHelper::IdForTab(web_contents()));
  std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
  DispatchEventToView(base::MakeUnique<GuestViewEvent>(
      "webViewInternal.onDetachGuest", std::move(args)));
}

void TabViewGuest::TabIdChanged() {
  std::unique_ptr<base::DictionaryValue> args(new base::DictionaryValue());
  args->SetInteger("tabID", extensions::TabHelper::IdForTab(web_contents()));
  DispatchEventToView(base::MakeUnique<GuestViewEvent>(
      "webViewInternal.onTabIdChanged", std::move(args)));
}

void TabViewGuest::DidInitialize(const base::DictionaryValue& create_params) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Locker locker(isolate);
  v8::HandleScope handle_scope(isolate);

  api_web_contents_ = atom::api::WebContents::CreateFrom(isolate,
      web_contents(), atom::api::WebContents::Type::WEB_VIEW).get();
  api_web_contents_->guest_delegate_ = this;
  web_contents()->SetDelegate(api_web_contents_);

  ApplyAttributes(create_params);
}

void TabViewGuest::CreateWebContents(
    const base::DictionaryValue& params,
    const WebContentsCreatedCallback& callback) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Locker locker(isolate);
  v8::HandleScope handle_scope(isolate);

  mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);

  std::string partition;
  params.GetString("partition", &partition);
  base::DictionaryValue partition_options;
  std::string parent_partition;
  if (params.GetString("parent_partition", &parent_partition)) {
    partition_options.SetString("parent_partition", parent_partition);
  } else {
    std::string session_tab_prefix = "persist:partition-";
    if (partition.compare(0, session_tab_prefix.length(),
          session_tab_prefix) == 0) {
      partition_options.SetString("parent_partition", "");
    }
  }
  atom::AtomBrowserContext* browser_context =
      brave::BraveBrowserContext::FromPartition(partition, partition_options);

  bool pinned = false;
  if (params.GetBoolean("pinned", &pinned)) {
    options.Set("pinned", pinned);
  }

  content::WebContents::CreateParams create_params(browser_context);
  create_params.guest_delegate = this;

  mate::Handle<atom::api::WebContents> new_api_web_contents =
      atom::api::WebContents::CreateWithParams(isolate, options, create_params);

  content::WebContents* web_contents = new_api_web_contents->web_contents();
  callback.Run(web_contents);
}

void TabViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
  bool is_pending_new_window = false;
  if (GetOpener()) {
    // We need to do a navigation here if the target URL has changed between
    // the time the WebContents was created and the time it was attached.
    // We also need to do an initial navigation if a RenderView was never
    // created for the new window in cases where there is no referrer.
    auto it = GetOpener()->pending_new_windows_.find(this);
    if (it != GetOpener()->pending_new_windows_.end()) {
      const NewWindowInfo& new_window_info = it->second;
      if (attached()) {
        if (new_window_info.changed || !web_contents()->HasOpener()) {
          NavigateGuest(
              new_window_info.url.spec(), false /* force_navigation */);
        }

        // Once a new guest is attached to the DOM of the embedder page the
        // lifetime of the new guest is no longer managed by the opener guest.
        GetOpener()->pending_new_windows_.erase(this);
      }
      is_pending_new_window = true;
    } else if (attached() && clone_) {
      clone_ = false;
      web_contents()->GetController().CopyStateFrom(
          GetOpener()->web_contents()->GetController());
    }
  }

  // handle navigation for src attribute changes
  if (!is_pending_new_window) {
    bool clone = false;
    if (params.GetBoolean("clone", &clone) && clone) {
      clone_ = true;
    } else {
      std::string src;
      if (params.GetString("src", &src)) {
        src_ = GURL(src);
      }

      if (attached() &&
          web_contents()->GetController().IsInitialNavigation()) {
        NavigateGuest(src_.spec(), true);
      }
    }
  }
}

void TabViewGuest::DidAttachToEmbedder() {
  DCHECK(api_web_contents_);

  content::ResourceDispatcherHost::ResumeBlockedRequestsForFrameFromUI(
        web_contents()->GetMainFrame());

  auto tab_helper = extensions::TabHelper::FromWebContents(web_contents());

  if (!tab_helper->IsDiscarded()) {
    api_web_contents_->ResumeLoadingCreatedWebContents();

    web_contents()->WasHidden();
    web_contents()->WasShown();
  }

  ApplyAttributes(*attach_params());

  if (web_contents()->GetController().IsInitialNavigation()) {
    web_contents()->GetController().LoadIfNecessary();
  }

  api_web_contents_->Emit("did-attach",
      extensions::TabHelper::IdForTab(web_contents()));

  tab_helper->DidAttach();
}

void TabViewGuest::DidDetachFromEmbedder() {
  content::ResourceDispatcherHost::BlockRequestsForFrameFromUI(
      web_contents()->GetMainFrame());

  if (api_web_contents_) {
    api_web_contents_->Emit("did-detach",
        extensions::TabHelper::IdForTab(web_contents()));
  }
}

bool TabViewGuest::ZoomPropagatesFromEmbedderToGuest() const {
  return false;
}

const char* TabViewGuest::GetAPINamespace() const {
  return "webViewInternal";
}

int TabViewGuest::GetTaskPrefix() const {
  // return IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX;
  return -1;
}

void TabViewGuest::GuestReady() {
  // we don't use guest only processes and don't want those limitations
  CHECK(!web_contents()->GetRenderProcessHost()->IsForGuestsOnly());

  web_contents()
      ->GetRenderViewHost()
      ->GetWidget()
      ->GetView()
      ->SetBackgroundColorToDefault();

  api_web_contents_->Emit("guest-ready",
      extensions::TabHelper::IdForTab(web_contents()), guest_instance_id());
}

void TabViewGuest::WillDestroy() {
  if (api_web_contents_)
    api_web_contents_->WebContentsDestroyed();
  api_web_contents_ = nullptr;

  if (!attached() && GetOpener())
    GetOpener()->pending_new_windows_.erase(this);
}

void TabViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size,
                                                 const gfx::Size& new_size) {
  if (api_web_contents_)
    api_web_contents_->Emit("size-changed",
                            old_size.width(), old_size.height(),
                            new_size.width(), new_size.height());
}

bool TabViewGuest::IsAutoSizeSupported() const {
  return true;
}

TabViewGuest::TabViewGuest(WebContents* owner_web_contents)
    : GuestView<TabViewGuest>(owner_web_contents),
      api_web_contents_(nullptr),
      clone_(false),
      can_run_detached_(true) {
}

TabViewGuest::~TabViewGuest() {
}

void TabViewGuest::WillAttachToEmbedder() {
  DCHECK(api_web_contents_);
  api_web_contents_->Emit("will-attach", owner_web_contents());

  // update the owner window
  atom::NativeWindow* owner_window = nullptr;
  auto relay = atom::NativeWindowRelay::FromWebContents(
      owner_web_contents());
  if (relay)
    owner_window = relay->window.get();

  if (owner_window) {
    TabIdChanged();
    api_web_contents_->SetOwnerWindow(owner_window);
  }
}

}  // namespace brave
<|endoftext|>


<commit_before>/* xml_helpers.h                                                   -*- C++ -*-
   Jeremy Barnes, 12 May 2013
   Copyright (c) 2013 Datacratic Inc.  All rights reserved.

   Helper functions to deal with XML.
*/

#include "xml_helpers.h"

namespace Datacratic {

tinyxml2::XMLElement *
extractNode(tinyxml2::XMLElement * element, const std::string & path)
{
    using namespace std;

    vector<string> splitPath = ML::split(path, '/');
    tinyxml2::XMLElement * p = element;
    for (unsigned i = 0;  i < splitPath.size();  ++i) {
        p = p->FirstChildElement(splitPath[i].c_str());
        if (!p) {
            //element->GetDocument()->Print();
            throw ML::Exception("required key " + splitPath[i]
                                + " not found on path " + path);
        }
    }

    return p;
}

}
<commit_msg>fixed decl for gcc 4.6<commit_after>/* xml_helpers.cc                                                  -*- C++ -*-
   Jeremy Barnes, 12 May 2013
   Copyright (c) 2013 Datacratic Inc.  All rights reserved.

   Helper functions to deal with XML.
*/

#include <memory>

#include "jml/utils/string_functions.h"

#include "xml_helpers.h"

using namespace std;

namespace Datacratic {

tinyxml2::XMLElement *
extractNode(tinyxml2::XMLElement * element, const string & path)
{
    using namespace std;

    vector<string> splitPath = ML::split(path, '/');
    tinyxml2::XMLElement * p = element;
    for (unsigned i = 0;  i < splitPath.size();  ++i) {
        p = p->FirstChildElement(splitPath[i].c_str());
        if (!p) {
            //element->GetDocument()->Print();
            throw ML::Exception("required key " + splitPath[i]
                                + " not found on path " + path);
        }
    }

    return p;
}

}
<|endoftext|>


<commit_before>// PythonScripting.cpp
//
// Breannan Smith
// Last updated: 09/10/2015

#include "PythonScripting.h"

#ifdef USE_PYTHON
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
#include "scisim/PythonTools.h"
#include "scisim/Math/Rational.h"
#include "RigidBody2DState.h"
#endif

#include <iostream>

#ifdef USE_PYTHON
static scalar s_timestep;
static unsigned s_next_iteration;
static RigidBody2DState* s_state;
static VectorXs* s_mu;
static VectorXs* s_cor;
static const std::vector<std::unique_ptr<Constraint>>* s_active_set;
#endif

PythonScripting::PythonScripting()
: m_path()
, m_module_name()
#ifdef USE_PYTHON
, m_loaded_module( nullptr )
, m_loaded_start_of_sim_callback( nullptr )
, m_loaded_end_of_sim_callback( nullptr )
, m_loaded_start_of_step_callback( nullptr )
, m_loaded_end_of_step_callback( nullptr )
, m_loaded_friction_coefficient_callback( nullptr )
, m_loaded_restitution_coefficient_callback( nullptr )
#endif
{}

PythonScripting::PythonScripting( const std::string& path, const std::string& module_name )
: m_path( path )
, m_module_name( module_name )
#ifdef USE_PYTHON
, m_loaded_module( nullptr )
, m_loaded_start_of_sim_callback( nullptr )
, m_loaded_end_of_sim_callback( nullptr )
, m_loaded_start_of_step_callback( nullptr )
, m_loaded_end_of_step_callback( nullptr )
, m_loaded_friction_coefficient_callback( nullptr )
, m_loaded_restitution_coefficient_callback( nullptr )
#endif
{
  intializePythonCallbacks();
}

PythonScripting::PythonScripting( std::istream& input_stream )
: m_path( StringUtilities::deserializeString( input_stream ) )
, m_module_name( StringUtilities::deserializeString( input_stream ) )
#ifdef USE_PYTHON
, m_loaded_module( nullptr )
, m_loaded_start_of_sim_callback( nullptr )
, m_loaded_end_of_sim_callback( nullptr )
, m_loaded_start_of_step_callback( nullptr )
, m_loaded_end_of_step_callback( nullptr )
, m_loaded_friction_coefficient_callback( nullptr )
, m_loaded_restitution_coefficient_callback( nullptr )
#endif
{
  intializePythonCallbacks();
}

void PythonScripting::intializePythonCallbacks()
{
  #ifdef USE_PYTHON
  // If no module name was provided, nothing to do
  if( m_module_name.empty() )
  {
    return;
  }

  // Load the module
  assert( m_loaded_module == nullptr );
  PythonTools::loadModule( m_path, m_module_name, m_loaded_module );

  // Hook up the start of sim callback
  assert( m_loaded_start_of_sim_callback == nullptr );
  PythonTools::loadFunction( "startOfSim", m_loaded_module, m_loaded_start_of_sim_callback );

  // Hook up the end of sim callback
  assert( m_loaded_end_of_sim_callback == nullptr );
  PythonTools::loadFunction( "endOfSim", m_loaded_module, m_loaded_end_of_sim_callback );

  // Hook up the start of step callback
  assert( m_loaded_start_of_step_callback == nullptr );
  PythonTools::loadFunction( "startOfStep", m_loaded_module, m_loaded_start_of_step_callback );

  // Hook up the end of step callback
  assert( m_loaded_end_of_step_callback == nullptr );
  PythonTools::loadFunction( "endOfStep", m_loaded_module, m_loaded_end_of_step_callback );

  // Hook up the friction coefficient callback
  assert( m_loaded_friction_coefficient_callback == nullptr );
  PythonTools::loadFunction( "frictionCoefficient", m_loaded_module, m_loaded_friction_coefficient_callback );

  // Hook up the restitution coefficient callback
  assert( m_loaded_restitution_coefficient_callback == nullptr );
  PythonTools::loadFunction( "restitutionCoefficient", m_loaded_module, m_loaded_restitution_coefficient_callback );
  assert( PyErr_Occurred() == nullptr );
  #else
  if( !m_module_name.empty() )
  {
    std::cerr << "Error, Python callback " << m_module_name << " requested, but program is not compiled with Python support. Exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  #endif
}

void swap( PythonScripting& first, PythonScripting& second )
{
  using std::swap;
  swap( first.m_path, second.m_path );
  swap( first.m_module_name, second.m_module_name );
  #ifdef USE_PYTHON
  swap( first.m_loaded_module, second.m_loaded_module );
  swap( first.m_loaded_start_of_sim_callback, second.m_loaded_start_of_sim_callback );
  swap( first.m_loaded_end_of_sim_callback, second.m_loaded_end_of_sim_callback );
  swap( first.m_loaded_start_of_step_callback, second.m_loaded_start_of_step_callback );
  swap( first.m_loaded_end_of_step_callback, second.m_loaded_end_of_step_callback );
  swap( first.m_loaded_friction_coefficient_callback, second.m_loaded_friction_coefficient_callback );
  swap( first.m_loaded_restitution_coefficient_callback, second.m_loaded_restitution_coefficient_callback );
  #endif
}

void PythonScripting::restitutionCoefficient( const std::vector<std::unique_ptr<Constraint>>& active_set, VectorXs& cor )
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_restitution_coefficient_callback == nullptr )
  {
    return;
  }
  // Get data ready for Python
  s_cor = &cor;
  s_active_set = &active_set;
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_restitution_coefficient_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback restitutionCoefficient failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_cor = nullptr;
  s_active_set = nullptr;
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::restitutionCoefficient must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::frictionCoefficient( const std::vector<std::unique_ptr<Constraint>>& active_set, VectorXs& mu )
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_friction_coefficient_callback == nullptr )
  {
    return;
  }
  // Get data ready for Python
  s_mu = μ
  s_active_set = &active_set;
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_friction_coefficient_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback frictionCoefficient failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_mu = nullptr;
  s_active_set = nullptr;
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::frictionCoefficient must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::startOfSim()
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_start_of_sim_callback == nullptr )
  {
    return;
  }
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_start_of_sim_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback startOfSim failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::startOfSim must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::endOfSim()
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_end_of_sim_callback == nullptr )
  {
    return;
  }
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_end_of_sim_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback endOfSim failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::endOfSim must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::startOfStep( const unsigned next_iteration, const Rational<std::intmax_t>& dt )
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_start_of_step_callback == nullptr )
  {
    return;
  }
  // Get data ready for Python
  s_timestep = scalar( dt );
  s_next_iteration = next_iteration;
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_start_of_step_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback startOfStep failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_timestep = std::numeric_limits<scalar>::signaling_NaN();
  s_next_iteration = 0;
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::startOfStep must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::endOfStep( const unsigned next_iteration, const Rational<std::intmax_t>& dt )
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_end_of_step_callback == nullptr )
  {
    return;
  }
  // Get data ready for Python
  s_timestep = scalar( dt );
  s_next_iteration = next_iteration;
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_end_of_step_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback endOfStep failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_timestep = std::numeric_limits<scalar>::signaling_NaN();
  s_next_iteration = 0;
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::endOfStep must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

std::string PythonScripting::name() const
{
  return m_module_name;
}

void PythonScripting::setState( RigidBody2DState& state )
{
  #ifdef USE_PYTHON
  s_state = &state;
  #endif
  // No need to handle state cache if scripting is disabled
}

void PythonScripting::forgetState()
{
  #ifdef USE_PYTHON
  s_state = nullptr;
  #endif
  // No need to handle state cache if scripting is disabled
}

void PythonScripting::serialize( std::ostream& output_stream )
{
  StringUtilities::serializeString( m_path, output_stream );
  StringUtilities::serializeString( m_module_name, output_stream );
  // Python variables are re-initialized on deserializaiton, so no action needed here
}

#ifdef USE_PYTHON
static PyObject* timestep( PyObject* self, PyObject* args )
{
  assert( args == nullptr );
  using std::is_same;
  static_assert( is_same<scalar,double>::value || is_same<scalar,float>::value, "Error, scalar type must be double or float for Python interface." );
  return Py_BuildValue( is_same<scalar,double>::value ? "d" : "f", s_timestep );
}

static PyObject* nextIteration( PyObject* self, PyObject* args )
{
  assert( args == nullptr );
  return Py_BuildValue( "I", s_next_iteration );
}

static PyObject* numStaticPlanes( PyObject* self, PyObject* args )
{
  assert( args == nullptr );
  assert( s_state != nullptr );
  return Py_BuildValue( "I", s_state->planes().size() );
}

static PyObject* deleteStaticPlane( PyObject* self, PyObject* args )
{
  unsigned plane_idx;
  assert( args != nullptr );
  if( !PyArg_ParseTuple( args, "I", &plane_idx ) )
  {
    PyErr_Print();
    std::cerr << "Failed to read parameters for deleteStaticPlane, parameters are: unsigned plane_idx. Exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  assert( s_state != nullptr );
  if( plane_idx > s_state->planes().size() )
  {
    std::cerr << "Invalid plane_idx parameter of " << plane_idx << " in deleteStaticPlane, plane_idx must be less than " << s_state->planes().size() << ". Exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_state->planes().erase( s_state->planes().begin() + plane_idx );
  return Py_BuildValue( "" );
}

static PyMethodDef RigidBody2DFunctions[] = {
  { "timestep", timestep, METH_NOARGS, "Returns the timestep." },
  { "nextIteration", nextIteration, METH_NOARGS, "Returns the end of step iteration." },
  { "numStaticPlanes", numStaticPlanes, METH_NOARGS, "Returns the number of static planes." },
  { "deleteStaticPlane", deleteStaticPlane, METH_VARARGS, "Deletes a static plane." },
  { nullptr, nullptr, 0, nullptr }
};

void PythonScripting::initializeCallbacks()
{
  if( _import_array() < 0 )
  {
    std::cerr << "Bad import array!" << std::endl;
    std::exit( EXIT_FAILURE );
  }
  Py_InitModule( "rigidbody2d", RigidBody2DFunctions );
}
#endif
<commit_msg>Fixed a build issue when python scripting is disabled<commit_after>// PythonScripting.cpp
//
// Breannan Smith
// Last updated: 09/10/2015

#include "PythonScripting.h"

#ifdef USE_PYTHON
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
#include "scisim/PythonTools.h"
#include "scisim/Math/Rational.h"
#include "RigidBody2DState.h"
#endif

#include "scisim/StringUtilities.h"

#include <iostream>

#ifdef USE_PYTHON
static scalar s_timestep;
static unsigned s_next_iteration;
static RigidBody2DState* s_state;
static VectorXs* s_mu;
static VectorXs* s_cor;
static const std::vector<std::unique_ptr<Constraint>>* s_active_set;
#endif

PythonScripting::PythonScripting()
: m_path()
, m_module_name()
#ifdef USE_PYTHON
, m_loaded_module( nullptr )
, m_loaded_start_of_sim_callback( nullptr )
, m_loaded_end_of_sim_callback( nullptr )
, m_loaded_start_of_step_callback( nullptr )
, m_loaded_end_of_step_callback( nullptr )
, m_loaded_friction_coefficient_callback( nullptr )
, m_loaded_restitution_coefficient_callback( nullptr )
#endif
{}

PythonScripting::PythonScripting( const std::string& path, const std::string& module_name )
: m_path( path )
, m_module_name( module_name )
#ifdef USE_PYTHON
, m_loaded_module( nullptr )
, m_loaded_start_of_sim_callback( nullptr )
, m_loaded_end_of_sim_callback( nullptr )
, m_loaded_start_of_step_callback( nullptr )
, m_loaded_end_of_step_callback( nullptr )
, m_loaded_friction_coefficient_callback( nullptr )
, m_loaded_restitution_coefficient_callback( nullptr )
#endif
{
  intializePythonCallbacks();
}

PythonScripting::PythonScripting( std::istream& input_stream )
: m_path( StringUtilities::deserializeString( input_stream ) )
, m_module_name( StringUtilities::deserializeString( input_stream ) )
#ifdef USE_PYTHON
, m_loaded_module( nullptr )
, m_loaded_start_of_sim_callback( nullptr )
, m_loaded_end_of_sim_callback( nullptr )
, m_loaded_start_of_step_callback( nullptr )
, m_loaded_end_of_step_callback( nullptr )
, m_loaded_friction_coefficient_callback( nullptr )
, m_loaded_restitution_coefficient_callback( nullptr )
#endif
{
  intializePythonCallbacks();
}

void PythonScripting::intializePythonCallbacks()
{
  #ifdef USE_PYTHON
  // If no module name was provided, nothing to do
  if( m_module_name.empty() )
  {
    return;
  }

  // Load the module
  assert( m_loaded_module == nullptr );
  PythonTools::loadModule( m_path, m_module_name, m_loaded_module );

  // Hook up the start of sim callback
  assert( m_loaded_start_of_sim_callback == nullptr );
  PythonTools::loadFunction( "startOfSim", m_loaded_module, m_loaded_start_of_sim_callback );

  // Hook up the end of sim callback
  assert( m_loaded_end_of_sim_callback == nullptr );
  PythonTools::loadFunction( "endOfSim", m_loaded_module, m_loaded_end_of_sim_callback );

  // Hook up the start of step callback
  assert( m_loaded_start_of_step_callback == nullptr );
  PythonTools::loadFunction( "startOfStep", m_loaded_module, m_loaded_start_of_step_callback );

  // Hook up the end of step callback
  assert( m_loaded_end_of_step_callback == nullptr );
  PythonTools::loadFunction( "endOfStep", m_loaded_module, m_loaded_end_of_step_callback );

  // Hook up the friction coefficient callback
  assert( m_loaded_friction_coefficient_callback == nullptr );
  PythonTools::loadFunction( "frictionCoefficient", m_loaded_module, m_loaded_friction_coefficient_callback );

  // Hook up the restitution coefficient callback
  assert( m_loaded_restitution_coefficient_callback == nullptr );
  PythonTools::loadFunction( "restitutionCoefficient", m_loaded_module, m_loaded_restitution_coefficient_callback );
  assert( PyErr_Occurred() == nullptr );
  #else
  if( !m_module_name.empty() )
  {
    std::cerr << "Error, Python callback " << m_module_name << " requested, but program is not compiled with Python support. Exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  #endif
}

void swap( PythonScripting& first, PythonScripting& second )
{
  using std::swap;
  swap( first.m_path, second.m_path );
  swap( first.m_module_name, second.m_module_name );
  #ifdef USE_PYTHON
  swap( first.m_loaded_module, second.m_loaded_module );
  swap( first.m_loaded_start_of_sim_callback, second.m_loaded_start_of_sim_callback );
  swap( first.m_loaded_end_of_sim_callback, second.m_loaded_end_of_sim_callback );
  swap( first.m_loaded_start_of_step_callback, second.m_loaded_start_of_step_callback );
  swap( first.m_loaded_end_of_step_callback, second.m_loaded_end_of_step_callback );
  swap( first.m_loaded_friction_coefficient_callback, second.m_loaded_friction_coefficient_callback );
  swap( first.m_loaded_restitution_coefficient_callback, second.m_loaded_restitution_coefficient_callback );
  #endif
}

void PythonScripting::restitutionCoefficient( const std::vector<std::unique_ptr<Constraint>>& active_set, VectorXs& cor )
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_restitution_coefficient_callback == nullptr )
  {
    return;
  }
  // Get data ready for Python
  s_cor = &cor;
  s_active_set = &active_set;
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_restitution_coefficient_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback restitutionCoefficient failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_cor = nullptr;
  s_active_set = nullptr;
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::restitutionCoefficient must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::frictionCoefficient( const std::vector<std::unique_ptr<Constraint>>& active_set, VectorXs& mu )
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_friction_coefficient_callback == nullptr )
  {
    return;
  }
  // Get data ready for Python
  s_mu = μ
  s_active_set = &active_set;
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_friction_coefficient_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback frictionCoefficient failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_mu = nullptr;
  s_active_set = nullptr;
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::frictionCoefficient must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::startOfSim()
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_start_of_sim_callback == nullptr )
  {
    return;
  }
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_start_of_sim_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback startOfSim failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::startOfSim must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::endOfSim()
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_end_of_sim_callback == nullptr )
  {
    return;
  }
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_end_of_sim_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback endOfSim failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::endOfSim must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::startOfStep( const unsigned next_iteration, const Rational<std::intmax_t>& dt )
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_start_of_step_callback == nullptr )
  {
    return;
  }
  // Get data ready for Python
  s_timestep = scalar( dt );
  s_next_iteration = next_iteration;
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_start_of_step_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback startOfStep failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_timestep = std::numeric_limits<scalar>::signaling_NaN();
  s_next_iteration = 0;
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::startOfStep must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

void PythonScripting::endOfStep( const unsigned next_iteration, const Rational<std::intmax_t>& dt )
{
  #ifdef USE_PYTHON
  assert( !m_module_name.empty() );
  assert( m_loaded_module != nullptr );
  if( m_loaded_end_of_step_callback == nullptr )
  {
    return;
  }
  // Get data ready for Python
  s_timestep = scalar( dt );
  s_next_iteration = next_iteration;
  // Make the function call
  const PythonObject value{ PyObject_CallObject( m_loaded_end_of_step_callback, nullptr ) };
  if( value == nullptr )
  {
    PyErr_Print();
    std::cerr << "Python callback endOfStep failed, exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_timestep = std::numeric_limits<scalar>::signaling_NaN();
  s_next_iteration = 0;
  assert( PyErr_Occurred() == nullptr );
  #else
  std::cerr << "PythonScripting::endOfStep must be compiled with Python support, exiting." << std::endl;
  std::exit( EXIT_FAILURE );
  #endif
}

std::string PythonScripting::name() const
{
  return m_module_name;
}

void PythonScripting::setState( RigidBody2DState& state )
{
  #ifdef USE_PYTHON
  s_state = &state;
  #endif
  // No need to handle state cache if scripting is disabled
}

void PythonScripting::forgetState()
{
  #ifdef USE_PYTHON
  s_state = nullptr;
  #endif
  // No need to handle state cache if scripting is disabled
}

void PythonScripting::serialize( std::ostream& output_stream )
{
  StringUtilities::serializeString( m_path, output_stream );
  StringUtilities::serializeString( m_module_name, output_stream );
  // Python variables are re-initialized on deserializaiton, so no action needed here
}

#ifdef USE_PYTHON
static PyObject* timestep( PyObject* self, PyObject* args )
{
  assert( args == nullptr );
  using std::is_same;
  static_assert( is_same<scalar,double>::value || is_same<scalar,float>::value, "Error, scalar type must be double or float for Python interface." );
  return Py_BuildValue( is_same<scalar,double>::value ? "d" : "f", s_timestep );
}

static PyObject* nextIteration( PyObject* self, PyObject* args )
{
  assert( args == nullptr );
  return Py_BuildValue( "I", s_next_iteration );
}

static PyObject* numStaticPlanes( PyObject* self, PyObject* args )
{
  assert( args == nullptr );
  assert( s_state != nullptr );
  return Py_BuildValue( "I", s_state->planes().size() );
}

static PyObject* deleteStaticPlane( PyObject* self, PyObject* args )
{
  unsigned plane_idx;
  assert( args != nullptr );
  if( !PyArg_ParseTuple( args, "I", &plane_idx ) )
  {
    PyErr_Print();
    std::cerr << "Failed to read parameters for deleteStaticPlane, parameters are: unsigned plane_idx. Exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  assert( s_state != nullptr );
  if( plane_idx > s_state->planes().size() )
  {
    std::cerr << "Invalid plane_idx parameter of " << plane_idx << " in deleteStaticPlane, plane_idx must be less than " << s_state->planes().size() << ". Exiting." << std::endl;
    std::exit( EXIT_FAILURE );
  }
  s_state->planes().erase( s_state->planes().begin() + plane_idx );
  return Py_BuildValue( "" );
}

static PyMethodDef RigidBody2DFunctions[] = {
  { "timestep", timestep, METH_NOARGS, "Returns the timestep." },
  { "nextIteration", nextIteration, METH_NOARGS, "Returns the end of step iteration." },
  { "numStaticPlanes", numStaticPlanes, METH_NOARGS, "Returns the number of static planes." },
  { "deleteStaticPlane", deleteStaticPlane, METH_VARARGS, "Deletes a static plane." },
  { nullptr, nullptr, 0, nullptr }
};

void PythonScripting::initializeCallbacks()
{
  if( _import_array() < 0 )
  {
    std::cerr << "Bad import array!" << std::endl;
    std::exit( EXIT_FAILURE );
  }
  Py_InitModule( "rigidbody2d", RigidBody2DFunctions );
}
#endif
<|endoftext|>


<commit_before>/* Copyright 2021 Stanford University, NVIDIA Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// portability wrapper over NUMA system interfaces

// NOTE: this is nowhere near a full libnuma-style interface - it's just the
//  calls that Realm's NUMA module needs

#include "realm/numa/numasysif.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include <vector>

#ifdef REALM_ON_LINUX
#include <alloca.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/mempolicy.h>
#include <dirent.h>
#include <sched.h>
#include <ctype.h>
#include <sys/mman.h>

namespace {
  long get_mempolicy(int *policy, unsigned long *nmask,
		     unsigned long maxnode, void *addr, int flags)
  {
    return syscall(__NR_get_mempolicy, policy, nmask,
		   maxnode, addr, flags);
  }

  long mbind(void *start, unsigned long len, int mode,
	     const unsigned long *nmask, unsigned long maxnode, unsigned flags)
  {
    return syscall(__NR_mbind, (long)start, len, mode, (long)nmask,
		   maxnode, flags);
  }

#if 0
  long set_mempolicy(int mode, const unsigned long *nmask,
		     unsigned long maxnode)
  {
    return syscall(__NR_set_mempolicy, mode, nmask, maxnode);
  }
#endif
};
#endif

namespace Realm {

  // as soon as we get more than one real version of these, split them out into
  //  separate files

#ifdef REALM_ON_LINUX
  namespace {
    // Linux wants you to guess how many nodes there are, and if you're wrong,
    //  it just tells you to try again - save the answer here so we only have
    //  to do it once
    int detected_node_count = 8 * sizeof(unsigned long);
    const int max_supported_node_count = 1024 * sizeof(unsigned long);
  };

  static bool mask_nonempty(const unsigned char *nmask, int max_count)
  {
    for(int i = 0; i < (max_count >> 3); i++)
      if(nmask[i] != 0)
	return true;
    return false;
  }
#endif

  // is NUMA support available in the system?
  bool numasysif_numa_available(void)
  {
#ifdef REALM_ON_LINUX
    int policy;
    unsigned char *nmask = (unsigned char *)alloca(max_supported_node_count >> 3);
    while(1) {
      errno = 0;
      int ret = get_mempolicy(&policy,
			      (unsigned long *)nmask, detected_node_count,
			      0, MPOL_F_MEMS_ALLOWED);
      if(ret == 0) break;

      // EINVAL maybe means our mask isn't big enough
      if((errno == EINVAL) &&
	 (detected_node_count < max_supported_node_count)) {
	detected_node_count <<= 1;
      } else {
	// otherwise we're out of luck
        //fprintf(stderr, "get_mempolicy() returned: ret=%d errno=%d\n", ret, errno);
	return false;
      }
    }

    // also check that we have at least one node set in the mask - if not,
    //  assume numa support is disabled
    if(!mask_nonempty(nmask, detected_node_count)) {
      //fprintf(stderr, "get_mempolicy() returned empty node mask!\n");
      return false;
    }

    return true;
#else
    return false;
#endif
  }

  // return info on the memory and cpu in each NUMA node
  // default is to restrict to only those nodes enabled in the current affinity mask
  bool numasysif_get_mem_info(std::map<int, NumaNodeMemInfo>& info,
			      bool only_available /*= true*/)
  {
#ifdef REALM_ON_LINUX
    int policy = -1;
    unsigned char *nmask = (unsigned char *)alloca(detected_node_count >> 3);
    for(int i = 0; i < detected_node_count >> 3; i++)
      nmask[i] = 0;
    int ret = -1;
    if(only_available) {
      // first, ask for the default policy for the thread to detect binding via
      //  numactl, mpirun, etc.
      errno = 0;
      ret = get_mempolicy(&policy,
			  (unsigned long *)nmask, detected_node_count, 0, 0);
    }
    if((ret != 0) || (policy != MPOL_BIND) ||
       !mask_nonempty(nmask, detected_node_count)) {
      // not a reasonable-looking bound state, so ask for all nodes
      errno = 0;
      ret = get_mempolicy(&policy,
			  (unsigned long *)nmask, detected_node_count,
			  0, MPOL_F_MEMS_ALLOWED);
      if((ret != 0) || !mask_nonempty(nmask, detected_node_count)) {
	// this really shouldn't fail, since we made the same call above in
	//  numasysif_numa_available()
	fprintf(stderr, "mems_allowed: ret=%d errno=%d mask=%08lx count=%d\n",
		ret, errno, *(unsigned long *)nmask, detected_node_count);
	return false;
      }
    }

    // for each bit set in the mask, try to query the free memory
    for(int i = 0; i < detected_node_count; i++)
      if(((nmask[i >> 3] >> (i & 7)) & 1) != 0) {
	// free information comes from /sys...
	char fname[80];
	sprintf(fname, "/sys/devices/system/node/node%d/meminfo", i);
	FILE *f = fopen(fname, "r");
	if(!f) {
	  fprintf(stderr, "can't read '%s': %s\n", fname, strerror(errno));
	  continue;
	}
	char line[256];
	while(fgets(line, 256, f)) {
	  const char *s = strstr(line, "MemFree");
	  if(!s) continue;
	  const char *endptr;
	  errno = 0;
	  long long sz = strtoll(s+9, (char **)&endptr, 10);
	  if((errno != 0) || strcmp(endptr, " kB\n")) {
	    fprintf(stderr, "ill-formed line: '%s' '%s'\n", s, endptr);
	    continue;
	  }
	  // success - add this to the list and stop reading
	  NumaNodeMemInfo& mi = info[i];
	  mi.node_id = i;
	  mi.bytes_available = (sz << 10);
	  break;
	}
	// if we get all the way through the file without finding the size,
	//   we just don't add anything to the info
	fclose(f);
      }

    // as long as we got at least one valid node, assume we're successful
    return !info.empty();
#else
    return false;
#endif
  }

  bool numasysif_get_cpu_info(std::map<int, NumaNodeCpuInfo>& info,
			      bool only_available /*= true*/)
  {
#ifdef REALM_ON_LINUX
    // if we're restricting to what's been made available, find what's been
    //  made available
    cpu_set_t avail_cpus;
    if(only_available) {
      int ret = sched_getaffinity(0, sizeof(avail_cpus), &avail_cpus);
      if(ret != 0) {
	fprintf(stderr, "sched_getaffinity failed: %s\n", strerror(errno));
	return false;
      }
    } else
      CPU_ZERO(&avail_cpus);

    // now enumerate cpus via /sys and determine which nodes they belong to
    std::map<int, int> cpu_counts;
    DIR *cpudir = opendir("/sys/devices/system/cpu");
    if(!cpudir) {
      fprintf(stderr, "couldn't read /sys/devices/system/cpu: %s\n", strerror(errno));
      return false;
    }
    struct dirent *de;
    while((de = readdir(cpudir)) != 0)
      if(!strncmp(de->d_name, "cpu", 3)) {
	int cpu_index = atoi(de->d_name + 3);
	if(only_available && !CPU_ISSET(cpu_index, &avail_cpus))
	  continue;
	// find the node symlink to determine the node
	char path2[256];
	sprintf(path2, "/sys/devices/system/cpu/%.16s", de->d_name);
	DIR *d2 = opendir(path2);
	if(!d2) {
	  fprintf(stderr, "couldn't read '%s': %s\n", path2, strerror(errno));
	  continue;
	}
	struct dirent *de2;
	while((de2 = readdir(d2)) != 0)
	  if(!strncmp(de2->d_name, "node", 4)) {
	    int node_index = atoi(de2->d_name + 4);
	    cpu_counts[node_index]++;
	    break;
	  }
	closedir(d2);
      }

    // any matches is "success"
    if(!cpu_counts.empty()) {
      for(std::map<int,int>::const_iterator it = cpu_counts.begin();
	  it != cpu_counts.end();
	  ++it) {
	NumaNodeCpuInfo& ci = info[it->first];
	ci.node_id = it->first;
	ci.cores_available = it->second;
      }
      return true;
    } else
      return false;
#else
    return false;
#endif
  }

  // return the "distance" between two nodes - try to normalize to Linux's model of
  //  10 being the same node and the cost for other nodes increasing by roughly 10
  //  per hop
  int numasysif_get_distance(int node1, int node2)
  {
#ifdef REALM_ON_LINUX
    static std::map<int, std::vector<int> > saved_distances;

    std::map<int, std::vector<int> >::iterator it = saved_distances.find(node1);
    if(it == saved_distances.end()) {
      // not one we've already looked up, so do it now

      // if we break out early, we'll end up with an empty vector, which means
      //  we'll return -1 for all future queries
      std::vector<int>& v = saved_distances[node1];

      char fname[256];
      sprintf(fname, "/sys/devices/system/node/node%d/distance", node1);
      FILE *f = fopen(fname, "r");
      if(!f) {
	fprintf(stderr, "can't read '%s': %s\n", fname, strerror(errno));
	saved_distances[node1].clear();
	return -1;
      }
      char line[256];
      if(fgets(line, 256, f)) {
	char *p = line;
	while(isdigit(*p)) {
	  errno = 0;
	  int d = strtol(p, &p, 10);
	  if(errno != 0) break;
	  v.push_back(d);
	  while(isspace(*p)) p++;
	}
      }
      fclose(f);
      if((node2 >= 0) && (node2 < (int)v.size()))
	return v[node2];
      else
	return -1;
    } else {
      const std::vector<int>& v = it->second;
      if((node2 >= 0) && (node2 < (int)v.size()))
	return v[node2];
      else
	return -1;
    }
#else
    return -1;
#endif
  }

  // allocate memory on a given NUMA node - pin if requested
  void *numasysif_alloc_mem(int node, size_t bytes, bool pin)
  {
#ifdef REALM_ON_LINUX
    // get memory from mmap
    // TODO: hugetlbfs, if possible
    void *base = mmap(0,
		      bytes,
		      PROT_READ | PROT_WRITE,
		      MAP_PRIVATE | MAP_ANONYMOUS,
		      -1,
		      0);
    if(!base) return 0;

    // use the bind call for the rest
    if(numasysif_bind_mem(node, base, bytes, pin))
      return base;

    // if not, clean up and return failure
    numasysif_free_mem(node, base, bytes);
    return 0;
#else
    return 0;
#endif
  }

  // free memory allocated on a given NUMA node
  bool numasysif_free_mem(int node, void *base, size_t bytes)
  {
#ifdef REALM_ON_LINUX
    int ret = munmap(base, bytes);
    return(ret == 0);
#else
    return false;
#endif
  }

  // bind already-allocated memory to a given node - pin if requested
  // may fail if the memory has already been touched
  bool numasysif_bind_mem(int node, void *base, size_t bytes, bool pin)
  {
#ifdef REALM_ON_LINUX
    int policy = MPOL_BIND;
    if((node < 0) || (node >= detected_node_count)) {
      fprintf(stderr, "bind request for node out of range: %d\n", node);
      return false;
    }
    unsigned char *nmask = (unsigned char *)alloca(detected_node_count >> 3);
    for(int i = 0; i < detected_node_count >> 3; i++)
      nmask[i] = 0;
    nmask[(node >> 3)] = (1 << (node & 7));
    int ret = mbind(base, bytes,
		    policy,
		    (const unsigned long *)nmask, detected_node_count,
		    MPOL_MF_STRICT | MPOL_MF_MOVE);
    if(ret != 0) {
      fprintf(stderr, "failed to bind memory for node %d: %s\n", node, strerror(errno));
      return false;
    }

    // attempt to pin the memory if requested
    if(pin) {
      int ret = mlock(base, bytes);
      if(ret != 0) {
	fprintf(stderr, "mlock failed for memory on node %d: %s\n", node, strerror(errno));
	return false;
      }
    }

    return true;
#else
    return false;
#endif
  }

};
<commit_msg>realm: handle non-contiguous numa domain ids<commit_after>/* Copyright 2021 Stanford University, NVIDIA Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// portability wrapper over NUMA system interfaces

// NOTE: this is nowhere near a full libnuma-style interface - it's just the
//  calls that Realm's NUMA module needs

#include "realm/numa/numasysif.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include <vector>

#ifdef REALM_ON_LINUX
#include <alloca.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/mempolicy.h>
#include <dirent.h>
#include <sched.h>
#include <ctype.h>
#include <sys/mman.h>

namespace {
  long get_mempolicy(int *policy, unsigned long *nmask,
		     unsigned long maxnode, void *addr, int flags)
  {
    return syscall(__NR_get_mempolicy, policy, nmask,
		   maxnode, addr, flags);
  }

  long mbind(void *start, unsigned long len, int mode,
	     const unsigned long *nmask, unsigned long maxnode, unsigned flags)
  {
    return syscall(__NR_mbind, (long)start, len, mode, (long)nmask,
		   maxnode, flags);
  }

#if 0
  long set_mempolicy(int mode, const unsigned long *nmask,
		     unsigned long maxnode)
  {
    return syscall(__NR_set_mempolicy, mode, nmask, maxnode);
  }
#endif
};
#endif

namespace Realm {

  // as soon as we get more than one real version of these, split them out into
  //  separate files

#ifdef REALM_ON_LINUX
  namespace {
    // Linux wants you to guess how many nodes there are, and if you're wrong,
    //  it just tells you to try again - save the answer here so we only have
    //  to do it once
    int detected_node_count = 8 * sizeof(unsigned long);
    const int max_supported_node_count = 1024 * sizeof(unsigned long);
  };

  static bool mask_nonempty(const unsigned char *nmask, int max_count)
  {
    for(int i = 0; i < (max_count >> 3); i++)
      if(nmask[i] != 0)
	return true;
    return false;
  }
#endif

  // is NUMA support available in the system?
  bool numasysif_numa_available(void)
  {
#ifdef REALM_ON_LINUX
    int policy;
    unsigned char *nmask = (unsigned char *)alloca(max_supported_node_count >> 3);
    while(1) {
      errno = 0;
      int ret = get_mempolicy(&policy,
			      (unsigned long *)nmask, detected_node_count,
			      0, MPOL_F_MEMS_ALLOWED);
      if(ret == 0) break;

      // EINVAL maybe means our mask isn't big enough
      if((errno == EINVAL) &&
	 (detected_node_count < max_supported_node_count)) {
	detected_node_count <<= 1;
      } else {
	// otherwise we're out of luck
        //fprintf(stderr, "get_mempolicy() returned: ret=%d errno=%d\n", ret, errno);
	return false;
      }
    }

    // also check that we have at least one node set in the mask - if not,
    //  assume numa support is disabled
    if(!mask_nonempty(nmask, detected_node_count)) {
      //fprintf(stderr, "get_mempolicy() returned empty node mask!\n");
      return false;
    }

    return true;
#else
    return false;
#endif
  }

  // return info on the memory and cpu in each NUMA node
  // default is to restrict to only those nodes enabled in the current affinity mask
  bool numasysif_get_mem_info(std::map<int, NumaNodeMemInfo>& info,
			      bool only_available /*= true*/)
  {
#ifdef REALM_ON_LINUX
    int policy = -1;
    unsigned char *nmask = (unsigned char *)alloca(detected_node_count >> 3);
    for(int i = 0; i < detected_node_count >> 3; i++)
      nmask[i] = 0;
    int ret = -1;
    if(only_available) {
      // first, ask for the default policy for the thread to detect binding via
      //  numactl, mpirun, etc.
      errno = 0;
      ret = get_mempolicy(&policy,
			  (unsigned long *)nmask, detected_node_count, 0, 0);
    }
    if((ret != 0) || (policy != MPOL_BIND) ||
       !mask_nonempty(nmask, detected_node_count)) {
      // not a reasonable-looking bound state, so ask for all nodes
      errno = 0;
      ret = get_mempolicy(&policy,
			  (unsigned long *)nmask, detected_node_count,
			  0, MPOL_F_MEMS_ALLOWED);
      if((ret != 0) || !mask_nonempty(nmask, detected_node_count)) {
	// this really shouldn't fail, since we made the same call above in
	//  numasysif_numa_available()
	fprintf(stderr, "mems_allowed: ret=%d errno=%d mask=%08lx count=%d\n",
		ret, errno, *(unsigned long *)nmask, detected_node_count);
	return false;
      }
    }

    // for each bit set in the mask, try to query the free memory
    for(int i = 0; i < detected_node_count; i++)
      if(((nmask[i >> 3] >> (i & 7)) & 1) != 0) {
	// free information comes from /sys...
	char fname[80];
	sprintf(fname, "/sys/devices/system/node/node%d/meminfo", i);
	FILE *f = fopen(fname, "r");
	if(!f) {
	  fprintf(stderr, "can't read '%s': %s\n", fname, strerror(errno));
	  continue;
	}
	char line[256];
	while(fgets(line, 256, f)) {
	  const char *s = strstr(line, "MemFree");
	  if(!s) continue;
	  const char *endptr;
	  errno = 0;
	  long long sz = strtoll(s+9, (char **)&endptr, 10);
	  if((errno != 0) || strcmp(endptr, " kB\n")) {
	    fprintf(stderr, "ill-formed line: '%s' '%s'\n", s, endptr);
	    continue;
	  }
	  // success - add this to the list and stop reading
	  NumaNodeMemInfo& mi = info[i];
	  mi.node_id = i;
	  mi.bytes_available = (sz << 10);
	  break;
	}
	// if we get all the way through the file without finding the size,
	//   we just don't add anything to the info
	fclose(f);
      }

    // as long as we got at least one valid node, assume we're successful
    return !info.empty();
#else
    return false;
#endif
  }

  bool numasysif_get_cpu_info(std::map<int, NumaNodeCpuInfo>& info,
			      bool only_available /*= true*/)
  {
#ifdef REALM_ON_LINUX
    // if we're restricting to what's been made available, find what's been
    //  made available
    cpu_set_t avail_cpus;
    if(only_available) {
      int ret = sched_getaffinity(0, sizeof(avail_cpus), &avail_cpus);
      if(ret != 0) {
	fprintf(stderr, "sched_getaffinity failed: %s\n", strerror(errno));
	return false;
      }
    } else
      CPU_ZERO(&avail_cpus);

    // now enumerate cpus via /sys and determine which nodes they belong to
    std::map<int, int> cpu_counts;
    DIR *cpudir = opendir("/sys/devices/system/cpu");
    if(!cpudir) {
      fprintf(stderr, "couldn't read /sys/devices/system/cpu: %s\n", strerror(errno));
      return false;
    }
    struct dirent *de;
    while((de = readdir(cpudir)) != 0)
      if(!strncmp(de->d_name, "cpu", 3)) {
	int cpu_index = atoi(de->d_name + 3);
	if(only_available && !CPU_ISSET(cpu_index, &avail_cpus))
	  continue;
	// find the node symlink to determine the node
	char path2[256];
	sprintf(path2, "/sys/devices/system/cpu/%.16s", de->d_name);
	DIR *d2 = opendir(path2);
	if(!d2) {
	  fprintf(stderr, "couldn't read '%s': %s\n", path2, strerror(errno));
	  continue;
	}
	struct dirent *de2;
	while((de2 = readdir(d2)) != 0)
	  if(!strncmp(de2->d_name, "node", 4)) {
	    int node_index = atoi(de2->d_name + 4);
	    cpu_counts[node_index]++;
	    break;
	  }
	closedir(d2);
      }

    // any matches is "success"
    if(!cpu_counts.empty()) {
      for(std::map<int,int>::const_iterator it = cpu_counts.begin();
	  it != cpu_counts.end();
	  ++it) {
	NumaNodeCpuInfo& ci = info[it->first];
	ci.node_id = it->first;
	ci.cores_available = it->second;
      }
      return true;
    } else
      return false;
#else
    return false;
#endif
  }

  // return the "distance" between two nodes - try to normalize to Linux's model of
  //  10 being the same node and the cost for other nodes increasing by roughly 10
  //  per hop
  int numasysif_get_distance(int node1, int node2)
  {
#ifdef REALM_ON_LINUX
    static std::map<int, std::vector<int> > saved_distances;
    static std::map<int, int> saved_node2index;

    // distance files just have the distances for each node in order, but
    //  discontiguous numa node numbering can mean we need to convert node
    //  id into its index when in sorted order
    if(saved_node2index.empty()) {
      DIR *dh = opendir("/sys/devices/system/node");
      if(dh) {
        struct dirent *de;
        while((de = readdir(dh)) != nullptr)
          if(!strncmp(de->d_name, "node", 4)) {
            int id = atoi(de->d_name + 4);
            // just insert into map for now - we'll assign indices once we have
            //  them all (and the map will have conveniently sorted them for us)
            saved_node2index[id] = 0;
          }
        closedir(dh);

        // now go through all the node ids in sorted order and store indices
        int index = 0;
        for(std::map<int, int>::iterator it = saved_node2index.begin();
            it != saved_node2index.end();
            ++it)
          it->second = index++;
      } else {
	fprintf(stderr, "can't read directory '/sys/devices/system/node': %s\n", strerror(errno));
        // add a dummy entry so we're not empty any more
        saved_node2index[-1] = -1;
        // and now give up because we don't know what index 'node2' maps to
	return -1;
      }
    }
    int node2_index;
    {
      std::map<int, int>::const_iterator it = saved_node2index.find(node2);
      if(it == saved_node2index.end()) {
        // we don't know the index, so we can't find the distance
        return -1;
      }

      node2_index = it->second;
    }

    std::map<int, std::vector<int> >::iterator it = saved_distances.find(node1);
    if(it == saved_distances.end()) {
      // not one we've already looked up, so do it now

      // if we break out early, we'll end up with an empty vector, which means
      //  we'll return -1 for all future queries
      std::vector<int>& v = saved_distances[node1];

      char fname[256];
      sprintf(fname, "/sys/devices/system/node/node%d/distance", node1);
      FILE *f = fopen(fname, "r");
      if(!f) {
	fprintf(stderr, "can't read '%s': %s\n", fname, strerror(errno));
	saved_distances[node1].clear();
	return -1;
      }
      char line[256];
      if(fgets(line, 256, f)) {
	char *p = line;
	while(isdigit(*p)) {
	  errno = 0;
	  int d = strtol(p, &p, 10);
	  if(errno != 0) break;
	  v.push_back(d);
	  while(isspace(*p)) p++;
	}
      }
      fclose(f);
      if((node2_index >= 0) && (node2_index < (int)v.size()))
	return v[node2_index];
      else
	return -1;
    } else {
      const std::vector<int>& v = it->second;
      if((node2_index >= 0) && (node2_index < (int)v.size()))
	return v[node2_index];
      else
	return -1;
    }
#else
    return -1;
#endif
  }

  // allocate memory on a given NUMA node - pin if requested
  void *numasysif_alloc_mem(int node, size_t bytes, bool pin)
  {
#ifdef REALM_ON_LINUX
    // get memory from mmap
    // TODO: hugetlbfs, if possible
    void *base = mmap(0,
		      bytes,
		      PROT_READ | PROT_WRITE,
		      MAP_PRIVATE | MAP_ANONYMOUS,
		      -1,
		      0);
    if(!base) return 0;

    // use the bind call for the rest
    if(numasysif_bind_mem(node, base, bytes, pin))
      return base;

    // if not, clean up and return failure
    numasysif_free_mem(node, base, bytes);
    return 0;
#else
    return 0;
#endif
  }

  // free memory allocated on a given NUMA node
  bool numasysif_free_mem(int node, void *base, size_t bytes)
  {
#ifdef REALM_ON_LINUX
    int ret = munmap(base, bytes);
    return(ret == 0);
#else
    return false;
#endif
  }

  // bind already-allocated memory to a given node - pin if requested
  // may fail if the memory has already been touched
  bool numasysif_bind_mem(int node, void *base, size_t bytes, bool pin)
  {
#ifdef REALM_ON_LINUX
    int policy = MPOL_BIND;
    if((node < 0) || (node >= detected_node_count)) {
      fprintf(stderr, "bind request for node out of range: %d\n", node);
      return false;
    }
    unsigned char *nmask = (unsigned char *)alloca(detected_node_count >> 3);
    for(int i = 0; i < detected_node_count >> 3; i++)
      nmask[i] = 0;
    nmask[(node >> 3)] = (1 << (node & 7));
    int ret = mbind(base, bytes,
		    policy,
		    (const unsigned long *)nmask, detected_node_count,
		    MPOL_MF_STRICT | MPOL_MF_MOVE);
    if(ret != 0) {
      fprintf(stderr, "failed to bind memory for node %d: %s\n", node, strerror(errno));
      return false;
    }

    // attempt to pin the memory if requested
    if(pin) {
      int ret = mlock(base, bytes);
      if(ret != 0) {
	fprintf(stderr, "mlock failed for memory on node %d: %s\n", node, strerror(errno));
	return false;
      }
    }

    return true;
#else
    return false;
#endif
  }

};
<|endoftext|>