<commit_before>// Copyright (c) 2010 The Chromium 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 "views/controls/label.h"

#include <math.h>
#include <limits>

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "app/text_elider.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "gfx/canvas.h"
#include "gfx/color_utils.h"
#include "gfx/font.h"
#include "gfx/insets.h"
#include "views/background.h"

namespace views {

// static
const char Label::kViewClassName[] = "views/Label";
SkColor Label::kEnabledColor, Label::kDisabledColor;

static const int kFocusBorderPadding = 1;

Label::Label() {
  Init(std::wstring(), GetDefaultFont());
}

Label::Label(const std::wstring& text) {
  Init(text, GetDefaultFont());
}

Label::Label(const std::wstring& text, const gfx::Font& font) {
  Init(text, font);
}

Label::~Label() {
}

gfx::Size Label::GetPreferredSize() {
  // Return a size of (0, 0) if the label is not visible and if the
  // collapse_when_hidden_ flag is set.
  // TODO(munjal): This logic probably belongs to the View class. But for now,
  // put it here since putting it in View class means all inheriting classes
  // need ot respect the collapse_when_hidden_ flag.
  if (!IsVisible() && collapse_when_hidden_)
    return gfx::Size();

  gfx::Size prefsize(GetTextSize());
  gfx::Insets insets = GetInsets();
  prefsize.Enlarge(insets.width(), insets.height());
  return prefsize;
}

int Label::GetBaseline() {
  return GetInsets().top() + font_.baseline();
}

int Label::GetHeightForWidth(int w) {
  return is_multi_line_ ?
      (GetTextSize().height() + GetInsets().height()) :
      View::GetHeightForWidth(w);
}

void Label::DidChangeBounds(const gfx::Rect& previous,
                            const gfx::Rect& current) {
  text_size_valid_ &= !is_multi_line_;
}

void Label::Paint(gfx::Canvas* canvas) {
  PaintBackground(canvas);
  std::wstring paint_text;
  gfx::Rect text_bounds;
  int flags = 0;
  CalculateDrawStringParams(&paint_text, &text_bounds, &flags);
  canvas->DrawStringInt(paint_text, font_, color_,
                        text_bounds.x(), text_bounds.y(),
                        text_bounds.width(), text_bounds.height(), flags);

  if (HasFocus() || paint_as_focused_) {
    text_bounds.Inset(-kFocusBorderPadding, -kFocusBorderPadding);
    // If the label is a single line of text, then the computed text bound
    // corresponds directly to the text being drawn and no mirroring is needed
    // for the RTL case. For multiline text, the text bound is an estimation
    // and is recomputed in gfx::Canvas::SizeStringInt(). For multiline text
    // in RTL, we need to take mirroring into account when computing the focus
    // rectangle.
    if (flags & gfx::Canvas::MULTI_LINE)
      text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
    canvas->DrawFocusRect(text_bounds.x(), text_bounds.y(),
                          text_bounds.width(), text_bounds.height());
  }
}

void Label::PaintBackground(gfx::Canvas* canvas) {
  const Background* bg = contains_mouse_ ? GetMouseOverBackground() : NULL;
  if (!bg)
    bg = background();
  if (bg)
    bg->Paint(canvas, this);
}

void Label::SetFont(const gfx::Font& font) {
  font_ = font;
  text_size_valid_ = false;
  SchedulePaint();
}

void Label::SetText(const std::wstring& text) {
  text_ = text;
  url_set_ = false;
  text_size_valid_ = false;
  SchedulePaint();
}

const std::wstring Label::GetText() const {
  return url_set_ ? UTF8ToWide(url_.spec()) : text_;
}

void Label::SetURL(const GURL& url) {
  url_ = url;
  text_ = UTF8ToWide(url_.spec());
  url_set_ = true;
  text_size_valid_ = false;
  SchedulePaint();
}

const GURL Label::GetURL() const {
  return url_set_ ? url_ : GURL(WideToUTF8(text_));
}

void Label::SetHorizontalAlignment(Alignment a) {
  // If the View's UI layout is right-to-left and rtl_alignment_mode_ is
  // USE_UI_ALIGNMENT, we need to flip the alignment so that the alignment
  // settings take into account the text directionality.
  if (UILayoutIsRightToLeft() && (rtl_alignment_mode_ == USE_UI_ALIGNMENT) &&
      (a != ALIGN_CENTER))
    a = (a == ALIGN_LEFT) ? ALIGN_RIGHT : ALIGN_LEFT;
  if (horiz_alignment_ != a) {
    horiz_alignment_ = a;
    SchedulePaint();
  }
}

void Label::SetMultiLine(bool f) {
  if (f != is_multi_line_) {
    is_multi_line_ = f;
    text_size_valid_ = false;
    SchedulePaint();
  }
}

void Label::SetAllowCharacterBreak(bool f) {
  if (f != allow_character_break_) {
    allow_character_break_ = f;
    text_size_valid_ = false;
    SchedulePaint();
  }
}

void Label::SetTooltipText(const std::wstring& tooltip_text) {
  tooltip_text_ = tooltip_text;
}

bool Label::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) {
  DCHECK(tooltip);

  // If a tooltip has been explicitly set, use it.
  if (!tooltip_text_.empty()) {
    tooltip->assign(tooltip_text_);
    return true;
  }

  // Show the full text if the text does not fit.
  if (!is_multi_line_ && font_.GetStringWidth(text_) > width()) {
    *tooltip = text_;
    return true;
  }
  return false;
}

void Label::OnMouseMoved(const MouseEvent& e) {
  UpdateContainsMouse(e);
}

void Label::OnMouseEntered(const MouseEvent& event) {
  UpdateContainsMouse(event);
}

void Label::OnMouseExited(const MouseEvent& event) {
  SetContainsMouse(false);
}

void Label::SetMouseOverBackground(Background* background) {
  mouse_over_background_.reset(background);
}

const Background* Label::GetMouseOverBackground() const {
  return mouse_over_background_.get();
}

void Label::SetEnabled(bool enabled) {
  if (enabled == enabled_)
    return;
  View::SetEnabled(enabled);
  SetColor(enabled ? kEnabledColor : kDisabledColor);
}

gfx::Insets Label::GetInsets() const {
  gfx::Insets insets = View::GetInsets();
  if (IsFocusable() || has_focus_border_)  {
    insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding,
                          kFocusBorderPadding, kFocusBorderPadding);
  }
  return insets;
}

void Label::SizeToFit(int max_width) {
  DCHECK(is_multi_line_);

  std::vector<std::wstring> lines;
  SplitString(text_, L'\n', &lines);

  int label_width = 0;
  for (std::vector<std::wstring>::const_iterator iter = lines.begin();
       iter != lines.end(); ++iter)
    label_width = std::max(label_width, font_.GetStringWidth(*iter));

  label_width += GetInsets().width();

  if (max_width > 0)
    label_width = std::min(label_width, max_width);

  SetBounds(x(), y(), label_width, 0);
  SizeToPreferredSize();
}

bool Label::GetAccessibleRole(AccessibilityTypes::Role* role) {
  DCHECK(role);

  *role = AccessibilityTypes::ROLE_TEXT;
  return true;
}

bool Label::GetAccessibleName(std::wstring* name) {
  DCHECK(name);
  *name = GetText();
  return !name->empty();
}

bool Label::GetAccessibleState(AccessibilityTypes::State* state) {
  DCHECK(state);

  *state = AccessibilityTypes::STATE_READONLY;
  return true;
}

void Label::SetHasFocusBorder(bool has_focus_border) {
  has_focus_border_ = has_focus_border;
  text_size_valid_ &= !is_multi_line_;
}

// static
gfx::Font Label::GetDefaultFont() {
  return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
}

void Label::Init(const std::wstring& text, const gfx::Font& font) {
  static bool initialized = false;
  if (!initialized) {
#if defined(OS_WIN)
    kEnabledColor = color_utils::GetSysSkColor(COLOR_WINDOWTEXT);
    kDisabledColor = color_utils::GetSysSkColor(COLOR_GRAYTEXT);
#else
    // TODO(beng): source from theme provider.
    kEnabledColor = SK_ColorBLACK;
    kDisabledColor = SK_ColorGRAY;
#endif

    initialized = true;
  }

  contains_mouse_ = false;
  font_ = font;
  text_size_valid_ = false;
  SetText(text);
  url_set_ = false;
  color_ = kEnabledColor;
  horiz_alignment_ = ALIGN_CENTER;
  is_multi_line_ = false;
  allow_character_break_ = false;
  collapse_when_hidden_ = false;
  rtl_alignment_mode_ = USE_UI_ALIGNMENT;
  paint_as_focused_ = false;
  has_focus_border_ = false;
}

void Label::CalculateDrawStringParams(std::wstring* paint_text,
                                      gfx::Rect* text_bounds,
                                      int* flags) const {
  DCHECK(paint_text && text_bounds && flags);

  if (url_set_) {
    // TODO(jungshik) : Figure out how to get 'intl.accept_languages'
    // preference and use it when calling ElideUrl.
    *paint_text = gfx::ElideUrl(url_, font_, width(), std::wstring());

    // An URLs is always treated as an LTR text and therefore we should
    // explicitly mark it as such if the locale is RTL so that URLs containing
    // Hebrew or Arabic characters are displayed correctly.
    //
    // Note that we don't check the View's UI layout setting in order to
    // determine whether or not to insert the special Unicode formatting
    // characters. We use the locale settings because an URL is always treated
    // as an LTR string, even if its containing view does not use an RTL UI
    // layout.
    if (base::i18n::IsRTL())
      base::i18n::WrapStringWithLTRFormatting(paint_text);
  } else {
    *paint_text = text_;
  }

  *text_bounds = GetTextBounds();
  *flags = ComputeMultiLineFlags();
}

void Label::UpdateContainsMouse(const MouseEvent& event) {
  SetContainsMouse(GetTextBounds().Contains(event.x(), event.y()));
}

void Label::SetContainsMouse(bool contains_mouse) {
  if (contains_mouse_ == contains_mouse)
    return;
  contains_mouse_ = contains_mouse;
  if (GetMouseOverBackground())
    SchedulePaint();
}

gfx::Rect Label::GetTextBounds() const {
  gfx::Rect available_rect(GetAvailableRect());
  gfx::Size text_size(GetTextSize());
  text_size.set_width(std::min(available_rect.width(), text_size.width()));

  gfx::Insets insets = GetInsets();
  gfx::Point text_origin(insets.left(), insets.top());
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      break;
    case ALIGN_CENTER:
      // We put any extra margin pixel on the left rather than the right, since
      // GetTextExtentPoint32() can report a value one too large on the right.
      text_origin.Offset((available_rect.width() + 1 - text_size.width()) / 2,
                         0);
      break;
    case ALIGN_RIGHT:
      text_origin.set_x(available_rect.right() - text_size.width());
      break;
    default:
      NOTREACHED();
      break;
  }
  text_origin.Offset(0,
      std::max(0, (available_rect.height() - text_size.height())) / 2);
  return gfx::Rect(text_origin, text_size);
}

gfx::Size Label::GetTextSize() const {
  if (!text_size_valid_) {
    int w = is_multi_line_ ?
        GetAvailableRect().width() : std::numeric_limits<int>::max();
    int h = font_.height();
    gfx::Canvas::SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags());
    text_size_.SetSize(w, h);
    text_size_valid_ = true;
  }

  return text_size_;
}

int Label::ComputeMultiLineFlags() const {
  if (!is_multi_line_)
    return 0;

  int flags = gfx::Canvas::MULTI_LINE;
#if !defined(OS_WIN)
    // Don't ellide multiline labels on Linux.
    // Todo(davemoore): Do we depend on elliding multiline text?
    // Pango insists on limiting the number of lines to one if text is
    // ellided. You can get around this if you can pass a maximum height
    // but we don't currently have that data when we call the pango code.
    flags |= gfx::Canvas::NO_ELLIPSIS;
#endif
  if (allow_character_break_)
    flags |= gfx::Canvas::CHARACTER_BREAK;
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
      break;
    case ALIGN_CENTER:
      flags |= gfx::Canvas::TEXT_ALIGN_CENTER;
      break;
    case ALIGN_RIGHT:
      flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
      break;
  }
  return flags;
}

gfx::Rect Label::GetAvailableRect() const {
  gfx::Rect bounds(gfx::Point(), size());
  gfx::Insets insets(GetInsets());
  bounds.Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
  return bounds;
}

}  // namespace views
<commit_msg>Fix regression from Label fixes -- I forgot to make GetHeightForWidth() actually care about the provided width for multi-line text.  Oops.<commit_after>// Copyright (c) 2010 The Chromium 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 "views/controls/label.h"

#include <cmath>

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "app/text_elider.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "gfx/canvas.h"
#include "gfx/color_utils.h"
#include "gfx/font.h"
#include "gfx/insets.h"
#include "views/background.h"

namespace views {

// static
const char Label::kViewClassName[] = "views/Label";
SkColor Label::kEnabledColor, Label::kDisabledColor;

static const int kFocusBorderPadding = 1;

Label::Label() {
  Init(std::wstring(), GetDefaultFont());
}

Label::Label(const std::wstring& text) {
  Init(text, GetDefaultFont());
}

Label::Label(const std::wstring& text, const gfx::Font& font) {
  Init(text, font);
}

Label::~Label() {
}

gfx::Size Label::GetPreferredSize() {
  // Return a size of (0, 0) if the label is not visible and if the
  // collapse_when_hidden_ flag is set.
  // TODO(munjal): This logic probably belongs to the View class. But for now,
  // put it here since putting it in View class means all inheriting classes
  // need ot respect the collapse_when_hidden_ flag.
  if (!IsVisible() && collapse_when_hidden_)
    return gfx::Size();

  gfx::Size prefsize(GetTextSize());
  gfx::Insets insets = GetInsets();
  prefsize.Enlarge(insets.width(), insets.height());
  return prefsize;
}

int Label::GetBaseline() {
  return GetInsets().top() + font_.baseline();
}

int Label::GetHeightForWidth(int w) {
  if (!is_multi_line_)
    return View::GetHeightForWidth(w);

  w = std::max(0, w - GetInsets().width());
  int h = font_.height();
  gfx::Canvas::SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags());
  return h + GetInsets().height();
}

void Label::DidChangeBounds(const gfx::Rect& previous,
                            const gfx::Rect& current) {
  text_size_valid_ &= !is_multi_line_;
}

void Label::Paint(gfx::Canvas* canvas) {
  PaintBackground(canvas);
  std::wstring paint_text;
  gfx::Rect text_bounds;
  int flags = 0;
  CalculateDrawStringParams(&paint_text, &text_bounds, &flags);
  canvas->DrawStringInt(paint_text, font_, color_,
                        text_bounds.x(), text_bounds.y(),
                        text_bounds.width(), text_bounds.height(), flags);

  if (HasFocus() || paint_as_focused_) {
    text_bounds.Inset(-kFocusBorderPadding, -kFocusBorderPadding);
    // If the label is a single line of text, then the computed text bound
    // corresponds directly to the text being drawn and no mirroring is needed
    // for the RTL case. For multiline text, the text bound is an estimation
    // and is recomputed in gfx::Canvas::SizeStringInt(). For multiline text
    // in RTL, we need to take mirroring into account when computing the focus
    // rectangle.
    if (flags & gfx::Canvas::MULTI_LINE)
      text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
    canvas->DrawFocusRect(text_bounds.x(), text_bounds.y(),
                          text_bounds.width(), text_bounds.height());
  }
}

void Label::PaintBackground(gfx::Canvas* canvas) {
  const Background* bg = contains_mouse_ ? GetMouseOverBackground() : NULL;
  if (!bg)
    bg = background();
  if (bg)
    bg->Paint(canvas, this);
}

void Label::SetFont(const gfx::Font& font) {
  font_ = font;
  text_size_valid_ = false;
  SchedulePaint();
}

void Label::SetText(const std::wstring& text) {
  text_ = text;
  url_set_ = false;
  text_size_valid_ = false;
  SchedulePaint();
}

const std::wstring Label::GetText() const {
  return url_set_ ? UTF8ToWide(url_.spec()) : text_;
}

void Label::SetURL(const GURL& url) {
  url_ = url;
  text_ = UTF8ToWide(url_.spec());
  url_set_ = true;
  text_size_valid_ = false;
  SchedulePaint();
}

const GURL Label::GetURL() const {
  return url_set_ ? url_ : GURL(WideToUTF8(text_));
}

void Label::SetHorizontalAlignment(Alignment a) {
  // If the View's UI layout is right-to-left and rtl_alignment_mode_ is
  // USE_UI_ALIGNMENT, we need to flip the alignment so that the alignment
  // settings take into account the text directionality.
  if (UILayoutIsRightToLeft() && (rtl_alignment_mode_ == USE_UI_ALIGNMENT) &&
      (a != ALIGN_CENTER))
    a = (a == ALIGN_LEFT) ? ALIGN_RIGHT : ALIGN_LEFT;
  if (horiz_alignment_ != a) {
    horiz_alignment_ = a;
    SchedulePaint();
  }
}

void Label::SetMultiLine(bool f) {
  if (f != is_multi_line_) {
    is_multi_line_ = f;
    text_size_valid_ = false;
    SchedulePaint();
  }
}

void Label::SetAllowCharacterBreak(bool f) {
  if (f != allow_character_break_) {
    allow_character_break_ = f;
    text_size_valid_ = false;
    SchedulePaint();
  }
}

void Label::SetTooltipText(const std::wstring& tooltip_text) {
  tooltip_text_ = tooltip_text;
}

bool Label::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) {
  DCHECK(tooltip);

  // If a tooltip has been explicitly set, use it.
  if (!tooltip_text_.empty()) {
    tooltip->assign(tooltip_text_);
    return true;
  }

  // Show the full text if the text does not fit.
  if (!is_multi_line_ && font_.GetStringWidth(text_) > width()) {
    *tooltip = text_;
    return true;
  }
  return false;
}

void Label::OnMouseMoved(const MouseEvent& e) {
  UpdateContainsMouse(e);
}

void Label::OnMouseEntered(const MouseEvent& event) {
  UpdateContainsMouse(event);
}

void Label::OnMouseExited(const MouseEvent& event) {
  SetContainsMouse(false);
}

void Label::SetMouseOverBackground(Background* background) {
  mouse_over_background_.reset(background);
}

const Background* Label::GetMouseOverBackground() const {
  return mouse_over_background_.get();
}

void Label::SetEnabled(bool enabled) {
  if (enabled == enabled_)
    return;
  View::SetEnabled(enabled);
  SetColor(enabled ? kEnabledColor : kDisabledColor);
}

gfx::Insets Label::GetInsets() const {
  gfx::Insets insets = View::GetInsets();
  if (IsFocusable() || has_focus_border_)  {
    insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding,
                          kFocusBorderPadding, kFocusBorderPadding);
  }
  return insets;
}

void Label::SizeToFit(int max_width) {
  DCHECK(is_multi_line_);

  std::vector<std::wstring> lines;
  SplitString(text_, L'\n', &lines);

  int label_width = 0;
  for (std::vector<std::wstring>::const_iterator iter = lines.begin();
       iter != lines.end(); ++iter)
    label_width = std::max(label_width, font_.GetStringWidth(*iter));

  label_width += GetInsets().width();

  if (max_width > 0)
    label_width = std::min(label_width, max_width);

  SetBounds(x(), y(), label_width, 0);
  SizeToPreferredSize();
}

bool Label::GetAccessibleRole(AccessibilityTypes::Role* role) {
  DCHECK(role);

  *role = AccessibilityTypes::ROLE_TEXT;
  return true;
}

bool Label::GetAccessibleName(std::wstring* name) {
  DCHECK(name);
  *name = GetText();
  return !name->empty();
}

bool Label::GetAccessibleState(AccessibilityTypes::State* state) {
  DCHECK(state);

  *state = AccessibilityTypes::STATE_READONLY;
  return true;
}

void Label::SetHasFocusBorder(bool has_focus_border) {
  has_focus_border_ = has_focus_border;
  text_size_valid_ &= !is_multi_line_;
}

// static
gfx::Font Label::GetDefaultFont() {
  return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
}

void Label::Init(const std::wstring& text, const gfx::Font& font) {
  static bool initialized = false;
  if (!initialized) {
#if defined(OS_WIN)
    kEnabledColor = color_utils::GetSysSkColor(COLOR_WINDOWTEXT);
    kDisabledColor = color_utils::GetSysSkColor(COLOR_GRAYTEXT);
#else
    // TODO(beng): source from theme provider.
    kEnabledColor = SK_ColorBLACK;
    kDisabledColor = SK_ColorGRAY;
#endif

    initialized = true;
  }

  contains_mouse_ = false;
  font_ = font;
  text_size_valid_ = false;
  SetText(text);
  url_set_ = false;
  color_ = kEnabledColor;
  horiz_alignment_ = ALIGN_CENTER;
  is_multi_line_ = false;
  allow_character_break_ = false;
  collapse_when_hidden_ = false;
  rtl_alignment_mode_ = USE_UI_ALIGNMENT;
  paint_as_focused_ = false;
  has_focus_border_ = false;
}

void Label::CalculateDrawStringParams(std::wstring* paint_text,
                                      gfx::Rect* text_bounds,
                                      int* flags) const {
  DCHECK(paint_text && text_bounds && flags);

  if (url_set_) {
    // TODO(jungshik) : Figure out how to get 'intl.accept_languages'
    // preference and use it when calling ElideUrl.
    *paint_text = gfx::ElideUrl(url_, font_, width(), std::wstring());

    // An URLs is always treated as an LTR text and therefore we should
    // explicitly mark it as such if the locale is RTL so that URLs containing
    // Hebrew or Arabic characters are displayed correctly.
    //
    // Note that we don't check the View's UI layout setting in order to
    // determine whether or not to insert the special Unicode formatting
    // characters. We use the locale settings because an URL is always treated
    // as an LTR string, even if its containing view does not use an RTL UI
    // layout.
    if (base::i18n::IsRTL())
      base::i18n::WrapStringWithLTRFormatting(paint_text);
  } else {
    *paint_text = text_;
  }

  *text_bounds = GetTextBounds();
  *flags = ComputeMultiLineFlags();
}

void Label::UpdateContainsMouse(const MouseEvent& event) {
  SetContainsMouse(GetTextBounds().Contains(event.x(), event.y()));
}

void Label::SetContainsMouse(bool contains_mouse) {
  if (contains_mouse_ == contains_mouse)
    return;
  contains_mouse_ = contains_mouse;
  if (GetMouseOverBackground())
    SchedulePaint();
}

gfx::Rect Label::GetTextBounds() const {
  gfx::Rect available_rect(GetAvailableRect());
  gfx::Size text_size(GetTextSize());
  text_size.set_width(std::min(available_rect.width(), text_size.width()));

  gfx::Insets insets = GetInsets();
  gfx::Point text_origin(insets.left(), insets.top());
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      break;
    case ALIGN_CENTER:
      // We put any extra margin pixel on the left rather than the right, since
      // GetTextExtentPoint32() can report a value one too large on the right.
      text_origin.Offset((available_rect.width() + 1 - text_size.width()) / 2,
                         0);
      break;
    case ALIGN_RIGHT:
      text_origin.set_x(available_rect.right() - text_size.width());
      break;
    default:
      NOTREACHED();
      break;
  }
  text_origin.Offset(0,
      std::max(0, (available_rect.height() - text_size.height())) / 2);
  return gfx::Rect(text_origin, text_size);
}

gfx::Size Label::GetTextSize() const {
  if (!text_size_valid_) {
    int w = GetAvailableRect().width();
    int h = font_.height();
    // For single-line strings, ignore the available width and calculate how
    // wide the text wants to be.
    int flags = ComputeMultiLineFlags();
    if (!is_multi_line_)
      flags |= gfx::Canvas::NO_ELLIPSIS;
    gfx::Canvas::SizeStringInt(text_, font_, &w, &h, flags);
    text_size_.SetSize(w, h);
    text_size_valid_ = true;
  }

  return text_size_;
}

int Label::ComputeMultiLineFlags() const {
  if (!is_multi_line_)
    return 0;

  int flags = gfx::Canvas::MULTI_LINE;
#if !defined(OS_WIN)
    // Don't elide multiline labels on Linux.
    // Todo(davemoore): Do we depend on eliding multiline text?
    // Pango insists on limiting the number of lines to one if text is
    // elided. You can get around this if you can pass a maximum height
    // but we don't currently have that data when we call the pango code.
    flags |= gfx::Canvas::NO_ELLIPSIS;
#endif
  if (allow_character_break_)
    flags |= gfx::Canvas::CHARACTER_BREAK;
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
      break;
    case ALIGN_CENTER:
      flags |= gfx::Canvas::TEXT_ALIGN_CENTER;
      break;
    case ALIGN_RIGHT:
      flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
      break;
  }
  return flags;
}

gfx::Rect Label::GetAvailableRect() const {
  gfx::Rect bounds(gfx::Point(), size());
  gfx::Insets insets(GetInsets());
  bounds.Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
  return bounds;
}

}  // namespace views
<|endoftext|>


<commit_before>#include "player.h"
#include <cmath>

const double PI = 3.14159265358979323846264338329750288419716939937510582;

Player::Player() : Speed(0), Moving(false), Name("Untitled"), StandingOn(NULL), GravitySpeed(0.f), SurfaceZ(0.f), Entity(Vector3(0,0,0), Vector3(0,0,0), Vector3(0,0,0)) {};

Player::Player(float initspeed, Vector3 initrot, Vector3 initpos, std::string Name) : Speed(initspeed), Moving(false), Name(Name), StandingOn(NULL), GravitySpeed(0.f), SurfaceZ(0.f), Entity(initpos, initrot, Vector3(1, 2, 1)) {};

void Player::Forward(float amount)
{
    Moving = true;
    float xStep = Speed * sin((PI * Rotation.Z) / 180) * amount;
    float yStep = Speed * cos((PI * Rotation.Z) / 180) * amount;
    //float zStep = -Speed * sin((PI * Rotation.Y) / 180) * amount;
    Pos.X += (xStep);// * cos((PI * Rotation.Y) / 180));
    Pos.Y += (yStep);// * cos((PI * Rotation.Y) / 180));
    //Z += zStep;
}

void Player::Strafe(float amount)
{
    float xStep = Speed * sin((PI * Rotation.Y) / 180) * amount;
    float yStep = Speed * cos((PI * Rotation.Y) / 180) * amount;
    if(Moving == true)
    {
        xStep *= 0.707106;
        yStep *= 0.707106;
    }

    Pos.X -= xStep;
    Pos.Y += yStep;
}

void Player::ChangeRotation(float deltaYRotation, float deltaZRotation)
{
    Rotation.Y += deltaYRotation;
    Rotation.Z += deltaZRotation;

    if(Rotation.Z >= 360)
        Rotation.Z -= 360;
    else if (Rotation.Z < 0)
        Rotation.Z += 360;

    if(Rotation.Y < -90)
        Rotation.Y = -90;
    else if(Rotation.Y >= 90)
        Rotation.Y = 90;
}

void Player::Jump()
{
    if (StandingOn) {
        GravitySpeed = 5.f;
        StandingOn = NULL;
    }
}

void Player::DoStep(float amount)
{
    Moving = false;

    if (!StandingOn)
    {
        GravitySpeed -= 9.8f * amount;
        if (Pos.Z < 0) GravitySpeed = -GravitySpeed;
        Pos.Z += GravitySpeed * amount;
    }
    else
    {
        GravitySpeed = 0.f;
        Pos.Z = SurfaceZ + 2.f;
    }
}

void Player::Render()
{
    //TODO: Write me
}

<commit_msg>X += yStep does not make sense.<commit_after>#include "player.h"
#include <cmath>

const double PI = 3.14159265358979323846264338329750288419716939937510582;

Player::Player() : Speed(0), Moving(false), Name("Untitled"), StandingOn(NULL), GravitySpeed(0.f), SurfaceZ(0.f), Entity(Vector3(0,0,0), Vector3(0,0,0), Vector3(0,0,0)) {};

Player::Player(float initspeed, Vector3 initrot, Vector3 initpos, std::string Name) : Speed(initspeed), Moving(false), Name(Name), StandingOn(NULL), GravitySpeed(0.f), SurfaceZ(0.f), Entity(initpos, initrot, Vector3(1, 2, 1)) {};

void Player::Forward(float amount)
{
    Moving = true;
    float xStep = Speed * sin((PI * Rotation.Z) / 180) * amount;
    float yStep = Speed * cos((PI * Rotation.Z) / 180) * amount;
    //float zStep = -Speed * sin((PI * Rotation.Y) / 180) * amount;
    Pos.X += (xStep);// * cos((PI * Rotation.Y) / 180));
    Pos.Y += (yStep);// * cos((PI * Rotation.Y) / 180));
    //Z += zStep;
}

void Player::Strafe(float amount)
{
    float yStep = Speed * sin((PI * Rotation.Z) / 180) * amount;
    float xStep = Speed * cos((PI * Rotation.Z) / 180) * amount;
    if(Moving == true)
    {
        xStep *= 0.707106;
        yStep *= 0.707106;
    }

    Pos.X -= xStep;
    Pos.Y += yStep;
}

void Player::ChangeRotation(float deltaYRotation, float deltaZRotation)
{
    Rotation.Y += deltaYRotation;
    Rotation.Z += deltaZRotation;

    if(Rotation.Z >= 360)
        Rotation.Z -= 360;
    else if (Rotation.Z < 0)
        Rotation.Z += 360;

    if(Rotation.Y < -90)
        Rotation.Y = -90;
    else if(Rotation.Y >= 90)
        Rotation.Y = 90;
}

void Player::Jump()
{
    if (StandingOn) {
        GravitySpeed = 5.f;
        StandingOn = NULL;
    }
}

void Player::DoStep(float amount)
{
    Moving = false;

    if (!StandingOn)
    {
        GravitySpeed -= 9.8f * amount;
        if (Pos.Z < 0) GravitySpeed = -GravitySpeed;
        Pos.Z += GravitySpeed * amount;
    }
    else
    {
        GravitySpeed = 0.f;
        Pos.Z = SurfaceZ + 2.f;
    }
}

void Player::Render()
{
    //TODO: Write me
}

<|endoftext|>


<commit_before>// SA:MP Profiler plugin
//
// Copyright (c) 2011 Zeex
//
// 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.

#include <algorithm>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iterator>
#include <list>
#include <map>
#include <string>

#include "amxname.h"
#include "debuginfo.h"
#include "jump.h"
#include "logprintf.h"
#include "plugin.h"
#include "profiler.h"
#include "version.h"

#include "amx/amx.h"

extern void *pAMXFunctions;

// Symbolic info, used for getting function names
static std::map<AMX*, DebugInfo> debugInfos;

// Both x86 and x86-64 are Little Endian...
static void *AMXAPI DummyAmxAlign(void *v) { return v; }

static uint32_t amx_Exec_addr;
static unsigned char amx_Exec_code[5];

static int AMXAPI Exec(AMX *amx, cell *retval, int index) {
	memcpy(reinterpret_cast<void*>(::amx_Exec_addr), ::amx_Exec_code, 5);

	int error = AMX_ERR_NONE;

	// Check if this script has a profiler attached to it
	Profiler *prof = Profiler::Get(amx);
	if (prof != 0) {
		error =  prof->Exec(retval, index);
	} else {
		error = amx_Exec(amx, retval, index);
	}

	SetJump(reinterpret_cast<void*>(::amx_Exec_addr), (void*)::Exec, ::amx_Exec_code);

	return error;
}

// Returns true if the .amx should be profiled
static bool WantsProfiler(const std::string &amxName) {
	std::ifstream config("plugins/profiler.cfg");

	std::istream_iterator<std::string> begin(config);
	std::istream_iterator<std::string> end;

	if (std::find(begin, end, amxName) != end) {
		return true;
	}

	return false;
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
	return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
	pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
	logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

	// The server does not export amx_Align* for some reason.
	// They are used in amxdbg.c and amxaux.c, so they must be callable.
	((void**)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align16] = (void*)DummyAmxAlign; // amx_Align16
	((void**)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align32] = (void*)DummyAmxAlign; // amx_Align32
	((void**)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align64] = (void*)DummyAmxAlign; // amx_Align64

	// Hook amx_Exec
	::amx_Exec_addr = reinterpret_cast<uint32_t>(((void**)pAMXFunctions)[PLUGIN_AMX_EXPORT_Exec]);
	SetJump(reinterpret_cast<void*>(::amx_Exec_addr), (void*)::Exec, ::amx_Exec_code);

	logprintf("  Profiler plugin v" VERSION " is OK.");

	return true;
}

PLUGIN_EXPORT void PLUGIN_CALL Unload() {
	return;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
	std::string filename = GetAmxName(amx);
	if (filename.empty()) {
		logprintf("Profiler: Failed to detect .amx name");
		return AMX_ERR_NONE;
	}

	if (!Profiler::IsScriptProfilable(amx)) {
		logprintf("Profiler: %s can't be profiled (do you use -d0?)", filename.c_str());
		return AMX_ERR_NONE;
	}

	std::replace(filename.begin(), filename.end(), '\\', '/');
	if (WantsProfiler(filename)) {
		logprintf("Profiler: Will profile %s", filename.c_str());
		if (DebugInfo::HasDebugInfo(amx)) {
			DebugInfo debugInfo;
			debugInfo.Load(filename);
			if (debugInfo.IsLoaded()) {
				logprintf("Profiler: Loaded debug info from %s", filename.c_str());
				::debugInfos[amx] = debugInfo;
				Profiler::Attach(amx, debugInfo);
				return AMX_ERR_NONE;
			} else {
				logprintf("Profiler: Error loading debug info from %s", filename.c_str());
			}
		}
		Profiler::Attach(amx);
	}

	return AMX_ERR_NONE;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
	// Get an instance of Profiler attached to the unloading AMX
	Profiler *prof = Profiler::Get(amx);

	// Detach profiler
	if (prof != 0) {
		// Before doing that, print stats to <amx_file_path>.prof
		std::string name = GetAmxName(amx);
		if (!name.empty()) {
			std::string outfile = name + std::string(".prof");
			logprintf("Profiler: Writing profile to %s", outfile.c_str());
			prof->PrintStats(outfile);
		}
		Profiler::Detach(amx);
	}

	// Free debug info
	std::map<AMX*, DebugInfo>::iterator it = ::debugInfos.find(amx);
	if (it != ::debugInfos.end()) {
		it->second.Free();
		::debugInfos.erase(it);
	}

	return AMX_ERR_NONE;
}
<commit_msg>Add ToPortablePath()<commit_after>// SA:MP Profiler plugin
//
// Copyright (c) 2011 Zeex
//
// 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.

#include <algorithm>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iterator>
#include <list>
#include <map>
#include <string>

#include "amxname.h"
#include "debuginfo.h"
#include "jump.h"
#include "logprintf.h"
#include "plugin.h"
#include "profiler.h"
#include "version.h"

#include "amx/amx.h"

extern void *pAMXFunctions;

// Symbolic info, used for getting function names
static std::map<AMX*, DebugInfo> debugInfos;

// Both x86 and x86-64 are Little Endian...
static void *AMXAPI DummyAmxAlign(void *v) { return v; }

static uint32_t amx_Exec_addr;
static unsigned char amx_Exec_code[5];

static int AMXAPI Exec(AMX *amx, cell *retval, int index) {
	memcpy(reinterpret_cast<void*>(::amx_Exec_addr), ::amx_Exec_code, 5);

	int error = AMX_ERR_NONE;

	// Check if this script has a profiler attached to it
	Profiler *prof = Profiler::Get(amx);
	if (prof != 0) {
		error =  prof->Exec(retval, index);
	} else {
		error = amx_Exec(amx, retval, index);
	}

	SetJump(reinterpret_cast<void*>(::amx_Exec_addr), (void*)::Exec, ::amx_Exec_code);

	return error;
}

// Replaces back slashes with forward slashes
static std::string ToPortablePath(const std::string &path) {
	std::string fsPath = path;
	std::replace(fsPath.begin(), fsPath.end(), '\\', '/');
	return fsPath;
}

// Returns true if the .amx should be profiled
static bool WantsProfiler(const std::string &amxName) {
	std::ifstream config("plugins/profiler.cfg");

	std::istream_iterator<std::string> begin(config);
	std::istream_iterator<std::string> end;

	if (std::find(begin, end, ToPortablePath(amxName)) != end) {
		return true;
	}

	return false;
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
	return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
	pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
	logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

	// The server does not export amx_Align* for some reason.
	// They are used in amxdbg.c and amxaux.c, so they must be callable.
	((void**)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align16] = (void*)DummyAmxAlign; // amx_Align16
	((void**)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align32] = (void*)DummyAmxAlign; // amx_Align32
	((void**)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align64] = (void*)DummyAmxAlign; // amx_Align64

	// Hook amx_Exec
	::amx_Exec_addr = reinterpret_cast<uint32_t>(((void**)pAMXFunctions)[PLUGIN_AMX_EXPORT_Exec]);
	SetJump(reinterpret_cast<void*>(::amx_Exec_addr), (void*)::Exec, ::amx_Exec_code);

	logprintf("  Profiler plugin v" VERSION " is OK.");

	return true;
}

PLUGIN_EXPORT void PLUGIN_CALL Unload() {
	return;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
	std::string filename = GetAmxName(amx);
	if (filename.empty()) {
		logprintf("Profiler: Failed to detect .amx name");
		return AMX_ERR_NONE;
	}

	if (!Profiler::IsScriptProfilable(amx)) {
		logprintf("Profiler: %s can't be profiled (do you use -d0?)", filename.c_str());
		return AMX_ERR_NONE;
	}

	if (WantsProfiler(filename)) {
		logprintf("Profiler: Will profile %s", filename.c_str());
		if (DebugInfo::HasDebugInfo(amx)) {
			DebugInfo debugInfo;
			debugInfo.Load(filename);
			if (debugInfo.IsLoaded()) {
				logprintf("Profiler: Loaded debug info from %s", filename.c_str());
				::debugInfos[amx] = debugInfo;
				Profiler::Attach(amx, debugInfo);
				return AMX_ERR_NONE;
			} else {
				logprintf("Profiler: Error loading debug info from %s", filename.c_str());
			}
		}
		Profiler::Attach(amx);
	}

	return AMX_ERR_NONE;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
	// Get an instance of Profiler attached to the unloading AMX
	Profiler *prof = Profiler::Get(amx);

	// Detach profiler
	if (prof != 0) {
		// Before doing that, print stats to <amx_file_path>.prof
		std::string name = GetAmxName(amx);
		if (!name.empty()) {
			std::string outfile = name + std::string(".prof");
			logprintf("Profiler: Writing profile to %s", outfile.c_str());
			prof->PrintStats(outfile);
		}
		Profiler::Detach(amx);
	}

	// Free debug info
	std::map<AMX*, DebugInfo>::iterator it = ::debugInfos.find(amx);
	if (it != ::debugInfos.end()) {
		it->second.Free();
		::debugInfos.erase(it);
	}

	return AMX_ERR_NONE;
}
<|endoftext|>


<commit_before>/*
 * SessionDependencies.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionDependencies.hpp"

#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/join.hpp>

#include <core/Error.hpp>
#include <core/Exec.hpp>
#include <core/system/Environment.hpp>

#include <core/json/JsonRpc.hpp>

#include <r/RExec.hpp>
#include <r/session/RSessionUtils.hpp>

#include <session/SessionModuleContext.hpp>
#include <session/SessionConsoleProcess.hpp>
#include <session/projects/SessionProjects.hpp>

using namespace core;

namespace session {

namespace {

struct EmbeddedPackage
{
   bool empty() const { return archivePath.empty(); }

   std::string name;
   std::string version;
   std::string sha1;
   std::string archivePath;
};

EmbeddedPackage embeddedPackageInfo(const std::string& name)
{
   // determine location of archives
   FilePath archivesDir = session::options().sessionPackageArchivesPath();
   std::vector<FilePath> children;
   Error error = archivesDir.children(&children);
   if (error)
   {
      LOG_ERROR(error);
      return EmbeddedPackage();
   }

   // we saw the regex with explicit character class ranges fail to match
   // on a windows 8.1 system so we are falling back to a simpler regex
   //
   // (note see below for another approach involving setting the locale
   // of the regex directly -- this assumes that the matching issue is
   // somehow related to locales)
   boost::regex re(name + "_([^_]+)_([^\\.]+)\\.tar\\.gz");

   /* another approach (which we didn't try) based on setting the regex locale
   boost::regex re;
   re.imbue(std::locale("en_US.UTF-8"));
   re.assign(name + "_([0-9]+\\.[0-9]+\\.[0-9]+)_([\\d\\w]+)\\.tar\\.gz");
   */

   BOOST_FOREACH(const FilePath& child, children)
   {
      boost::smatch match;
      if (boost::regex_match(child.filename(), match, re))
      {
         EmbeddedPackage pkg;
         pkg.name = name;
         pkg.version = match[1];
         pkg.sha1 = match[2];
         pkg.archivePath = string_utils::utf8ToSystem(child.absolutePath());
         return pkg;
      }
   }

   // none found
   return EmbeddedPackage();
}

} // anonymous namespace

namespace modules {
namespace dependencies {

namespace {

const int kCRANPackageDependency = 0;
const int kEmbeddedPackageDependency = 1;

struct Dependency
{
   Dependency() : type(0) {}

   bool empty() const { return name.empty(); }

   int type;
   std::string name;
   std::string version;
};

std::string nameFromDep(const Dependency& dep)
{
   return dep.name;
}

std::vector<std::string> packageNames(const std::vector<Dependency> deps)
{
   std::vector<std::string> names;
   std::transform(deps.begin(),
                  deps.end(),
                  std::back_inserter(names),
                  nameFromDep);
   return names;
}

std::vector<Dependency> dependenciesFromJson(const json::Array& depsJson)
{
   std::vector<Dependency> deps;
   BOOST_FOREACH(const json::Value& depJsonValue, depsJson)
   {
      if (json::isType<json::Object>(depJsonValue))
      {
         Dependency dep;
         json::Object depJson = depJsonValue.get_obj();
         Error error = json::readObject(depJson,
                                        "type", &(dep.type),
                                        "name", &(dep.name),
                                        "version", &(dep.version));
         if (!error)
         {
            deps.push_back(dep);
         }
         else
         {
            LOG_ERROR(error);
         }
      }
   }
   return deps;
}

json::Array dependenciesToJson(const std::vector<Dependency>& deps)
{
   json::Array depsJson;
   BOOST_FOREACH(const Dependency& dep, deps)
   {
      json::Object depJson;
      depJson["type"] = dep.type;
      depJson["name"] = dep.name;
      depJson["version"] = dep.version;
      depsJson.push_back(depJson);
   }
   return depsJson;
}

bool embeddedPackageRequiresUpdate(const EmbeddedPackage& pkg)
{
   // if this package came from the rstudio ide then check if it needs
   // an update (i.e. has a different SHA1)
   r::exec::RFunction func(".rs.rstudioIDEPackageRequiresUpdate",
                           pkg.name, pkg.sha1);
   bool requiresUpdate = false;
   Error error = func.call(&requiresUpdate);
   if (error)
      LOG_ERROR(error);

   return requiresUpdate;
}

void silentUpdateEmbeddedPackage(const EmbeddedPackage& pkg)
{
   // suppress output which occurs during silent update
   r::session::utils::SuppressOutputInScope suppressOutput;

   Error error = r::exec::RFunction(".rs.updateRStudioIDEPackage",
                             pkg.name, pkg.archivePath).call();
   if (error)
      LOG_ERROR(error);
}

Error unsatisfiedDependencies(const json::JsonRpcRequest& request,
                              json::JsonRpcResponse* pResponse)
{
   // get list of dependencies and silentUpdate flag
   json::Array depsJson;
   bool silentUpdate;
   Error error = json::readParams(request.params, &depsJson, &silentUpdate);
   if (error)
      return error;
   std::vector<Dependency> deps = dependenciesFromJson(depsJson);

   // build the list of unsatisifed dependencies
   using namespace module_context;
   std::vector<Dependency> unsatisfiedDeps;
   BOOST_FOREACH(const Dependency& dep, deps)
   {
      switch(dep.type)
      {
      case kCRANPackageDependency:
         if (!isPackageVersionInstalled(dep.name, dep.version))
         {
            unsatisfiedDeps.push_back(dep);
         }
         break;

      case kEmbeddedPackageDependency:
         EmbeddedPackage pkg = embeddedPackageInfo(dep.name);

         // package isn't installed so report that it reqires installation
         if (!isPackageInstalled(dep.name))
         {
            unsatisfiedDeps.push_back(dep);
         }
         // package installed was from IDE but is out of date -- silent update
         else if (silentUpdate && embeddedPackageRequiresUpdate(pkg))
         {
            silentUpdateEmbeddedPackage(pkg);
         }
         // package installed wasn't from the IDE but is older than
         // the version we currently have embedded -- silent update
         else if (silentUpdate && !isPackageVersionInstalled(pkg.name, pkg.version))
         {
            silentUpdateEmbeddedPackage(pkg);
         }
         else
         {
            // the only remaining case is a newer version of the package is
            // already installed (e.g. directly from github). in this case
            // we do nothing
         }

         break;
      }
   }

   // return unsatisfied dependencies
   pResponse->setResult(dependenciesToJson(unsatisfiedDeps));
   return Success();
}

Error installDependencies(const json::JsonRpcRequest& request,
                          json::JsonRpcResponse* pResponse)
{
   // get list of dependencies
   json::Array depsJson;
   Error error = json::readParams(request.params, &depsJson);
   if (error)
      return error;
   std::vector<Dependency> deps = dependenciesFromJson(depsJson);

   // Ensure we have a writeable user library
   error = r::exec::RFunction(".rs.ensureWriteableUserLibrary").call();
   if (error)
      return error;

   // force unload as necessary
   std::vector<std::string> names = packageNames(deps);
   error = r::exec::RFunction(".rs.forceUnloadForPackageInstall", names).call();
   if (error)
      LOG_ERROR(error);

   // R binary
   FilePath rProgramPath;
   error = module_context::rScriptPath(&rProgramPath);
   if (error)
      return error;

   // options
   core::system::ProcessOptions options;
   options.terminateChildren = true;
   options.redirectStdErrToStdOut = true;

   // build lists of cran packages and archives
   std::vector<std::string> cranPackages;
   std::vector<std::string> embeddedPackages;
   BOOST_FOREACH(const Dependency& dep, deps)
   {
      switch(dep.type)
      {
      case kCRANPackageDependency:
         cranPackages.push_back("'" + dep.name + "'");
         break;

      case kEmbeddedPackageDependency:
         EmbeddedPackage pkg = embeddedPackageInfo(dep.name);
         if (!pkg.empty())
            embeddedPackages.push_back(pkg.archivePath);
         break;
      }
   }

   // build install command
   std::string cmd = "{";
   if (!cranPackages.empty())
   {
      std::string pkgList = boost::algorithm::join(cranPackages, ",");
      cmd += "utils::install.packages(c(" + pkgList + "), " +
             "repos = '"+ module_context::CRANReposURL() + "');";
   }
   BOOST_FOREACH(const std::string& pkg, embeddedPackages)
   {
      cmd += "utils::install.packages('" + pkg + "', "
                                      "repos = NULL, type = 'source');";
   }
   cmd += "}";

   // build args
   std::vector<std::string> args;
   args.push_back("--slave");

   // for packrat projects we execute the profile and set the working
   // directory to the project directory; for other contexts we just
   // propagate the R_LIBS
   if (module_context::packratContext().modeOn)
   {
      options.workingDir = projects::projectContext().directory();
   }
   else
   {
      args.push_back("--vanilla");
      core::system::Options childEnv;
      core::system::environment(&childEnv);
      std::string libPaths = module_context::libPathsString();
      if (!libPaths.empty())
         core::system::setenv(&childEnv, "R_LIBS", libPaths);
      options.environment = childEnv;
   }

   args.push_back("-e");
   args.push_back(cmd);

   // create and execute console process
   boost::shared_ptr<console_process::ConsoleProcess> pCP;
   pCP = console_process::ConsoleProcess::create(
            string_utils::utf8ToSystem(rProgramPath.absolutePath()),
            args,
            options,
            "Installing Packages",
            true,
            console_process::InteractionNever);

   // return console process
   pResponse->setResult(pCP->toJson());
   return Success();
}

} // anonymous namespace

Error initialize()
{
   // install handlers
   using boost::bind;
   using namespace session::module_context;
   ExecBlock initBlock ;
   initBlock.addFunctions()
      (bind(registerRpcMethod, "unsatisfied_dependencies", unsatisfiedDependencies))
      (bind(registerRpcMethod, "install_dependencies", installDependencies));
   return initBlock.execute();
}

} // namepsace dependencies
} // namespace modules

namespace module_context {

Error installEmbeddedPackage(const std::string& name)
{
   EmbeddedPackage pkg = embeddedPackageInfo(name);
   return module_context::installPackage(pkg.archivePath);
}

} // anonymous namespace

} // namesapce session

<commit_msg>don't silentUpdate embedded dependencies when packfied<commit_after>/*
 * SessionDependencies.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionDependencies.hpp"

#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/join.hpp>

#include <core/Error.hpp>
#include <core/Exec.hpp>
#include <core/system/Environment.hpp>

#include <core/json/JsonRpc.hpp>

#include <r/RExec.hpp>
#include <r/session/RSessionUtils.hpp>

#include <session/SessionModuleContext.hpp>
#include <session/SessionConsoleProcess.hpp>
#include <session/projects/SessionProjects.hpp>

using namespace core;

namespace session {

namespace {

struct EmbeddedPackage
{
   bool empty() const { return archivePath.empty(); }

   std::string name;
   std::string version;
   std::string sha1;
   std::string archivePath;
};

EmbeddedPackage embeddedPackageInfo(const std::string& name)
{
   // determine location of archives
   FilePath archivesDir = session::options().sessionPackageArchivesPath();
   std::vector<FilePath> children;
   Error error = archivesDir.children(&children);
   if (error)
   {
      LOG_ERROR(error);
      return EmbeddedPackage();
   }

   // we saw the regex with explicit character class ranges fail to match
   // on a windows 8.1 system so we are falling back to a simpler regex
   //
   // (note see below for another approach involving setting the locale
   // of the regex directly -- this assumes that the matching issue is
   // somehow related to locales)
   boost::regex re(name + "_([^_]+)_([^\\.]+)\\.tar\\.gz");

   /* another approach (which we didn't try) based on setting the regex locale
   boost::regex re;
   re.imbue(std::locale("en_US.UTF-8"));
   re.assign(name + "_([0-9]+\\.[0-9]+\\.[0-9]+)_([\\d\\w]+)\\.tar\\.gz");
   */

   BOOST_FOREACH(const FilePath& child, children)
   {
      boost::smatch match;
      if (boost::regex_match(child.filename(), match, re))
      {
         EmbeddedPackage pkg;
         pkg.name = name;
         pkg.version = match[1];
         pkg.sha1 = match[2];
         pkg.archivePath = string_utils::utf8ToSystem(child.absolutePath());
         return pkg;
      }
   }

   // none found
   return EmbeddedPackage();
}

} // anonymous namespace

namespace modules {
namespace dependencies {

namespace {

const int kCRANPackageDependency = 0;
const int kEmbeddedPackageDependency = 1;

struct Dependency
{
   Dependency() : type(0) {}

   bool empty() const { return name.empty(); }

   int type;
   std::string name;
   std::string version;
};

std::string nameFromDep(const Dependency& dep)
{
   return dep.name;
}

std::vector<std::string> packageNames(const std::vector<Dependency> deps)
{
   std::vector<std::string> names;
   std::transform(deps.begin(),
                  deps.end(),
                  std::back_inserter(names),
                  nameFromDep);
   return names;
}

std::vector<Dependency> dependenciesFromJson(const json::Array& depsJson)
{
   std::vector<Dependency> deps;
   BOOST_FOREACH(const json::Value& depJsonValue, depsJson)
   {
      if (json::isType<json::Object>(depJsonValue))
      {
         Dependency dep;
         json::Object depJson = depJsonValue.get_obj();
         Error error = json::readObject(depJson,
                                        "type", &(dep.type),
                                        "name", &(dep.name),
                                        "version", &(dep.version));
         if (!error)
         {
            deps.push_back(dep);
         }
         else
         {
            LOG_ERROR(error);
         }
      }
   }
   return deps;
}

json::Array dependenciesToJson(const std::vector<Dependency>& deps)
{
   json::Array depsJson;
   BOOST_FOREACH(const Dependency& dep, deps)
   {
      json::Object depJson;
      depJson["type"] = dep.type;
      depJson["name"] = dep.name;
      depJson["version"] = dep.version;
      depsJson.push_back(depJson);
   }
   return depsJson;
}

bool embeddedPackageRequiresUpdate(const EmbeddedPackage& pkg)
{
   // if this package came from the rstudio ide then check if it needs
   // an update (i.e. has a different SHA1)
   r::exec::RFunction func(".rs.rstudioIDEPackageRequiresUpdate",
                           pkg.name, pkg.sha1);
   bool requiresUpdate = false;
   Error error = func.call(&requiresUpdate);
   if (error)
      LOG_ERROR(error);

   return requiresUpdate;
}

void silentUpdateEmbeddedPackage(const EmbeddedPackage& pkg)
{
   // suppress output which occurs during silent update
   r::session::utils::SuppressOutputInScope suppressOutput;

   Error error = r::exec::RFunction(".rs.updateRStudioIDEPackage",
                             pkg.name, pkg.archivePath).call();
   if (error)
      LOG_ERROR(error);
}

Error unsatisfiedDependencies(const json::JsonRpcRequest& request,
                              json::JsonRpcResponse* pResponse)
{
   // get list of dependencies and silentUpdate flag
   json::Array depsJson;
   bool silentUpdate;
   Error error = json::readParams(request.params, &depsJson, &silentUpdate);
   if (error)
      return error;
   std::vector<Dependency> deps = dependenciesFromJson(depsJson);

   // build the list of unsatisifed dependencies
   using namespace module_context;
   std::vector<Dependency> unsatisfiedDeps;
   BOOST_FOREACH(const Dependency& dep, deps)
   {
      switch(dep.type)
      {
      case kCRANPackageDependency:
         if (!isPackageVersionInstalled(dep.name, dep.version))
         {
            unsatisfiedDeps.push_back(dep);
         }
         break;

      case kEmbeddedPackageDependency:
         EmbeddedPackage pkg = embeddedPackageInfo(dep.name);

         // package isn't installed so report that it reqires installation
         if (!isPackageInstalled(dep.name))
         {
            unsatisfiedDeps.push_back(dep);
         }
         // silent update if necessary (as long as we aren't packified)
         else if (silentUpdate && !packratContext().packified)
         {
            // package installed was from IDE but is out of date
            if (embeddedPackageRequiresUpdate(pkg))
            {
               silentUpdateEmbeddedPackage(pkg);
            }
            // package installed wasn't from the IDE but is older than
            // the version we currently have embedded
            else if (!isPackageVersionInstalled(pkg.name, pkg.version))
            {
               silentUpdateEmbeddedPackage(pkg);
            }
            else
            {
               // the only remaining case is a newer version of the package is
               // already installed (e.g. directly from github). in this case
               // we do nothing
            }
         }

         break;
      }
   }

   // return unsatisfied dependencies
   pResponse->setResult(dependenciesToJson(unsatisfiedDeps));
   return Success();
}

Error installDependencies(const json::JsonRpcRequest& request,
                          json::JsonRpcResponse* pResponse)
{
   // get list of dependencies
   json::Array depsJson;
   Error error = json::readParams(request.params, &depsJson);
   if (error)
      return error;
   std::vector<Dependency> deps = dependenciesFromJson(depsJson);

   // Ensure we have a writeable user library
   error = r::exec::RFunction(".rs.ensureWriteableUserLibrary").call();
   if (error)
      return error;

   // force unload as necessary
   std::vector<std::string> names = packageNames(deps);
   error = r::exec::RFunction(".rs.forceUnloadForPackageInstall", names).call();
   if (error)
      LOG_ERROR(error);

   // R binary
   FilePath rProgramPath;
   error = module_context::rScriptPath(&rProgramPath);
   if (error)
      return error;

   // options
   core::system::ProcessOptions options;
   options.terminateChildren = true;
   options.redirectStdErrToStdOut = true;

   // build lists of cran packages and archives
   std::vector<std::string> cranPackages;
   std::vector<std::string> embeddedPackages;
   BOOST_FOREACH(const Dependency& dep, deps)
   {
      switch(dep.type)
      {
      case kCRANPackageDependency:
         cranPackages.push_back("'" + dep.name + "'");
         break;

      case kEmbeddedPackageDependency:
         EmbeddedPackage pkg = embeddedPackageInfo(dep.name);
         if (!pkg.empty())
            embeddedPackages.push_back(pkg.archivePath);
         break;
      }
   }

   // build install command
   std::string cmd = "{";
   if (!cranPackages.empty())
   {
      std::string pkgList = boost::algorithm::join(cranPackages, ",");
      cmd += "utils::install.packages(c(" + pkgList + "), " +
             "repos = '"+ module_context::CRANReposURL() + "');";
   }
   BOOST_FOREACH(const std::string& pkg, embeddedPackages)
   {
      cmd += "utils::install.packages('" + pkg + "', "
                                      "repos = NULL, type = 'source');";
   }
   cmd += "}";

   // build args
   std::vector<std::string> args;
   args.push_back("--slave");

   // for packrat projects we execute the profile and set the working
   // directory to the project directory; for other contexts we just
   // propagate the R_LIBS
   if (module_context::packratContext().modeOn)
   {
      options.workingDir = projects::projectContext().directory();
   }
   else
   {
      args.push_back("--vanilla");
      core::system::Options childEnv;
      core::system::environment(&childEnv);
      std::string libPaths = module_context::libPathsString();
      if (!libPaths.empty())
         core::system::setenv(&childEnv, "R_LIBS", libPaths);
      options.environment = childEnv;
   }

   args.push_back("-e");
   args.push_back(cmd);

   // create and execute console process
   boost::shared_ptr<console_process::ConsoleProcess> pCP;
   pCP = console_process::ConsoleProcess::create(
            string_utils::utf8ToSystem(rProgramPath.absolutePath()),
            args,
            options,
            "Installing Packages",
            true,
            console_process::InteractionNever);

   // return console process
   pResponse->setResult(pCP->toJson());
   return Success();
}

} // anonymous namespace

Error initialize()
{
   // install handlers
   using boost::bind;
   using namespace session::module_context;
   ExecBlock initBlock ;
   initBlock.addFunctions()
      (bind(registerRpcMethod, "unsatisfied_dependencies", unsatisfiedDependencies))
      (bind(registerRpcMethod, "install_dependencies", installDependencies));
   return initBlock.execute();
}

} // namepsace dependencies
} // namespace modules

namespace module_context {

Error installEmbeddedPackage(const std::string& name)
{
   EmbeddedPackage pkg = embeddedPackageInfo(name);
   return module_context::installPackage(pkg.archivePath);
}

} // anonymous namespace

} // namesapce session

<|endoftext|>


<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/
#include "tensorflow/core/distributed_runtime/eager/cluster_function_library_runtime.h"

#include <map>
#include <memory>

#include "tensorflow/core/common_runtime/eager/context.h"
#include "tensorflow/core/common_runtime/eager/eager_operation.h"
#include "tensorflow/core/common_runtime/function.h"
#include "tensorflow/core/distributed_runtime/eager/eager_client.h"
#include "tensorflow/core/distributed_runtime/eager/remote_execute_node.h"
#include "tensorflow/core/distributed_runtime/eager/remote_mgr.h"
#include "tensorflow/core/framework/function.h"
#include "tensorflow/core/framework/graph_def_util.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/gtl/cleanup.h"

namespace tensorflow {
namespace eager {
namespace {
void StripDefaultAttributesInRegisterFunctionOp(
    RegisterFunctionOp* register_function) {
  StripDefaultAttributes(
      *OpRegistry::Global(),
      register_function->mutable_function_def()->mutable_node_def());
  for (auto& function :
       *register_function->mutable_library()->mutable_function()) {
    StripDefaultAttributes(*OpRegistry::Global(), function.mutable_node_def());
  }
}
}  // namespace

void EagerClusterFunctionLibraryRuntime::Instantiate(
    const string& function_name, const FunctionLibraryDefinition& lib_def,
    AttrSlice attrs, const FunctionLibraryRuntime::InstantiateOptions& options,
    FunctionLibraryRuntime::LocalHandle* handle,
    FunctionLibraryRuntime::DoneCallback done) {
  auto target = options.target;
  auto released_op = std::make_unique<EagerOperation>(ctx_);
  Status s =
      released_op->Reset(function_name.c_str(), target.c_str(), true, nullptr);
  if (!s.ok()) {
    done(s);
    return;
  }
  if (!released_op->is_function()) {
    done(errors::Internal(function_name, " is not a function."));
    return;
  }

  VLOG(1) << "CFLR::Instantiate: " << function_name << " on " << target
          << " (this: " << this << ")";
  core::RefCountPtr<eager::EagerClient> eager_client;
  Device* device;
  s = ctx_->FindDeviceFromName(target.c_str(), &device);
  if (!s.ok()) {
    done(s);
    return;
  }
  s = ctx_->GetClient(device, &eager_client);
  if (!s.ok()) {
    done(s);
    return;
  }

  if (eager_client == nullptr) {
    done(errors::InvalidArgument("Could not find eager client for target: ",
                                 target));
    return;
  }

  const FunctionLibraryDefinition& func_lib_def =
      options.lib_def ? *options.lib_def : lib_def;

  EnqueueRequest* request = new EnqueueRequest;
  EnqueueResponse* response = new EnqueueResponse;

  request->set_context_id(context_id_);

  RegisterFunctionOp* register_function =
      request->add_queue()->mutable_register_function();
  *register_function->mutable_function_def() =
      *func_lib_def.Find(function_name);
  register_function->set_is_component_function(true);
  *register_function->mutable_library() =
      func_lib_def.ReachableDefinitions(register_function->function_def())
          .ToProto();
  StripDefaultAttributesInRegisterFunctionOp(register_function);

  eager_client->EnqueueAsync(
      request, response,
      [this, request, response, handle, released_op = released_op.release(),
       target, eager_client = eager_client.get(), done](const Status& s) {
        {
          mutex_lock l(mu_);
          *handle = function_data_.size();
          function_data_.emplace_back(target, eager_client,
                                      absl::WrapUnique(released_op));
        }
        done(s);
        delete request;
        delete response;
      });
}

void EagerClusterFunctionLibraryRuntime::Run(
    const FunctionLibraryRuntime::Options& opts,
    FunctionLibraryRuntime::LocalHandle handle, gtl::ArraySlice<Tensor> args,
    std::vector<Tensor>* rets, FunctionLibraryRuntime::DoneCallback done) {
  std::vector<FunctionArg> function_args;
  for (const auto& tensor : args) {
    function_args.push_back(tensor);
  }
  Run(opts, handle, function_args, rets, std::move(done));
}

void EagerClusterFunctionLibraryRuntime::Run(
    const FunctionLibraryRuntime::Options& opts,
    FunctionLibraryRuntime::LocalHandle handle,
    gtl::ArraySlice<FunctionArg> args, std::vector<Tensor>* rets,
    FunctionLibraryRuntime::DoneCallback done) {
  FunctionData* function_data = nullptr;
  {
    mutex_lock l(mu_);
    DCHECK_LE(handle, function_data_.size());
    function_data = &function_data_[handle];
  }

  EagerClient* eager_client = function_data->eager_client.get();
  if (eager_client == nullptr) {
    done(errors::Internal("Could not find eager client"));
    return;
  }

  Device* device;
  Status s = ctx_->FindDeviceFromName(function_data->target.c_str(), &device);
  if (!s.ok()) {
    done(errors::Internal("Failed to get device"));
    return;
  }

  EagerOperation* op = function_data->op.get();

  eager::EnqueueRequest* request = new eager::EnqueueRequest;
  request->set_context_id(context_id_);
  eager::Operation* remote_op = request->add_queue()->mutable_operation();

  for (const auto& arg : args) {
    if (arg.index() == 0) {
      absl::get<Tensor>(arg).AsProtoTensorContent(
          remote_op->add_op_inputs()->mutable_tensor());
    } else {
      remote_op->add_op_inputs()->mutable_remote_handle()->Swap(
          absl::get<RemoteTensorHandle*>(arg));
    }
  }

  // The remote component function should use the same op_id as its parent
  // multi-device function's in order to get the global unique op_id generated
  // by the master context.
  if (opts.op_id.has_value()) {
    remote_op->set_id(opts.op_id.value());
  } else {
    remote_op->set_id(kInvalidRemoteOpId);
  }
  remote_op->set_is_function(true);
  remote_op->set_is_component_function(true);
  remote_op->set_func_step_id(opts.step_id);
  remote_op->set_name(op->Name());
  op->Attrs().FillAttrValueMap(remote_op->mutable_attrs());
  remote_op->set_device(function_data->target);

  for (auto handle : op->Inputs()) {
    handle->Ref();
  }

  // StreamingEnqueueAsync may introduce a deadlock. When streaming RPC is
  // disabled, Run() returns when the remote function execution completes, which
  // might be blocked by a non-enqueued function execution.
  EnqueueResponse* response = new EnqueueResponse;
  eager_client->EnqueueAsync(
      request, response,
      [op, request, response, rets, done = std::move(done)](const Status& s) {
        Status status = s;
        auto cleanup = gtl::MakeCleanup([request, response, &status, &done] {
          done(status);
          delete request;
          delete response;
        });

        for (auto handle : op->Inputs()) {
          handle->Unref();
        }
        if (!status.ok()) {
          return;
        }
        if (response->queue_response_size() != 1) {
          status.Update(errors::Internal(
              "Expect that the size of response queue equals 1, but got: ",
              response->queue_response_size()));
          return;
        }
        for (const auto& tensor_proto : response->queue_response(0).tensor()) {
          Tensor t;
          if (t.FromProto(tensor_proto)) {
            rets->push_back(std::move(t));
          } else {
            status.Update(errors::Internal("Could not convert tensor proto: ",
                                           tensor_proto.DebugString()));
            return;
          }
        }
      });
}

void EagerClusterFunctionLibraryRuntime::CleanUp(
    uint64 step_id, FunctionLibraryRuntime::LocalHandle handle,
    FunctionLibraryRuntime::DoneCallback done) {
  FunctionData* function_data = nullptr;
  {
    mutex_lock l(mu_);
    DCHECK_LE(handle, function_data_.size());
    function_data = &function_data_[handle];
  }

  EagerClient* eager_client = function_data->eager_client.get();
  if (eager_client == nullptr) {
    done(errors::Internal("Could not find eager client"));
    return;
  }

  eager::EnqueueRequest* request = new eager::EnqueueRequest;
  EnqueueResponse* response = new EnqueueResponse;
  request->set_context_id(context_id_);
  CleanupFunctionOp* cleanup_function =
      request->add_queue()->mutable_cleanup_function();
  cleanup_function->set_step_id(step_id);
  // StreamingEnqueueAsync could be blocking when streaming RPC is disabled.
  // CleanUp() needs to be non-blocking since it would be invoked inside the
  // enqueue done callback of Run(). So we don't use StreamingEnqueueAsync here.
  eager_client->EnqueueAsync(request, response,
                             [request, response, done](const Status& status) {
                               done(status);
                               delete request;
                               delete response;
                             });
}

DistributedFunctionLibraryRuntime* CreateClusterFLR(
    const uint64 context_id, EagerContext* ctx, WorkerSession* worker_session) {
  if (ctx->LazyCopyFunctionRemoteInputs()) {
    return new EagerClusterFunctionLibraryRuntime(
        context_id, ctx, worker_session->remote_device_mgr());
  } else {
    return worker_session->cluster_flr();
  }
}

}  // namespace eager
}  // namespace tensorflow
<commit_msg>[Cleanup] Remove redundant inputs ref/unref during execution, since inputs are not set during instantiation.<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/
#include "tensorflow/core/distributed_runtime/eager/cluster_function_library_runtime.h"

#include <map>
#include <memory>

#include "tensorflow/core/common_runtime/eager/context.h"
#include "tensorflow/core/common_runtime/eager/eager_operation.h"
#include "tensorflow/core/common_runtime/function.h"
#include "tensorflow/core/distributed_runtime/eager/eager_client.h"
#include "tensorflow/core/distributed_runtime/eager/remote_execute_node.h"
#include "tensorflow/core/distributed_runtime/eager/remote_mgr.h"
#include "tensorflow/core/framework/function.h"
#include "tensorflow/core/framework/graph_def_util.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/gtl/cleanup.h"

namespace tensorflow {
namespace eager {
namespace {
void StripDefaultAttributesInRegisterFunctionOp(
    RegisterFunctionOp* register_function) {
  StripDefaultAttributes(
      *OpRegistry::Global(),
      register_function->mutable_function_def()->mutable_node_def());
  for (auto& function :
       *register_function->mutable_library()->mutable_function()) {
    StripDefaultAttributes(*OpRegistry::Global(), function.mutable_node_def());
  }
}
}  // namespace

void EagerClusterFunctionLibraryRuntime::Instantiate(
    const string& function_name, const FunctionLibraryDefinition& lib_def,
    AttrSlice attrs, const FunctionLibraryRuntime::InstantiateOptions& options,
    FunctionLibraryRuntime::LocalHandle* handle,
    FunctionLibraryRuntime::DoneCallback done) {
  auto target = options.target;
  auto released_op = std::make_unique<EagerOperation>(ctx_);
  Status s =
      released_op->Reset(function_name.c_str(), target.c_str(), true, nullptr);
  if (!s.ok()) {
    done(s);
    return;
  }
  if (!released_op->is_function()) {
    done(errors::Internal(function_name, " is not a function."));
    return;
  }

  VLOG(1) << "CFLR::Instantiate: " << function_name << " on " << target
          << " (this: " << this << ")";
  core::RefCountPtr<eager::EagerClient> eager_client;
  Device* device;
  s = ctx_->FindDeviceFromName(target.c_str(), &device);
  if (!s.ok()) {
    done(s);
    return;
  }
  s = ctx_->GetClient(device, &eager_client);
  if (!s.ok()) {
    done(s);
    return;
  }

  if (eager_client == nullptr) {
    done(errors::InvalidArgument("Could not find eager client for target: ",
                                 target));
    return;
  }

  const FunctionLibraryDefinition& func_lib_def =
      options.lib_def ? *options.lib_def : lib_def;

  EnqueueRequest* request = new EnqueueRequest;
  EnqueueResponse* response = new EnqueueResponse;

  request->set_context_id(context_id_);

  RegisterFunctionOp* register_function =
      request->add_queue()->mutable_register_function();
  *register_function->mutable_function_def() =
      *func_lib_def.Find(function_name);
  register_function->set_is_component_function(true);
  *register_function->mutable_library() =
      func_lib_def.ReachableDefinitions(register_function->function_def())
          .ToProto();
  StripDefaultAttributesInRegisterFunctionOp(register_function);

  eager_client->EnqueueAsync(
      request, response,
      [this, request, response, handle, released_op = released_op.release(),
       target, eager_client = eager_client.get(), done](const Status& s) {
        {
          mutex_lock l(mu_);
          *handle = function_data_.size();
          function_data_.emplace_back(target, eager_client,
                                      absl::WrapUnique(released_op));
        }
        done(s);
        delete request;
        delete response;
      });
}

void EagerClusterFunctionLibraryRuntime::Run(
    const FunctionLibraryRuntime::Options& opts,
    FunctionLibraryRuntime::LocalHandle handle, gtl::ArraySlice<Tensor> args,
    std::vector<Tensor>* rets, FunctionLibraryRuntime::DoneCallback done) {
  std::vector<FunctionArg> function_args;
  for (const auto& tensor : args) {
    function_args.push_back(tensor);
  }
  Run(opts, handle, function_args, rets, std::move(done));
}

void EagerClusterFunctionLibraryRuntime::Run(
    const FunctionLibraryRuntime::Options& opts,
    FunctionLibraryRuntime::LocalHandle handle,
    gtl::ArraySlice<FunctionArg> args, std::vector<Tensor>* rets,
    FunctionLibraryRuntime::DoneCallback done) {
  FunctionData* function_data = nullptr;
  {
    mutex_lock l(mu_);
    DCHECK_LE(handle, function_data_.size());
    function_data = &function_data_[handle];
  }

  EagerClient* eager_client = function_data->eager_client.get();
  if (eager_client == nullptr) {
    done(errors::Internal("Could not find eager client"));
    return;
  }

  Device* device;
  Status s = ctx_->FindDeviceFromName(function_data->target.c_str(), &device);
  if (!s.ok()) {
    done(errors::Internal("Failed to get device"));
    return;
  }

  EagerOperation* op = function_data->op.get();

  if (!op->Inputs().empty()) {
    done(errors::Internal("Inputs should not be set during instantiation."));
    return;
  }

  eager::EnqueueRequest* request = new eager::EnqueueRequest;
  request->set_context_id(context_id_);
  eager::Operation* remote_op = request->add_queue()->mutable_operation();

  for (const auto& arg : args) {
    if (arg.index() == 0) {
      absl::get<Tensor>(arg).AsProtoTensorContent(
          remote_op->add_op_inputs()->mutable_tensor());
    } else {
      remote_op->add_op_inputs()->mutable_remote_handle()->Swap(
          absl::get<RemoteTensorHandle*>(arg));
    }
  }

  // The remote component function should use the same op_id as its parent
  // multi-device function's in order to get the global unique op_id generated
  // by the master context.
  if (opts.op_id.has_value()) {
    remote_op->set_id(opts.op_id.value());
  } else {
    remote_op->set_id(kInvalidRemoteOpId);
  }
  remote_op->set_is_function(true);
  remote_op->set_is_component_function(true);
  remote_op->set_func_step_id(opts.step_id);
  remote_op->set_name(op->Name());
  op->Attrs().FillAttrValueMap(remote_op->mutable_attrs());
  remote_op->set_device(function_data->target);

  // StreamingEnqueueAsync may introduce a deadlock. When streaming RPC is
  // disabled, Run() returns when the remote function execution completes, which
  // might be blocked by a non-enqueued function execution.
  EnqueueResponse* response = new EnqueueResponse;
  eager_client->EnqueueAsync(
      request, response,
      [request, response, rets, done = std::move(done)](const Status& s) {
        Status status = s;
        auto cleanup = gtl::MakeCleanup([request, response, &status, &done] {
          done(status);
          delete request;
          delete response;
        });

        if (!status.ok()) {
          return;
        }
        if (response->queue_response_size() != 1) {
          status.Update(errors::Internal(
              "Expect that the size of response queue equals 1, but got: ",
              response->queue_response_size()));
          return;
        }
        for (const auto& tensor_proto : response->queue_response(0).tensor()) {
          Tensor t;
          if (t.FromProto(tensor_proto)) {
            rets->push_back(std::move(t));
          } else {
            status.Update(errors::Internal("Could not convert tensor proto: ",
                                           tensor_proto.DebugString()));
            return;
          }
        }
      });
}

void EagerClusterFunctionLibraryRuntime::CleanUp(
    uint64 step_id, FunctionLibraryRuntime::LocalHandle handle,
    FunctionLibraryRuntime::DoneCallback done) {
  FunctionData* function_data = nullptr;
  {
    mutex_lock l(mu_);
    DCHECK_LE(handle, function_data_.size());
    function_data = &function_data_[handle];
  }

  EagerClient* eager_client = function_data->eager_client.get();
  if (eager_client == nullptr) {
    done(errors::Internal("Could not find eager client"));
    return;
  }

  eager::EnqueueRequest* request = new eager::EnqueueRequest;
  EnqueueResponse* response = new EnqueueResponse;
  request->set_context_id(context_id_);
  CleanupFunctionOp* cleanup_function =
      request->add_queue()->mutable_cleanup_function();
  cleanup_function->set_step_id(step_id);
  // StreamingEnqueueAsync could be blocking when streaming RPC is disabled.
  // CleanUp() needs to be non-blocking since it would be invoked inside the
  // enqueue done callback of Run(). So we don't use StreamingEnqueueAsync here.
  eager_client->EnqueueAsync(request, response,
                             [request, response, done](const Status& status) {
                               done(status);
                               delete request;
                               delete response;
                             });
}

DistributedFunctionLibraryRuntime* CreateClusterFLR(
    const uint64 context_id, EagerContext* ctx, WorkerSession* worker_session) {
  if (ctx->LazyCopyFunctionRemoteInputs()) {
    return new EagerClusterFunctionLibraryRuntime(
        context_id, ctx, worker_session->remote_device_mgr());
  } else {
    return worker_session->cluster_flr();
  }
}

}  // namespace eager
}  // namespace tensorflow
<|endoftext|>


<commit_before>#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <stdlib.h>
#include <unistd.h>
#include "process.h"
#include "log.h"

using namespace std;

/**
 * Sends signal SIGTERM to pid and all of its children. Waits up to wait_upto
 * seconds before SIGKILL is sent.
 * @param pid
 * @return
 */
bool kill_process(pid_t pid, int wait_upto) {
	DIR *proc_dir;
	struct dirent *process_dir;
	pid_t c_pid, ppid;

	// first get all pids of the currently running processes
	if ((proc_dir = opendir("/proc")) == NULL) {
		log_error(AT, "Could not open /proc");
		return false;
	}
	vector < pid_t > pids;
	while ((process_dir = readdir(proc_dir)) != NULL) {
		if (isdigit(process_dir->d_name[0])) {
			c_pid = (pid_t) atoi(process_dir->d_name);
			pids.push_back(c_pid);
		}
	}

	// iterate over children and add the children of the children, and so on.
	vector < pid_t > children;
	children.push_back(pid);
	FILE *proc_file;
	for (unsigned int i = 0; i < children.size(); i++) {
		vector<pid_t>::const_iterator p;
		for (p = pids.begin(); p != pids.end(); p++) {
			char proc_filename[1024];
			sprintf(proc_filename, "/proc/%d/status", *p);
			if ((proc_file = fopen(proc_filename, "r")) != NULL) {
				ppid = -1;
				char line[81];
				while (ppid == -1 && fgets(line, 80, proc_file) != NULL) {
					sscanf(line, "PPid: %d", &ppid);
				}
				if (ppid == children[i]) {
					children.push_back(*p);
				}
				fclose(proc_file);
			}
		}
	}
	kill(pid, SIGTERM);

	// wait; check if pid is killed
	for (int i = 0; i < wait_upto; i++) {
		if (kill(pid, 0) != 0)
			break;
		sleep(1000);
	}

	vector<pid_t>::reverse_iterator child;
	for (child = children.rbegin(); child != children.rend(); child++) {
		if (kill(*child, 0) == 0) {
			// the child isn't killed -> SIGKILL
			log_message(LOG_IMPORTANT, "Sending SIGKILL to %d", *child);
			kill(*child, SIGKILL);
		}
	}
	return true;
}

/**
 * Sends signal SIGTERM to pid and all of its children. Waits max. 2 sec until
 * SIGKILL is sent.
 * @param pid
 * @return
 */
bool kill_process(pid_t pid) {
	return kill_process(pid, 2);
}
<commit_msg>bugfix<commit_after>#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <stdlib.h>
#include <unistd.h>
#include "process.h"
#include "log.h"

using namespace std;

/**
 * Sends signal SIGTERM to pid and all of its children. Waits up to wait_upto
 * seconds before SIGKILL is sent.
 * @param pid
 * @return
 */
bool kill_process(pid_t pid, int wait_upto) {
	DIR *proc_dir;
	struct dirent *process_dir;
	pid_t c_pid, ppid;

	// first get all pids of the currently running processes
	if ((proc_dir = opendir("/proc")) == NULL) {
		log_error(AT, "Could not open /proc");
		return false;
	}
	vector < pid_t > pids;
	while ((process_dir = readdir(proc_dir)) != NULL) {
		if (isdigit(process_dir->d_name[0])) {
			c_pid = (pid_t) atoi(process_dir->d_name);
			pids.push_back(c_pid);
		}
	}

	// iterate over children and add the children of the children, and so on.
	vector < pid_t > children;
	children.push_back(pid);
	FILE *proc_file;
	for (unsigned int i = 0; i < children.size(); i++) {
		vector<pid_t>::const_iterator p;
		for (p = pids.begin(); p != pids.end(); p++) {
			char proc_filename[1024];
			sprintf(proc_filename, "/proc/%d/status", *p);
			if ((proc_file = fopen(proc_filename, "r")) != NULL) {
				ppid = -1;
				char line[81];
				while (ppid == -1 && fgets(line, 80, proc_file) != NULL) {
					sscanf(line, "PPid: %d", &ppid);
				}
				if (ppid == children[i]) {
					children.push_back(*p);
				}
				fclose(proc_file);
			}
		}
	}
	kill(pid, SIGTERM);

	// wait; check if pid is killed
	for (int i = 0; i < wait_upto; i++) {
		if (kill(pid, 0) != 0)
			break;
		sleep(1);
	}

	vector<pid_t>::reverse_iterator child;
	for (child = children.rbegin(); child != children.rend(); child++) {
		if (kill(*child, 0) == 0) {
			// the child isn't killed -> SIGKILL
			log_message(LOG_IMPORTANT, "Sending SIGKILL to %d", *child);
			kill(*child, SIGKILL);
		}
	}
	return true;
}

/**
 * Sends signal SIGTERM to pid and all of its children. Waits max. 2 sec until
 * SIGKILL is sent.
 * @param pid
 * @return
 */
bool kill_process(pid_t pid) {
	return kill_process(pid, 2);
}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/coro/generator.h>
#include <vespa/vespalib/util/benchmark_timer.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <ranges>
#include <vector>

using vespalib::coro::Generator;
using vespalib::BenchmarkTimer;

std::vector<size_t> make_data() __attribute__((noinline));
std::vector<size_t> make_data(size_t size) {
    std::vector<size_t> data;
    for (size_t i = 0; i < size; ++i) {
        data.push_back(i);
    }
    return data;
}

template <std::ranges::input_range T>
size_t calc_sum(T&& values) {
    size_t sum = 0;
    for (auto&& value: values) {
        sum += value;
    }
    return sum;
}

size_t calc_sum_direct(const std::vector<size_t> &values) {
    return calc_sum(values);
}

size_t calc_sum_wrapped(const std::vector<size_t> &values) {
    return calc_sum([](const std::vector<size_t> &inner_values)->Generator<size_t>
                    {
                        for (auto&& value: inner_values) {
                            co_yield value;
                        }
                    }(values));
}

TEST(GeneratorBench, direct_vs_wrapped_vector_for_loop) {
    std::vector<size_t> data = make_data(100000);
    double direct_ms = BenchmarkTimer::benchmark([&data](){
                                                     size_t sink = calc_sum_direct(data);
                                                     (void) sink;
                                                 }, 5.0) * 1000.0;
    fprintf(stderr, "direct: %g ms\n", direct_ms);
    double wrapped_ms = BenchmarkTimer::benchmark([&data](){
                                                      size_t sink = calc_sum_wrapped(data);
                                                      (void) sink;
                                                  }, 5.0) * 1000.0;
    fprintf(stderr, "wrapped: %g ms\n", wrapped_ms);
    fprintf(stderr, "ratio: %g\n", (wrapped_ms/direct_ms));
}

GTEST_MAIN_RUN_ALL_TESTS()
<commit_msg>adjust generator bench<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/coro/generator.h>
#include <vespa/vespalib/util/benchmark_timer.h>
#include <vespa/vespalib/util/sequence.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <ranges>
#include <vector>

using vespalib::coro::Generator;
using vespalib::BenchmarkTimer;
using vespalib::Sequence;

template <std::ranges::input_range T>
size_t calc_sum(T&& values) {
    size_t sum = 0;
    for (auto&& value: values) {
        sum += value;
    }
    return sum;
}

size_t calc_sum(Sequence<size_t> &seq) {
    size_t sum = 0;
    for (; seq.valid(); seq.next()) {
        sum += seq.get();
    }
    return sum;
}

struct ValueRange {
    using iterator_concept = std::input_iterator_tag;
    using difference_type = std::ptrdiff_t;
    using value_type = size_t;
    size_t first;
    size_t last;
    ValueRange() noexcept : first(0), last(0) {}
    ValueRange(size_t first_in, size_t last_in) noexcept
      : first(first_in), last(last_in) {}
    auto begin() noexcept { return ValueRange(first, last); }
    auto end() const noexcept { return std::default_sentinel_t(); }
    bool operator==(std::default_sentinel_t) const noexcept { return (first == last); }
    ValueRange &operator++() noexcept { ++first; return *this; }
    void operator++(int) noexcept { operator++(); }
    size_t operator*() const noexcept { return first; }
};

size_t calc_sum_direct(size_t first, size_t last) {
    return calc_sum(ValueRange(first, last));
}

Generator<size_t> gen_values(size_t first, size_t last) {
    for (size_t i = first; i < last; ++i) {
        co_yield i;
    }
}

size_t calc_sum_generator(size_t first, size_t last) {
    return calc_sum(gen_values(first, last));
}

struct MySeq : Sequence<size_t> {
    size_t first;
    size_t last;
    MySeq(size_t first_in, size_t last_in)
      : first(first_in), last(last_in) {}
    bool valid() const override { return (first < last); }
    size_t get() const override { return first; }
    void next() override { ++first; }
};

size_t calc_sum_sequence(size_t first, size_t last) {
    MySeq seq(first, last);
    return calc_sum(seq);
}

TEST(GeneratorBench, direct_vs_generated_for_loop) {
    size_t first = 0;
    size_t last = 100000;
    size_t res_direct = 0;
    size_t res_generator = 1;
    size_t res_sequence = 2;
    double direct_ms = BenchmarkTimer::benchmark([first,last,&res_direct](){
                                                     res_direct = calc_sum_direct(first, last);
                                                 }, 5.0) * 1000.0;
    fprintf(stderr, "direct: %g ms\n", direct_ms);
    double generator_ms = BenchmarkTimer::benchmark([first,last,&res_generator](){
                                                        res_generator = calc_sum_generator(first, last);
                                                    }, 5.0) * 1000.0;
    fprintf(stderr, "generator: %g ms\n", generator_ms);
    double sequence_ms = BenchmarkTimer::benchmark([first,last,&res_sequence](){
                                                       res_sequence = calc_sum_sequence(first, last);
                                                   }, 5.0) * 1000.0;
    fprintf(stderr, "sequence: %g ms\n", sequence_ms);
    fprintf(stderr, "ratio (generator/direct): %g\n", (generator_ms/direct_ms));
    fprintf(stderr, "ratio (generator/sequence): %g\n", (generator_ms/sequence_ms));
}

GTEST_MAIN_RUN_ALL_TESTS()
<|endoftext|>