<commit_before>// Copyright 2013 The Flutter 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 "flutter/flow/scene_update_context.h"

#include "flutter/flow/layers/layer.h"
#include "flutter/flow/matrix_decomposition.h"
#include "flutter/fml/trace_event.h"
#include "include/core/SkColor.h"

namespace flutter {

// Helper function to generate clip planes for a scenic::EntityNode.
static void SetEntityNodeClipPlanes(scenic::EntityNode& entity_node,
                                    const SkRect& bounds) {
  const float top = bounds.top();
  const float bottom = bounds.bottom();
  const float left = bounds.left();
  const float right = bounds.right();

  // We will generate 4 oriented planes, one for each edge of the bounding rect.
  std::vector<fuchsia::ui::gfx::Plane3> clip_planes;
  clip_planes.resize(4);

  // Top plane.
  clip_planes[0].dist = top;
  clip_planes[0].dir.x = 0.f;
  clip_planes[0].dir.y = 1.f;
  clip_planes[0].dir.z = 0.f;

  // Bottom plane.
  clip_planes[1].dist = -bottom;
  clip_planes[1].dir.x = 0.f;
  clip_planes[1].dir.y = -1.f;
  clip_planes[1].dir.z = 0.f;

  // Left plane.
  clip_planes[2].dist = left;
  clip_planes[2].dir.x = 1.f;
  clip_planes[2].dir.y = 0.f;
  clip_planes[2].dir.z = 0.f;

  // Right plane.
  clip_planes[3].dist = -right;
  clip_planes[3].dir.x = -1.f;
  clip_planes[3].dir.y = 0.f;
  clip_planes[3].dir.z = 0.f;

  entity_node.SetClipPlanes(std::move(clip_planes));
}

SceneUpdateContext::SceneUpdateContext(scenic::Session* session,
                                       SurfaceProducer* surface_producer)
    : session_(session), surface_producer_(surface_producer) {
  FML_DCHECK(surface_producer_ != nullptr);
}

void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node,
                                     const SkRRect& rrect,
                                     SkColor color,
                                     SkAlpha opacity,
                                     const SkRect& paint_bounds,
                                     std::vector<Layer*> paint_layers,
                                     Layer* layer) {
  FML_DCHECK(!rrect.isEmpty());

  // Frames always clip their children.
  SkRect shape_bounds = rrect.getBounds();
  SetEntityNodeClipPlanes(entity_node, shape_bounds);

  // and possibly for its texture.
  // TODO(SCN-137): Need to be able to express the radii as vectors.
  scenic::ShapeNode shape_node(session());
  scenic::Rectangle shape(session_,       // session
                          rrect.width(),  // width
                          rrect.height()  // height
  );
  shape_node.SetShape(shape);
  shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(),
                            shape_bounds.height() * 0.5f + shape_bounds.top(),
                            0.f);

  // Check whether the painted layers will be visible.
  if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds))
    paint_layers.clear();

  scenic::Material material(session());
  shape_node.SetMaterial(material);
  entity_node.AddChild(shape_node);

  // Check whether a solid color will suffice.
  if (paint_layers.empty()) {
    SetMaterialColor(material, color, opacity);
  } else {
    // Apply current metrics and transformation scale factors.
    const float scale_x = ScaleX();
    const float scale_y = ScaleY();

    // Apply a texture to the whole shape.
    SetMaterialTextureAndColor(material, color, opacity, scale_x, scale_y,
                               shape_bounds, std::move(paint_layers), layer,
                               std::move(entity_node));
  }
}

void SceneUpdateContext::SetMaterialTextureAndColor(
    scenic::Material& material,
    SkColor color,
    SkAlpha opacity,
    SkScalar scale_x,
    SkScalar scale_y,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer,
    scenic::EntityNode entity_node) {
  scenic::Image* image = GenerateImageIfNeeded(
      color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer,
      std::move(entity_node));

  if (image != nullptr) {
    // The final shape's color is material_color * texture_color.  The passed in
    // material color was already used as a background when generating the
    // texture, so set the model color to |SK_ColorWHITE| in order to allow
    // using the texture's color unmodified.
    SetMaterialColor(material, SK_ColorWHITE, opacity);
    material.SetTexture(*image);
  } else {
    // No texture was needed, so apply a solid color to the whole shape.
    SetMaterialColor(material, color, opacity);
  }
}

void SceneUpdateContext::SetMaterialColor(scenic::Material& material,
                                          SkColor color,
                                          SkAlpha opacity) {
  const SkAlpha color_alpha = static_cast<SkAlpha>(
      ((float)SkColorGetA(color) * (float)opacity) / 255.0f);
  material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color),
                    color_alpha);
}

scenic::Image* SceneUpdateContext::GenerateImageIfNeeded(
    SkColor color,
    SkScalar scale_x,
    SkScalar scale_y,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer,
    scenic::EntityNode entity_node) {
  // Bail if there's nothing to paint.
  if (paint_layers.empty())
    return nullptr;

  // Bail if the physical bounds are empty after rounding.
  SkISize physical_size = SkISize::Make(paint_bounds.width() * scale_x,
                                        paint_bounds.height() * scale_y);
  if (physical_size.isEmpty())
    return nullptr;

  // Acquire a surface from the surface producer and register the paint tasks.
  std::unique_ptr<SurfaceProducerSurface> surface =
      surface_producer_->ProduceSurface(
          physical_size,
          LayerRasterCacheKey(
              // Root frame has a nullptr layer
              layer ? layer->unique_id() : 0, Matrix()),
          std::make_unique<scenic::EntityNode>(std::move(entity_node)));

  if (!surface) {
    FML_LOG(ERROR) << "Could not acquire a surface from the surface producer "
                      "of size: "
                   << physical_size.width() << "x" << physical_size.height();
    return nullptr;
  }

  auto image = surface->GetImage();

  // Enqueue the paint task.
  paint_tasks_.push_back({.surface = std::move(surface),
                          .left = paint_bounds.left(),
                          .top = paint_bounds.top(),
                          .scale_x = scale_x,
                          .scale_y = scale_y,
                          .background_color = color,
                          .layers = std::move(paint_layers)});
  return image;
}

std::vector<
    std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>>
SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) {
  TRACE_EVENT0("flutter", "SceneUpdateContext::ExecutePaintTasks");
  std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces_to_submit;
  for (auto& task : paint_tasks_) {
    FML_DCHECK(task.surface);
    SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas();
    Layer::PaintContext context = {canvas,
                                   canvas,
                                   frame.gr_context(),
                                   nullptr,
                                   frame.context().raster_time(),
                                   frame.context().ui_time(),
                                   frame.context().texture_registry(),
                                   &frame.context().raster_cache(),
                                   false,
                                   frame_physical_depth_,
                                   frame_device_pixel_ratio_};
    canvas->restoreToCount(1);
    canvas->save();
    canvas->clear(task.background_color);
    canvas->scale(task.scale_x, task.scale_y);
    canvas->translate(-task.left, -task.top);
    for (Layer* layer : task.layers) {
      layer->Paint(context);
    }
    surfaces_to_submit.emplace_back(std::move(task.surface));
  }
  paint_tasks_.clear();
  alpha_ = 1.f;
  topmost_global_scenic_elevation_ = kScenicZElevationBetweenLayers;
  scenic_elevation_ = 0.f;
  return surfaces_to_submit;
}

SceneUpdateContext::Entity::Entity(SceneUpdateContext& context)
    : context_(context),
      previous_entity_(context.top_entity_),
      entity_node_(context.session()) {
  if (previous_entity_)
    previous_entity_->embedder_node().AddChild(entity_node_);
  context.top_entity_ = this;
}

SceneUpdateContext::Entity::~Entity() {
  FML_DCHECK(context_.top_entity_ == this);
  context_.top_entity_ = previous_entity_;
}

SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
                                         const SkMatrix& transform)
    : Entity(context),
      previous_scale_x_(context.top_scale_x_),
      previous_scale_y_(context.top_scale_y_) {
  entity_node().SetLabel("flutter::Transform");
  if (!transform.isIdentity()) {
    // TODO(SCN-192): The perspective and shear components in the matrix
    // are not handled correctly.
    MatrixDecomposition decomposition(transform);
    if (decomposition.IsValid()) {
      // Don't allow clients to control the z dimension; we control that
      // instead to make sure layers appear in proper order.
      entity_node().SetTranslation(decomposition.translation().x(),  //
                                   decomposition.translation().y(),  //
                                   0.f                               //
      );

      entity_node().SetScale(decomposition.scale().x(),  //
                             decomposition.scale().y(),  //
                             0.f                         //
      );
      context.top_scale_x_ *= decomposition.scale().x();
      context.top_scale_y_ *= decomposition.scale().y();

      entity_node().SetRotation(decomposition.rotation().fData[0],  //
                                decomposition.rotation().fData[1],  //
                                decomposition.rotation().fData[2],  //
                                decomposition.rotation().fData[3]   //
      );
    }
  }
}

SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
                                         float scale_x,
                                         float scale_y,
                                         float scale_z)
    : Entity(context),
      previous_scale_x_(context.top_scale_x_),
      previous_scale_y_(context.top_scale_y_) {
  entity_node().SetLabel("flutter::Transform");
  if (scale_x != 1.f || scale_y != 1.f || scale_z != 1.f) {
    entity_node().SetScale(scale_x, scale_y, scale_z);
    context.top_scale_x_ *= scale_x;
    context.top_scale_y_ *= scale_y;
  }
}

SceneUpdateContext::Transform::~Transform() {
  context().top_scale_x_ = previous_scale_x_;
  context().top_scale_y_ = previous_scale_y_;
}

SceneUpdateContext::Frame::Frame(SceneUpdateContext& context,
                                 const SkRRect& rrect,
                                 SkColor color,
                                 SkAlpha opacity,
                                 std::string label,
                                 float z_translation,
                                 Layer* layer)
    : Entity(context),
      rrect_(rrect),
      color_(color),
      opacity_(opacity),
      opacity_node_(context.session()),
      paint_bounds_(SkRect::MakeEmpty()),
      layer_(layer) {
  entity_node().SetLabel(label);
  entity_node().SetTranslation(0.f, 0.f, z_translation);
  entity_node().AddChild(opacity_node_);
  // Scenic currently lacks an API to enable rendering of alpha channel; alpha
  // channels are only rendered if there is a OpacityNode higher in the tree
  // with opacity != 1. For now, clamp to a infinitesimally smaller value than
  // 1, which does not cause visual problems in practice.
  opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f));
}

SceneUpdateContext::Frame::~Frame() {
  // We don't need a shape if the frame is zero size.
  if (rrect_.isEmpty())
    return;

  // isEmpty should account for this, but we are adding these experimental
  // checks to validate if this is the root cause for b/144933519.
  if (std::isnan(rrect_.width()) || std::isnan(rrect_.height())) {
    FML_LOG(ERROR) << "Invalid RoundedRectangle";
    return;
  }

  // Add a part which represents the frame's geometry for clipping purposes
  context().CreateFrame(std::move(entity_node()), rrect_, color_, opacity_,
                        paint_bounds_, std::move(paint_layers_), layer_);
}

void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) {
  FML_DCHECK(layer->needs_painting());
  paint_layers_.push_back(layer);
  paint_bounds_.join(layer->paint_bounds());
}

SceneUpdateContext::Clip::Clip(SceneUpdateContext& context,
                               const SkRect& shape_bounds)
    : Entity(context) {
  entity_node().SetLabel("flutter::Clip");
  SetEntityNodeClipPlanes(entity_node(), shape_bounds);
}

}  // namespace flutter
<commit_msg>[fuchsia] Fix bug when applying scale. (#17436)<commit_after>// Copyright 2013 The Flutter 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 "flutter/flow/scene_update_context.h"

#include "flutter/flow/layers/layer.h"
#include "flutter/flow/matrix_decomposition.h"
#include "flutter/fml/trace_event.h"
#include "include/core/SkColor.h"

namespace flutter {

// Helper function to generate clip planes for a scenic::EntityNode.
static void SetEntityNodeClipPlanes(scenic::EntityNode& entity_node,
                                    const SkRect& bounds) {
  const float top = bounds.top();
  const float bottom = bounds.bottom();
  const float left = bounds.left();
  const float right = bounds.right();

  // We will generate 4 oriented planes, one for each edge of the bounding rect.
  std::vector<fuchsia::ui::gfx::Plane3> clip_planes;
  clip_planes.resize(4);

  // Top plane.
  clip_planes[0].dist = top;
  clip_planes[0].dir.x = 0.f;
  clip_planes[0].dir.y = 1.f;
  clip_planes[0].dir.z = 0.f;

  // Bottom plane.
  clip_planes[1].dist = -bottom;
  clip_planes[1].dir.x = 0.f;
  clip_planes[1].dir.y = -1.f;
  clip_planes[1].dir.z = 0.f;

  // Left plane.
  clip_planes[2].dist = left;
  clip_planes[2].dir.x = 1.f;
  clip_planes[2].dir.y = 0.f;
  clip_planes[2].dir.z = 0.f;

  // Right plane.
  clip_planes[3].dist = -right;
  clip_planes[3].dir.x = -1.f;
  clip_planes[3].dir.y = 0.f;
  clip_planes[3].dir.z = 0.f;

  entity_node.SetClipPlanes(std::move(clip_planes));
}

SceneUpdateContext::SceneUpdateContext(scenic::Session* session,
                                       SurfaceProducer* surface_producer)
    : session_(session), surface_producer_(surface_producer) {
  FML_DCHECK(surface_producer_ != nullptr);
}

void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node,
                                     const SkRRect& rrect,
                                     SkColor color,
                                     SkAlpha opacity,
                                     const SkRect& paint_bounds,
                                     std::vector<Layer*> paint_layers,
                                     Layer* layer) {
  FML_DCHECK(!rrect.isEmpty());

  // Frames always clip their children.
  SkRect shape_bounds = rrect.getBounds();
  SetEntityNodeClipPlanes(entity_node, shape_bounds);

  // and possibly for its texture.
  // TODO(SCN-137): Need to be able to express the radii as vectors.
  scenic::ShapeNode shape_node(session());
  scenic::Rectangle shape(session_,       // session
                          rrect.width(),  // width
                          rrect.height()  // height
  );
  shape_node.SetShape(shape);
  shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(),
                            shape_bounds.height() * 0.5f + shape_bounds.top(),
                            0.f);

  // Check whether the painted layers will be visible.
  if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds))
    paint_layers.clear();

  scenic::Material material(session());
  shape_node.SetMaterial(material);
  entity_node.AddChild(shape_node);

  // Check whether a solid color will suffice.
  if (paint_layers.empty()) {
    SetMaterialColor(material, color, opacity);
  } else {
    // Apply current metrics and transformation scale factors.
    const float scale_x = ScaleX();
    const float scale_y = ScaleY();

    // Apply a texture to the whole shape.
    SetMaterialTextureAndColor(material, color, opacity, scale_x, scale_y,
                               shape_bounds, std::move(paint_layers), layer,
                               std::move(entity_node));
  }
}

void SceneUpdateContext::SetMaterialTextureAndColor(
    scenic::Material& material,
    SkColor color,
    SkAlpha opacity,
    SkScalar scale_x,
    SkScalar scale_y,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer,
    scenic::EntityNode entity_node) {
  scenic::Image* image = GenerateImageIfNeeded(
      color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer,
      std::move(entity_node));

  if (image != nullptr) {
    // The final shape's color is material_color * texture_color.  The passed in
    // material color was already used as a background when generating the
    // texture, so set the model color to |SK_ColorWHITE| in order to allow
    // using the texture's color unmodified.
    SetMaterialColor(material, SK_ColorWHITE, opacity);
    material.SetTexture(*image);
  } else {
    // No texture was needed, so apply a solid color to the whole shape.
    SetMaterialColor(material, color, opacity);
  }
}

void SceneUpdateContext::SetMaterialColor(scenic::Material& material,
                                          SkColor color,
                                          SkAlpha opacity) {
  const SkAlpha color_alpha = static_cast<SkAlpha>(
      ((float)SkColorGetA(color) * (float)opacity) / 255.0f);
  material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color),
                    color_alpha);
}

scenic::Image* SceneUpdateContext::GenerateImageIfNeeded(
    SkColor color,
    SkScalar scale_x,
    SkScalar scale_y,
    const SkRect& paint_bounds,
    std::vector<Layer*> paint_layers,
    Layer* layer,
    scenic::EntityNode entity_node) {
  // Bail if there's nothing to paint.
  if (paint_layers.empty())
    return nullptr;

  // Bail if the physical bounds are empty after rounding.
  SkISize physical_size = SkISize::Make(paint_bounds.width() * scale_x,
                                        paint_bounds.height() * scale_y);
  if (physical_size.isEmpty())
    return nullptr;

  // Acquire a surface from the surface producer and register the paint tasks.
  std::unique_ptr<SurfaceProducerSurface> surface =
      surface_producer_->ProduceSurface(
          physical_size,
          LayerRasterCacheKey(
              // Root frame has a nullptr layer
              layer ? layer->unique_id() : 0, Matrix()),
          std::make_unique<scenic::EntityNode>(std::move(entity_node)));

  if (!surface) {
    FML_LOG(ERROR) << "Could not acquire a surface from the surface producer "
                      "of size: "
                   << physical_size.width() << "x" << physical_size.height();
    return nullptr;
  }

  auto image = surface->GetImage();

  // Enqueue the paint task.
  paint_tasks_.push_back({.surface = std::move(surface),
                          .left = paint_bounds.left(),
                          .top = paint_bounds.top(),
                          .scale_x = scale_x,
                          .scale_y = scale_y,
                          .background_color = color,
                          .layers = std::move(paint_layers)});
  return image;
}

std::vector<
    std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>>
SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) {
  TRACE_EVENT0("flutter", "SceneUpdateContext::ExecutePaintTasks");
  std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces_to_submit;
  for (auto& task : paint_tasks_) {
    FML_DCHECK(task.surface);
    SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas();
    Layer::PaintContext context = {canvas,
                                   canvas,
                                   frame.gr_context(),
                                   nullptr,
                                   frame.context().raster_time(),
                                   frame.context().ui_time(),
                                   frame.context().texture_registry(),
                                   &frame.context().raster_cache(),
                                   false,
                                   frame_physical_depth_,
                                   frame_device_pixel_ratio_};
    canvas->restoreToCount(1);
    canvas->save();
    canvas->clear(task.background_color);
    canvas->scale(task.scale_x, task.scale_y);
    canvas->translate(-task.left, -task.top);
    for (Layer* layer : task.layers) {
      layer->Paint(context);
    }
    surfaces_to_submit.emplace_back(std::move(task.surface));
  }
  paint_tasks_.clear();
  alpha_ = 1.f;
  topmost_global_scenic_elevation_ = kScenicZElevationBetweenLayers;
  scenic_elevation_ = 0.f;
  return surfaces_to_submit;
}

SceneUpdateContext::Entity::Entity(SceneUpdateContext& context)
    : context_(context),
      previous_entity_(context.top_entity_),
      entity_node_(context.session()) {
  if (previous_entity_)
    previous_entity_->embedder_node().AddChild(entity_node_);
  context.top_entity_ = this;
}

SceneUpdateContext::Entity::~Entity() {
  FML_DCHECK(context_.top_entity_ == this);
  context_.top_entity_ = previous_entity_;
}

SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
                                         const SkMatrix& transform)
    : Entity(context),
      previous_scale_x_(context.top_scale_x_),
      previous_scale_y_(context.top_scale_y_) {
  entity_node().SetLabel("flutter::Transform");
  if (!transform.isIdentity()) {
    // TODO(SCN-192): The perspective and shear components in the matrix
    // are not handled correctly.
    MatrixDecomposition decomposition(transform);
    if (decomposition.IsValid()) {
      // Don't allow clients to control the z dimension; we control that
      // instead to make sure layers appear in proper order.
      entity_node().SetTranslation(decomposition.translation().x(),  //
                                   decomposition.translation().y(),  //
                                   0.f                               //
      );

      entity_node().SetScale(decomposition.scale().x(),  //
                             decomposition.scale().y(),  //
                             1.f                         //
      );
      context.top_scale_x_ *= decomposition.scale().x();
      context.top_scale_y_ *= decomposition.scale().y();

      entity_node().SetRotation(decomposition.rotation().fData[0],  //
                                decomposition.rotation().fData[1],  //
                                decomposition.rotation().fData[2],  //
                                decomposition.rotation().fData[3]   //
      );
    }
  }
}

SceneUpdateContext::Transform::Transform(SceneUpdateContext& context,
                                         float scale_x,
                                         float scale_y,
                                         float scale_z)
    : Entity(context),
      previous_scale_x_(context.top_scale_x_),
      previous_scale_y_(context.top_scale_y_) {
  entity_node().SetLabel("flutter::Transform");
  if (scale_x != 1.f || scale_y != 1.f || scale_z != 1.f) {
    entity_node().SetScale(scale_x, scale_y, scale_z);
    context.top_scale_x_ *= scale_x;
    context.top_scale_y_ *= scale_y;
  }
}

SceneUpdateContext::Transform::~Transform() {
  context().top_scale_x_ = previous_scale_x_;
  context().top_scale_y_ = previous_scale_y_;
}

SceneUpdateContext::Frame::Frame(SceneUpdateContext& context,
                                 const SkRRect& rrect,
                                 SkColor color,
                                 SkAlpha opacity,
                                 std::string label,
                                 float z_translation,
                                 Layer* layer)
    : Entity(context),
      rrect_(rrect),
      color_(color),
      opacity_(opacity),
      opacity_node_(context.session()),
      paint_bounds_(SkRect::MakeEmpty()),
      layer_(layer) {
  entity_node().SetLabel(label);
  entity_node().SetTranslation(0.f, 0.f, z_translation);
  entity_node().AddChild(opacity_node_);
  // Scenic currently lacks an API to enable rendering of alpha channel; alpha
  // channels are only rendered if there is a OpacityNode higher in the tree
  // with opacity != 1. For now, clamp to a infinitesimally smaller value than
  // 1, which does not cause visual problems in practice.
  opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f));
}

SceneUpdateContext::Frame::~Frame() {
  // We don't need a shape if the frame is zero size.
  if (rrect_.isEmpty())
    return;

  // isEmpty should account for this, but we are adding these experimental
  // checks to validate if this is the root cause for b/144933519.
  if (std::isnan(rrect_.width()) || std::isnan(rrect_.height())) {
    FML_LOG(ERROR) << "Invalid RoundedRectangle";
    return;
  }

  // Add a part which represents the frame's geometry for clipping purposes
  context().CreateFrame(std::move(entity_node()), rrect_, color_, opacity_,
                        paint_bounds_, std::move(paint_layers_), layer_);
}

void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) {
  FML_DCHECK(layer->needs_painting());
  paint_layers_.push_back(layer);
  paint_bounds_.join(layer->paint_bounds());
}

SceneUpdateContext::Clip::Clip(SceneUpdateContext& context,
                               const SkRect& shape_bounds)
    : Entity(context) {
  entity_node().SetLabel("flutter::Clip");
  SetEntityNodeClipPlanes(entity_node(), shape_bounds);
}

}  // namespace flutter
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2004 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commericial and non commericial
 * applications, as long as this copyright notice is maintained.
 *
 * This application 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.
 *
*/

/* file:        src/osgPlugins/trans/ReaderWriterTRANS.cpp
 * author:      Mike Weiblen http://mew.cx/ 2004-07-15
 * copyright:   (C) 2004 Michael Weiblen
 * license:     OpenSceneGraph Public License (OSGPL)
*/

#include <osg/Notify>
#include <osg/Matrix>
#include <osg/MatrixTransform>

#include <osgDB/ReaderWriter>
#include <osgDB/FileNameUtils>
#include <osgDB/Registry>
#include <osgDB/ReadFile>

#include <stdio.h>

#define EXTENSION_NAME "trans"

static bool getFilenameAndParams(const std::string& input, std::string& filename, std::string& params)
{
    // find the start of the params list, accounting for nesting of [] and () brackets,
    // note, we are working backwards.
    int noNestedBrackets = 0;
    std::string::size_type pos = input.size();
    for(; pos>0; )
    {
        --pos;
        char c = input[pos];
        if (c==']') ++noNestedBrackets;
        else if (c=='[') --noNestedBrackets;
        else if (c==')') ++noNestedBrackets;
        else if (c=='(') --noNestedBrackets;
        else if (c=='.' && noNestedBrackets==0) break;
    }

    // get the next "extension", which actually contains the pseudo-loader parameters
    params = input.substr(pos+1, std::string::npos );
    if( params.empty() )
    {
        osg::notify(osg::WARN) << "Missing parameters for " EXTENSION_NAME " pseudo-loader" << std::endl;
        return false;
    }

    // clear the params sting of any brackets.
    std::string::size_type params_pos = params.size();
    for(; params_pos>0; )
    {
        --params_pos;
        char c = params[params_pos];
        if (c==']' || c=='[' || c==')' || c=='(')
        {
            params.erase(params_pos,1);
        }
    }

    // strip the "params extension", which must leave a sub-filename.
    filename = input.substr(0, pos );

    return true;
}

///////////////////////////////////////////////////////////////////////////

/**
 * An OSG reader plugin for the ".trans" pseudo-loader, which inserts a
 * translation transform above the loaded geometry.
 * This pseudo-loader make it simple to change the origin of a saved model
 * by specifying a correcting translation as part of the filename.
 *
 * Usage: <modelfile.ext>.<tx>,<ty>,<tz>.globe
 * where:
 *      <modelfile.ext> = an model filename.
 *      <tx> = translation along the X axis.
 *      <ty> = translation along the Y axis.
 *      <tz> = translation along the Z axis.
 *
 * example: osgviewer cow.osg.25,0,0.trans cessna.osg
 */

class ReaderWriterTRANS : public osgDB::ReaderWriter
{
public:
    ReaderWriterTRANS()
    {
        supportsExtension(EXTENSION_NAME,"Translation Psuedo loader.");
    }

    virtual const char* className() const { return "translation pseudo-loader"; }

    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
    {
        std::string ext = osgDB::getLowerCaseFileExtension(fileName);
        if( !acceptsExtension(ext) )
            return ReadResult::FILE_NOT_HANDLED;

        osg::notify(osg::INFO) << "ReaderWriterTRANS( \"" << fileName << "\" )" << std::endl;

        // strip the pseudo-loader extension
        std::string tmpName = osgDB::getNameLessExtension( fileName );

        if (tmpName.empty())
            return ReadResult::FILE_NOT_HANDLED;

        std::string subFileName, params;
        if (!getFilenameAndParams(tmpName, subFileName, params))
        {
            return ReadResult::FILE_NOT_HANDLED;
        }

        if( subFileName.empty())
        {
            osg::notify(osg::WARN) << "Missing subfilename for " EXTENSION_NAME " pseudo-loader" << std::endl;
            return ReadResult::FILE_NOT_HANDLED;
        }

        osg::notify(osg::INFO) << " params = \"" << params << "\"" << std::endl;
        osg::notify(osg::INFO) << " subFileName = \"" << subFileName << "\"" << std::endl;

        float tx, ty, tz;
        int count = sscanf( params.c_str(), "%f,%f,%f", &tx, &ty, &tz );
        if( count != 3 )
        {
            osg::notify(osg::WARN) << "Bad parameters for " EXTENSION_NAME " pseudo-loader: \"" << params << "\"" << std::endl;
            return ReadResult::FILE_NOT_HANDLED;
        }

        // recursively load the subfile.
        osg::Node *node = osgDB::readNodeFile( subFileName, options );
        if( !node )
        {
            // propagate the read failure upwards
            osg::notify(osg::WARN) << "Subfile \"" << subFileName << "\" could not be loaded" << std::endl;
            return ReadResult::FILE_NOT_HANDLED;
        }

        osg::MatrixTransform *xform = new osg::MatrixTransform;
        xform->setDataVariance( osg::Object::STATIC );
        xform->setMatrix( osg::Matrix::translate( tx, ty, tz ) );
        xform->addChild( node );
        return xform;
    }
};

// Add ourself to the Registry to instantiate the reader/writer.
REGISTER_OSGPLUGIN(trans, ReaderWriterTRANS)

/*EOF*/

<commit_msg>Converted osg::notify to OSG_INFO etc.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2004 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commericial and non commericial
 * applications, as long as this copyright notice is maintained.
 *
 * This application 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.
 *
*/

/* file:        src/osgPlugins/trans/ReaderWriterTRANS.cpp
 * author:      Mike Weiblen http://mew.cx/ 2004-07-15
 * copyright:   (C) 2004 Michael Weiblen
 * license:     OpenSceneGraph Public License (OSGPL)
*/

#include <osg/Notify>
#include <osg/Matrix>
#include <osg/MatrixTransform>

#include <osgDB/ReaderWriter>
#include <osgDB/FileNameUtils>
#include <osgDB/Registry>
#include <osgDB/ReadFile>

#include <stdio.h>

#define EXTENSION_NAME "trans"

static bool getFilenameAndParams(const std::string& input, std::string& filename, std::string& params)
{
    // find the start of the params list, accounting for nesting of [] and () brackets,
    // note, we are working backwards.
    int noNestedBrackets = 0;
    std::string::size_type pos = input.size();
    for(; pos>0; )
    {
        --pos;
        char c = input[pos];
        if (c==']') ++noNestedBrackets;
        else if (c=='[') --noNestedBrackets;
        else if (c==')') ++noNestedBrackets;
        else if (c=='(') --noNestedBrackets;
        else if (c=='.' && noNestedBrackets==0) break;
    }

    // get the next "extension", which actually contains the pseudo-loader parameters
    params = input.substr(pos+1, std::string::npos );
    if( params.empty() )
    {
        OSG_WARN << "Missing parameters for " EXTENSION_NAME " pseudo-loader" << std::endl;
        return false;
    }

    // clear the params sting of any brackets.
    std::string::size_type params_pos = params.size();
    for(; params_pos>0; )
    {
        --params_pos;
        char c = params[params_pos];
        if (c==']' || c=='[' || c==')' || c=='(')
        {
            params.erase(params_pos,1);
        }
    }

    // strip the "params extension", which must leave a sub-filename.
    filename = input.substr(0, pos );

    return true;
}

///////////////////////////////////////////////////////////////////////////

/**
 * An OSG reader plugin for the ".trans" pseudo-loader, which inserts a
 * translation transform above the loaded geometry.
 * This pseudo-loader make it simple to change the origin of a saved model
 * by specifying a correcting translation as part of the filename.
 *
 * Usage: <modelfile.ext>.<tx>,<ty>,<tz>.globe
 * where:
 *      <modelfile.ext> = an model filename.
 *      <tx> = translation along the X axis.
 *      <ty> = translation along the Y axis.
 *      <tz> = translation along the Z axis.
 *
 * example: osgviewer cow.osg.25,0,0.trans cessna.osg
 */

class ReaderWriterTRANS : public osgDB::ReaderWriter
{
public:
    ReaderWriterTRANS()
    {
        supportsExtension(EXTENSION_NAME,"Translation Psuedo loader.");
    }

    virtual const char* className() const { return "translation pseudo-loader"; }

    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
    {
        std::string ext = osgDB::getLowerCaseFileExtension(fileName);
        if( !acceptsExtension(ext) )
            return ReadResult::FILE_NOT_HANDLED;

        OSG_INFO << "ReaderWriterTRANS( \"" << fileName << "\" )" << std::endl;

        // strip the pseudo-loader extension
        std::string tmpName = osgDB::getNameLessExtension( fileName );

        if (tmpName.empty())
            return ReadResult::FILE_NOT_HANDLED;

        std::string subFileName, params;
        if (!getFilenameAndParams(tmpName, subFileName, params))
        {
            return ReadResult::FILE_NOT_HANDLED;
        }

        if( subFileName.empty())
        {
            OSG_WARN << "Missing subfilename for " EXTENSION_NAME " pseudo-loader" << std::endl;
            return ReadResult::FILE_NOT_HANDLED;
        }

        OSG_INFO << " params = \"" << params << "\"" << std::endl;
        OSG_INFO << " subFileName = \"" << subFileName << "\"" << std::endl;

        float tx, ty, tz;
        int count = sscanf( params.c_str(), "%f,%f,%f", &tx, &ty, &tz );
        if( count != 3 )
        {
            OSG_WARN << "Bad parameters for " EXTENSION_NAME " pseudo-loader: \"" << params << "\"" << std::endl;
            return ReadResult::FILE_NOT_HANDLED;
        }

        // recursively load the subfile.
        osg::Node *node = osgDB::readNodeFile( subFileName, options );
        if( !node )
        {
            // propagate the read failure upwards
            OSG_WARN << "Subfile \"" << subFileName << "\" could not be loaded" << std::endl;
            return ReadResult::FILE_NOT_HANDLED;
        }

        osg::MatrixTransform *xform = new osg::MatrixTransform;
        xform->setDataVariance( osg::Object::STATIC );
        xform->setMatrix( osg::Matrix::translate( tx, ty, tz ) );
        xform->addChild( node );
        return xform;
    }
};

// Add ourself to the Registry to instantiate the reader/writer.
REGISTER_OSGPLUGIN(trans, ReaderWriterTRANS)

/*EOF*/

<|endoftext|>


<commit_before>// Time:  build tree: O(n), query: O(h)
// Space: O(h)

/**
 * Definition of Interval:
 * classs Interval {
 *     int start, end;
 *     Interval(int start, int end) {
 *         this->start = start;
 *         this->end = end;
 *     }
 */

class SegmentTreeMinNode {
public:
    int start, end, min;
    SegmentTreeMinNode *left, *right;
    SegmentTreeMinNode(int start, int end, int min) {
        this->start = start;
        this->end = end;
        this->min = min;
        this->left = this->right = NULL;
    }
};

class Solution {
public:
    /**
     *@param A, queries: Given an integer array and an query list
     *@return: The result list
     */
    vector<int> intervalMinNumber(vector<int> &A, vector<Interval> &queries) {
        vector<int> res;

        // Build segment tree.
        SegmentTreeMinNode *root = build(A, 0, A.size() - 1);

        // Do each query.
        for (const auto& q : queries) {
            res.emplace_back(query(root, q.start, q.end));
        }

        return res;
    }

    // Build segment tree.
    SegmentTreeMinNode *build(vector<int> &A, int start, int end) {
        if (start > end) {
            return nullptr;
        }

        // The root's start and end is given by build method.
        SegmentTreeMinNode *root = new SegmentTreeMinNode(start, end, INT_MAX);

        // If start equals to end, there will be no children for this node.
        if (start == end) {
            root->min = A[start];
            return root;
        }

        // Left child: start=A.left, end=(A.left + A.right) / 2.
        root->left = build(A, start, (start + end) / 2);

        // Right child: start=(A.left + A.right) / 2 + 1, end=A.right.
        root->right = build(A, (start + end) / 2 + 1, end);

        int left_min = root->left != nullptr ? root->left->min : INT_MAX;
        int right_min = root->right != nullptr ? root->right->min : INT_MAX;

        // Update min.
        root->min = min(left_min, right_min);
        return root;
    }

    // Query min in given range.
    int query(SegmentTreeMinNode *root, int start, int end) {
        // Out of range.
        if (root == nullptr || root->start > end || root->end < start) {
            return INT_MAX;
        }

        // Current segment is totally within range [start, end]
        if (root->start >= start && root->end <= end) {
            return root->min;
        }

        int left = query(root->left, start, end);
        int right = query(root->right, start, end);

        // Find min in the children.
        return min(left, right);
    }
};
<commit_msg>Update interval-minimum-number.cpp<commit_after>// Time:  build tree: O(n), query: O(h)
// Space: O(h)

/**
 * Definition of Interval:
 * classs Interval {
 *     int start, end;
 *     Interval(int start, int end) {
 *         this->start = start;
 *         this->end = end;
 *     }
 */

class SegmentTreeMinNode {
public:
    int start, end, min;
    SegmentTreeMinNode *left, *right;
    SegmentTreeMinNode(int start, int end, int min) {
        this->start = start;
        this->end = end;
        this->min = min;
        this->left = this->right = NULL;
    }
};

class Solution {
public:
    /**
     *@param A, queries: Given an integer array and an query list
     *@return: The result list
     */
    vector<int> intervalMinNumber(vector<int> &A, vector<Interval> &queries) {
        vector<int> res;

        // Build segment tree.
        SegmentTreeMinNode *root = build(A, 0, A.size() - 1);

        // Do each query.
        for (const auto& q : queries) {
            res.emplace_back(query(root, q.start, q.end));
        }

        return res;
    }

    // Build segment tree.
    SegmentTreeMinNode *build(vector<int> &A, int start, int end) {
        if (start > end) {
            return nullptr;
        }

        // The root's start and end is given by build method.
        SegmentTreeMinNode *root = new SegmentTreeMinNode(start, end, INT_MAX);

        // If start equals to end, there will be no children for this node.
        if (start == end) {
            root->min = A[start];
            return root;
        }

        // Left child: start=A.left, end=(A.left + A.right) / 2.
        root->left = build(A, start, (start + end) / 2);

        // Right child: start=(A.left + A.right) / 2 + 1, end=A.right.
        root->right = build(A, (start + end) / 2 + 1, end);

        int left_min = root->left != nullptr ? root->left->min : INT_MAX;
        int right_min = root->right != nullptr ? root->right->min : INT_MAX;

        // Update min.
        root->min = min(left_min, right_min);
        return root;
    }

    // Query min in given range.
    int query(SegmentTreeMinNode *root, int start, int end) {
        // Out of range.
        if (root == nullptr || root->start > end || root->end < start) {
            return INT_MAX;
        }

        // Current segment is totally within range [start, end]
        if (root->start >= start && root->end <= end) {
            return root->min;
        }

        const int left = query(root->left, start, end);
        const int right = query(root->right, start, end);

        // Find min in the children.
        return min(left, right);
    }
};
<|endoftext|>


<commit_before>// Copyright 2020 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "iree/base/internal/threading.h"

#include <chrono>
#include <cstring>
#include <thread>

#include "iree/base/internal/atomics.h"
#include "iree/base/internal/synchronization.h"
#include "iree/base/internal/threading_impl.h"  // to test the override list
#include "iree/base/status_cc.h"
#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.h"

namespace {

using iree::Status;

//==============================================================================
// iree_thread_t
//==============================================================================

TEST(ThreadTest, Lifetime) {
  // Default parameters:
  iree_thread_create_params_t params;
  memset(&params, 0, sizeof(params));

  // Our thread: do a bit of math and notify the main test thread when done.
  struct entry_data_t {
    iree_atomic_int32_t value;
    iree_notification_t barrier;
  } entry_data;
  iree_atomic_store_int32(&entry_data.value, 123, iree_memory_order_relaxed);
  iree_notification_initialize(&entry_data.barrier);
  iree_thread_entry_t entry_fn = +[](void* entry_arg) -> int {
    auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
    iree_atomic_fetch_add_int32(&entry_data->value, 1,
                                iree_memory_order_acq_rel);
    iree_notification_post(&entry_data->barrier, IREE_ALL_WAITERS);
    return 0;
  };

  // Create the thread and immediately begin running it.
  iree_thread_t* thread = nullptr;
  IREE_ASSERT_OK(iree_thread_create(entry_fn, &entry_data, params,
                                    iree_allocator_system(), &thread));
  EXPECT_NE(0, iree_thread_id(thread));

  // Wait for the thread to finish.
  iree_notification_await(
      &entry_data.barrier,
      +[](void* entry_arg) -> bool {
        auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
        return iree_atomic_load_int32(&entry_data->value,
                                      iree_memory_order_relaxed) == (123 + 1);
      },
      &entry_data, iree_infinite_timeout());

  // By holding on to the thread object and releasing it here after the thread
  // has finished, we ensure that destruction occurs on the main thread,
  // avoiding data races reported by TSan.
  iree_thread_release(thread);
  iree_notification_deinitialize(&entry_data.barrier);
}

TEST(ThreadTest, CreateSuspended) {
  iree_thread_create_params_t params;
  memset(&params, 0, sizeof(params));
  params.create_suspended = true;

  struct entry_data_t {
    iree_atomic_int32_t value;
    iree_notification_t barrier;
  } entry_data;
  iree_atomic_store_int32(&entry_data.value, 123, iree_memory_order_relaxed);
  iree_notification_initialize(&entry_data.barrier);
  iree_thread_entry_t entry_fn = +[](void* entry_arg) -> int {
    auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
    iree_atomic_fetch_add_int32(&entry_data->value, 1,
                                iree_memory_order_acq_rel);
    iree_notification_post(&entry_data->barrier, IREE_ALL_WAITERS);
    return 0;
  };

  iree_thread_t* thread = nullptr;
  IREE_ASSERT_OK(iree_thread_create(entry_fn, &entry_data, params,
                                    iree_allocator_system(), &thread));
  EXPECT_NE(0, iree_thread_id(thread));

  // NOTE: the thread will not be running and we should not expect a change in
  // the value. I can't think of a good way to test this, though, so we'll just
  // wait a moment here and assume that if the thread was able to run it would
  // have during this wait.
  ASSERT_EQ(123, iree_atomic_load_int32(&entry_data.value,
                                        iree_memory_order_seq_cst));
  std::this_thread::sleep_for(std::chrono::milliseconds(150));
  ASSERT_EQ(123, iree_atomic_load_int32(&entry_data.value,
                                        iree_memory_order_seq_cst));

  // Resume the thread and wait for it to finish its work.
  iree_thread_resume(thread);
  iree_notification_await(
      &entry_data.barrier,
      +[](void* entry_arg) -> bool {
        auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
        return iree_atomic_load_int32(&entry_data->value,
                                      iree_memory_order_relaxed) == (123 + 1);
      },
      &entry_data, iree_infinite_timeout());
  iree_notification_deinitialize(&entry_data.barrier);
  iree_thread_release(thread);
}

// NOTE: testing whether priority took effect is really hard given that on
// certain platforms the priority may not be respected or may be clamped by
// the system. This is here to test the mechanics of the priority override code
// on our side and assumes that if we tell the OS something it respects it.
TEST(ThreadTest, PriorityOverride) {
  iree_thread_create_params_t params;
  memset(&params, 0, sizeof(params));

  struct entry_data_t {
    iree_atomic_int32_t value;
    iree_notification_t barrier;
  } entry_data;
  iree_atomic_store_int32(&entry_data.value, 0, iree_memory_order_relaxed);
  iree_notification_initialize(&entry_data.barrier);
  iree_thread_entry_t entry_fn = +[](void* entry_arg) -> int {
    auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
    iree_atomic_fetch_add_int32(&entry_data->value, 1,
                                iree_memory_order_acq_rel);
    iree_notification_post(&entry_data->barrier, IREE_ALL_WAITERS);
    return 0;
  };

  iree_thread_t* thread = nullptr;
  IREE_ASSERT_OK(iree_thread_create(entry_fn, &entry_data, params,
                                    iree_allocator_system(), &thread));
  EXPECT_NE(0, iree_thread_id(thread));

  // Push a few overrides.
  iree_thread_override_t* override0 = iree_thread_priority_class_override_begin(
      thread, IREE_THREAD_PRIORITY_CLASS_HIGH);
  EXPECT_NE(nullptr, override0);
  iree_thread_override_t* override1 = iree_thread_priority_class_override_begin(
      thread, IREE_THREAD_PRIORITY_CLASS_HIGHEST);
  EXPECT_NE(nullptr, override1);
  iree_thread_override_t* override2 = iree_thread_priority_class_override_begin(
      thread, IREE_THREAD_PRIORITY_CLASS_LOWEST);
  EXPECT_NE(nullptr, override2);

  // Wait for the thread to finish.
  iree_notification_await(
      &entry_data.barrier,
      +[](void* entry_arg) -> bool {
        auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
        return iree_atomic_load_int32(&entry_data->value,
                                      iree_memory_order_relaxed) == 1;
      },
      &entry_data, iree_infinite_timeout());
  iree_notification_deinitialize(&entry_data.barrier);

  // Pop overrides (in opposite order intentionally).
  iree_thread_override_end(override0);
  iree_thread_override_end(override1);
  iree_thread_override_end(override2);

  iree_thread_release(thread);
}

//==============================================================================
// iree_thread_override_list_t
//==============================================================================
// This is an implementation detail but useful to test on its own as it's shared
// across several platform implementations.

TEST(ThreadOverrideListTest, PriorityClass) {
  static iree_thread_t* kThreadSentinel =
      reinterpret_cast<iree_thread_t*>(0x123);
  static iree_thread_priority_class_t current_priority_class =
      IREE_THREAD_PRIORITY_CLASS_NORMAL;
  iree_thread_override_list_t list;
  iree_thread_override_list_initialize(
      +[](iree_thread_t* thread, iree_thread_priority_class_t priority_class) {
        EXPECT_EQ(kThreadSentinel, thread);
        EXPECT_NE(current_priority_class, priority_class);
        current_priority_class = priority_class;
      },
      current_priority_class, iree_allocator_system(), &list);

  // (NORMAL) -> HIGH -> [ignored LOW] -> HIGHEST
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_NORMAL, current_priority_class);
  iree_thread_override_t* override0 = iree_thread_override_list_add(
      &list, kThreadSentinel, IREE_THREAD_PRIORITY_CLASS_HIGH);
  EXPECT_NE(nullptr, override0);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGH, current_priority_class);
  iree_thread_override_t* override1 = iree_thread_override_list_add(
      &list, kThreadSentinel, IREE_THREAD_PRIORITY_CLASS_LOW);
  EXPECT_NE(nullptr, override1);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGH, current_priority_class);
  iree_thread_override_t* override2 = iree_thread_override_list_add(
      &list, kThreadSentinel, IREE_THREAD_PRIORITY_CLASS_HIGHEST);
  EXPECT_NE(nullptr, override2);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGHEST, current_priority_class);

  // Out of order to ensure highest bit sticks:
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGHEST, current_priority_class);
  iree_thread_override_remove_self(override1);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGHEST, current_priority_class);
  iree_thread_override_remove_self(override0);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGHEST, current_priority_class);
  iree_thread_override_remove_self(override2);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_NORMAL, current_priority_class);

  iree_thread_override_list_deinitialize(&list);
}

}  // namespace
<commit_msg>Fix teardown race in threading_test (#9558)<commit_after>// Copyright 2020 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "iree/base/internal/threading.h"

#include <chrono>
#include <cstring>
#include <thread>

#include "iree/base/internal/atomics.h"
#include "iree/base/internal/synchronization.h"
#include "iree/base/internal/threading_impl.h"  // to test the override list
#include "iree/base/status_cc.h"
#include "iree/testing/gtest.h"
#include "iree/testing/status_matchers.h"

namespace {

using iree::Status;

//==============================================================================
// iree_thread_t
//==============================================================================

TEST(ThreadTest, Lifetime) {
  // Default parameters:
  iree_thread_create_params_t params;
  memset(&params, 0, sizeof(params));

  // Our thread: do a bit of math and notify the main test thread when done.
  struct entry_data_t {
    iree_atomic_int32_t value;
    iree_notification_t barrier;
  } entry_data;
  iree_atomic_store_int32(&entry_data.value, 123, iree_memory_order_relaxed);
  iree_notification_initialize(&entry_data.barrier);
  iree_thread_entry_t entry_fn = +[](void* entry_arg) -> int {
    auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
    iree_atomic_fetch_add_int32(&entry_data->value, 1,
                                iree_memory_order_acq_rel);
    iree_notification_post(&entry_data->barrier, IREE_ALL_WAITERS);
    return 0;
  };

  // Create the thread and immediately begin running it.
  iree_thread_t* thread = nullptr;
  IREE_ASSERT_OK(iree_thread_create(entry_fn, &entry_data, params,
                                    iree_allocator_system(), &thread));
  EXPECT_NE(0, iree_thread_id(thread));

  // Wait for the thread to finish.
  iree_notification_await(
      &entry_data.barrier,
      +[](void* entry_arg) -> bool {
        auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
        return iree_atomic_load_int32(&entry_data->value,
                                      iree_memory_order_relaxed) == (123 + 1);
      },
      &entry_data, iree_infinite_timeout());

  // By holding on to the thread object and releasing it here after the thread
  // has finished, we ensure that destruction occurs on the main thread,
  // avoiding data races reported by TSan.
  iree_thread_release(thread);
  iree_notification_deinitialize(&entry_data.barrier);
}

TEST(ThreadTest, CreateSuspended) {
  iree_thread_create_params_t params;
  memset(&params, 0, sizeof(params));
  params.create_suspended = true;

  struct entry_data_t {
    iree_atomic_int32_t value;
    iree_notification_t barrier;
  } entry_data;
  iree_atomic_store_int32(&entry_data.value, 123, iree_memory_order_relaxed);
  iree_notification_initialize(&entry_data.barrier);
  iree_thread_entry_t entry_fn = +[](void* entry_arg) -> int {
    auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
    iree_atomic_fetch_add_int32(&entry_data->value, 1,
                                iree_memory_order_acq_rel);
    iree_notification_post(&entry_data->barrier, IREE_ALL_WAITERS);
    return 0;
  };

  iree_thread_t* thread = nullptr;
  IREE_ASSERT_OK(iree_thread_create(entry_fn, &entry_data, params,
                                    iree_allocator_system(), &thread));
  EXPECT_NE(0, iree_thread_id(thread));

  // NOTE: the thread will not be running and we should not expect a change in
  // the value. I can't think of a good way to test this, though, so we'll just
  // wait a moment here and assume that if the thread was able to run it would
  // have during this wait.
  ASSERT_EQ(123, iree_atomic_load_int32(&entry_data.value,
                                        iree_memory_order_seq_cst));
  std::this_thread::sleep_for(std::chrono::milliseconds(150));
  ASSERT_EQ(123, iree_atomic_load_int32(&entry_data.value,
                                        iree_memory_order_seq_cst));

  // Resume the thread and wait for it to finish its work.
  iree_thread_resume(thread);
  iree_notification_await(
      &entry_data.barrier,
      +[](void* entry_arg) -> bool {
        auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
        return iree_atomic_load_int32(&entry_data->value,
                                      iree_memory_order_relaxed) == (123 + 1);
      },
      &entry_data, iree_infinite_timeout());
  iree_notification_deinitialize(&entry_data.barrier);
  iree_thread_release(thread);
}

// NOTE: testing whether priority took effect is really hard given that on
// certain platforms the priority may not be respected or may be clamped by
// the system. This is here to test the mechanics of the priority override code
// on our side and assumes that if we tell the OS something it respects it.
TEST(ThreadTest, PriorityOverride) {
  iree_thread_create_params_t params;
  memset(&params, 0, sizeof(params));

  struct entry_data_t {
    iree_atomic_int32_t value;
  } entry_data;
  iree_atomic_store_int32(&entry_data.value, 0, iree_memory_order_relaxed);
  iree_thread_entry_t entry_fn = +[](void* entry_arg) -> int {
    auto* entry_data = reinterpret_cast<struct entry_data_t*>(entry_arg);
    iree_atomic_fetch_add_int32(&entry_data->value, 1,
                                iree_memory_order_release);
    return 0;
  };

  iree_thread_t* thread = nullptr;
  IREE_ASSERT_OK(iree_thread_create(entry_fn, &entry_data, params,
                                    iree_allocator_system(), &thread));
  EXPECT_NE(0, iree_thread_id(thread));

  // Push a few overrides.
  iree_thread_override_t* override0 = iree_thread_priority_class_override_begin(
      thread, IREE_THREAD_PRIORITY_CLASS_HIGH);
  EXPECT_NE(nullptr, override0);
  iree_thread_override_t* override1 = iree_thread_priority_class_override_begin(
      thread, IREE_THREAD_PRIORITY_CLASS_HIGHEST);
  EXPECT_NE(nullptr, override1);
  iree_thread_override_t* override2 = iree_thread_priority_class_override_begin(
      thread, IREE_THREAD_PRIORITY_CLASS_LOWEST);
  EXPECT_NE(nullptr, override2);

  // Wait for the thread to finish.
  while (iree_atomic_load_int32(&entry_data.value, iree_memory_order_acquire) !=
         1) {
    iree_thread_yield();
  }

  // Pop overrides (in opposite order intentionally).
  iree_thread_override_end(override0);
  iree_thread_override_end(override1);
  iree_thread_override_end(override2);

  iree_thread_release(thread);
}

//==============================================================================
// iree_thread_override_list_t
//==============================================================================
// This is an implementation detail but useful to test on its own as it's shared
// across several platform implementations.

TEST(ThreadOverrideListTest, PriorityClass) {
  static iree_thread_t* kThreadSentinel =
      reinterpret_cast<iree_thread_t*>(0x123);
  static iree_thread_priority_class_t current_priority_class =
      IREE_THREAD_PRIORITY_CLASS_NORMAL;
  iree_thread_override_list_t list;
  iree_thread_override_list_initialize(
      +[](iree_thread_t* thread, iree_thread_priority_class_t priority_class) {
        EXPECT_EQ(kThreadSentinel, thread);
        EXPECT_NE(current_priority_class, priority_class);
        current_priority_class = priority_class;
      },
      current_priority_class, iree_allocator_system(), &list);

  // (NORMAL) -> HIGH -> [ignored LOW] -> HIGHEST
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_NORMAL, current_priority_class);
  iree_thread_override_t* override0 = iree_thread_override_list_add(
      &list, kThreadSentinel, IREE_THREAD_PRIORITY_CLASS_HIGH);
  EXPECT_NE(nullptr, override0);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGH, current_priority_class);
  iree_thread_override_t* override1 = iree_thread_override_list_add(
      &list, kThreadSentinel, IREE_THREAD_PRIORITY_CLASS_LOW);
  EXPECT_NE(nullptr, override1);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGH, current_priority_class);
  iree_thread_override_t* override2 = iree_thread_override_list_add(
      &list, kThreadSentinel, IREE_THREAD_PRIORITY_CLASS_HIGHEST);
  EXPECT_NE(nullptr, override2);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGHEST, current_priority_class);

  // Out of order to ensure highest bit sticks:
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGHEST, current_priority_class);
  iree_thread_override_remove_self(override1);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGHEST, current_priority_class);
  iree_thread_override_remove_self(override0);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_HIGHEST, current_priority_class);
  iree_thread_override_remove_self(override2);
  ASSERT_EQ(IREE_THREAD_PRIORITY_CLASS_NORMAL, current_priority_class);

  iree_thread_override_list_deinitialize(&list);
}

}  // namespace
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
 */

#include "base/logging.h"
#include "redis_processor_vizd.h"
#include "redis_connection.h"
#include <boost/assign/list_of.hpp>

#include "seqnum_lua.cpp"
#include "delrequest_lua.cpp"
#include "uveupdate_lua.cpp"
#include "uveupdate_st_lua.cpp"
#include "uvedelete_lua.cpp"
#include "expiredgens_lua.cpp"
#include "withdrawgen_lua.cpp"

using std::string;
using std::vector;
using std::pair;
using std::map;
using std::make_pair;
using boost::assign::list_of;

void
RedisProcessorExec::RefreshGenerator(RedisAsyncConnection * rac,
            const std::string &source, const std::string &module,
            const std::string &coll, int timeout) {
    string gkey = string("GENERATOR:") + source + ":" + module;
    std::ostringstream tstr;
    tstr << timeout;

    assert(timeout);
    rac->RedisAsyncArgCmd(NULL,
            list_of(string("SET"))(gkey)(coll)(string("EX"))(tstr.str()));

}

void
RedisProcessorExec::WithdrawGenerator(RedisAsyncConnection * rac,
            const std::string &source, const std::string &module,
            const std::string &coll) {

    string lua_scr(reinterpret_cast<char *>(withdrawgen_lua), withdrawgen_lua_len);
    rac->RedisAsyncArgCmd(NULL,
        list_of(string("EVAL"))(lua_scr)("0")(source)(module)(coll));
}

void
RedisProcessorExec::UVEUpdate(RedisAsyncConnection * rac, RedisProcessorIf *rpi,
                       const std::string &type, const std::string &attr,
                       const std::string &source, const std::string &module,
                       const std::string &key, const std::string &msg,
                       int32_t seq, const std::string &agg,
                       const std::string &hist, int64_t ts) {

    size_t sep = key.find(":");
    string table = key.substr(0, sep);
    std::ostringstream seqstr;
    seqstr << seq;
    std::ostringstream tsstr;
    tsstr << "{\"ts\":" << ts << "}";

    if (agg == "stats") {
        std::string sc,sp,ss;
        int64_t tsbin = (ts / 3600000000) * 3600000000;
        std::ostringstream tsbinstr;
        tsbinstr << tsbin;
        std::string lhist = hist;
        if (hist == "") {
            lhist = string("1");
        }
        ss = string("HISTORY-10:") + key + ":" + source + ":" + module + ":" + type + ":" + attr;
        sc = string("S-3600-TOPVALS:") + key + ":" + source + ":" + module + ":" + type + ":" + attr + ":" + tsbinstr.str();
        sp = string("S-3600-SUMMARY:") + key + ":" + source + ":" + module + ":" + type + ":" + attr + ":" + tsbinstr.str();

        string lua_scr(reinterpret_cast<char *>(uveupdate_st_lua), uveupdate_st_lua_len);
        rac->RedisAsyncArgCmd(rpi,
            list_of(string("EVAL"))(lua_scr)("8")(
                string("TYPES:") + source + ":" + module)(
                string("ORIGINS:") + key)(
                string("TABLE:") + table)(
                string("UVES:") + source + ":" + module + ":" + type)(
                string("VALUES:") + key + ":" + source + ":" + module + ":" + type)(
                ss)(sc)(sp)(
                source)(module)(type)(attr)(key)(seqstr.str())(lhist)(tsstr.str())(msg));

    } else {

        string lua_scr(reinterpret_cast<char *>(uveupdate_lua), uveupdate_lua_len);
        rac->RedisAsyncArgCmd(rpi,
            list_of(string("EVAL"))(lua_scr)("5")(
                string("TYPES:") + source + ":" + module)(
                string("ORIGINS:") + key)(
                string("TABLE:") + table)(
                string("UVES:") + source + ":" + module + ":" + type)(
                string("VALUES:") + key + ":" + source + ":" + module + ":" + type)(
                source)(module)(type)(attr)(key)(seqstr.str())(msg));
    }
}

void
RedisProcessorExec::UVEDelete(RedisAsyncConnection * rac, RedisProcessorIf *rpi,
        const std::string &type,
        const std::string &source, const std::string &module,
        const string &key, const int32_t seq) {

    size_t sep = key.find(":");
    string table = key.substr(0, sep);

    std::ostringstream seqstr;
    seqstr << seq;

    string lua_scr(reinterpret_cast<char *>(uvedelete_lua), uvedelete_lua_len);
    rac->RedisAsyncArgCmd(rpi,
        list_of(string("EVAL"))(lua_scr)("6")(
            string("DEL:") + key + ":" + source + ":" + module + ":" + type + ":" + seqstr.str())(
            string("VALUES:") + key + ":" + source + ":" + module + ":" + type)(
            string("UVES:") + source + ":" + module + ":" + type)(
            string("ORIGINS:") + key)(
            string("TABLE:") + table)(
            string("DELETED"))(
            source)(module)(type)(key));

}

void RedisProcessorIf::ChildSpawn(const vector<RedisProcessorIf *> & vch) {
    for (vector<RedisProcessorIf *>::const_iterator it = vch.begin();
            it != vch.end(); it++) {
        if ((*it)->RedisSend()) {
            childMap_.insert(std::make_pair((*it)->Key(), (void *)NULL));
        }
    }
    replyCount_ = 0;
    if (vch.size() == 0) FinalResult();
}

void RedisProcessorIf::ChildResult(const string& key, void * childRes) {
    map<string,void *>::iterator mapentry = childMap_.find(key);
    assert(mapentry != childMap_.end());

    mapentry->second = childRes;
    if ((size_t)(++replyCount_) == childMap_.size()) {
        FinalResult();
    }
}

DelRequest::DelRequest(RedisAsyncConnection * rac, finFn fn,
        const std::string& source, const std::string& module,
        const std::string& coll, int timeout) :
            rac_(rac), fin_(fn), source_(source), module_(module),
            coll_(coll), timeout_(timeout) {}

string DelRequest::Key() {
    string vkey = "TYPES:" + source_ + ":" + module_;
    return vkey;
}

bool DelRequest::RedisSend() {
    if (!rac_->IsConnUp())
        return false;

    std::ostringstream tstr;
    tstr << timeout_;
    string lua_scr(reinterpret_cast<char *>(delrequest_lua), delrequest_lua_len);
    rac_->RedisAsyncArgCmd(this,
        list_of(string("EVAL"))(lua_scr)("0")(source_)(module_)(coll_)(tstr.str()));
    return true;
}

void DelRequest::ProcessCallback(redisReply *reply) {
    if (reply->type == REDIS_REPLY_INTEGER) {
        res_ = true;
    } else {
        res_ = false;
    }
    FinalResult();
}

void DelRequest::FinalResult() {
    (fin_)(Key(), res_);
    delete this;
}

TypesQuery::TypesQuery(RedisAsyncConnection * rac, finFn fn,
        const std::string& source, const std::string& module,
        const std::string& coll, int timeout) :
            rac_(rac), fin_(fn), source_(source), module_(module),
            coll_(coll), timeout_(timeout) {}

string TypesQuery::Key() {
    string vkey = "TYPES:" + source_ + ":" + module_;
    return vkey;
}

bool TypesQuery::RedisSend() {
    if (!rac_->IsConnUp())
        return false;

    std::ostringstream tstr;
    tstr << timeout_;
    string lua_scr(reinterpret_cast<char *>(seqnum_lua), seqnum_lua_len);
    rac_->RedisAsyncArgCmd(this,
        list_of(string("EVAL"))(lua_scr)("0")(source_)(module_)(coll_)(tstr.str()));
    return true;
}

void TypesQuery::ProcessCallback(redisReply *reply) {

    if (reply->type == REDIS_REPLY_ARRAY) {
        for (uint iter=0; iter < reply->elements; iter+=2) {
            LOG(INFO, "SeqQuery : " << reply->element[iter]->str <<
                    " seq " << reply->element[iter+1]->str);
            res_.insert(make_pair(reply->element[iter]->str,
                    atoi(reply->element[iter+1]->str)));
        }
    }
    FinalResult();
}

void TypesQuery::FinalResult() {
    (fin_)(Key(), res_);
    delete this;
}

struct GenClear : public RedisProcessorIf {
    typedef boost::function<void(const std::string &, bool)> finFn;
    // callback function to parent
    GenClear(RedisAsyncConnection * rac, finFn fn,
            const std::string& source, const std::string& module) :
            rac_(rac), fin_(fn), source_(source), module_(module) {}

    string Key() {
        string vkey = "GENERATOR:" + source_ + ":" + module_ ;
        return vkey;
    }

    bool RedisSend() {

        if (!rac_->IsConnUp())
            return false;
        string lua_scr(reinterpret_cast<char *>(delrequest_lua), delrequest_lua_len);

        return rac_->RedisAsyncArgCmd(this,
            list_of(string("EVAL"))(lua_scr)("0")(source_)(module_)(
                "")("-1"));
    }

    void ProcessCallback(redisReply *reply) {
        res_ = false;

        if (reply->type == REDIS_REPLY_INTEGER) {
            LOG(INFO,"GenClear successful for " << Key());
            res_ = true;
        }
        FinalResult();
    }

    void FinalResult() {
        (fin_)(Key(), res_);
        delete this;
    }

private:
    RedisAsyncConnection * rac_;
    finFn fin_;
    std::string source_;
    std::string module_;
    bool res_;
};

GenCleanupReq::GenCleanupReq (RedisAsyncConnection * rac,
        finFn fn) : rac_(rac), fin_(fn), res_(0) {}

string GenCleanupReq::Key() {
    string vkey = "GENERATOR_CLEANUP";
    return vkey;
}

bool GenCleanupReq::RedisSend() {
    if (!rac_->IsConnUp())
        return false;

    string lua_scr(reinterpret_cast<char *>(expiredgens_lua), expiredgens_lua_len);
    rac_->RedisAsyncArgCmd(this,
        list_of(string("EVAL"))(lua_scr)("0"));
    return true;
}

void GenCleanupReq::ProcessCallback(redisReply *reply) {

    vector<RedisProcessorIf *> vch;

    if (reply->type == REDIS_REPLY_ARRAY) {
        assert((reply->elements % 2) == 0);
        for (uint iter=0; iter < reply->elements; iter+=2) {
            GenClear * gc = new GenClear(rac_,
                boost::bind(&GenCleanupReq::CallbackFromChild, this, _1, _2),
                reply->element[iter]->str, reply->element[iter+1]->str);
            vch.push_back(gc);
            LOG(INFO, "Creating GenClear for " << gc->Key());
        }
    }
    rac_->GetEVM()->io_service()->post(boost::bind(&RedisProcessorIf::ChildSpawn, this, vch));
}

void GenCleanupReq::CallbackFromChild(const std::string & key, bool res) {
    bool  *p = new bool(res);
    ChildResult(key, (void *)p);
}

void GenCleanupReq::FinalResult() {

    for (map<string,void *>::iterator it = childMap_.begin();
            it != childMap_.end(); it++) {
        bool *p = (bool *)it->second;
        if  (*p) res_++;
        delete p;
    }
    if (res_) {
        LOG(INFO, "GenCleanupReq attempted " << childMap_.size() <<
                " succeeded " << res_);
    }
    (fin_)(Key(), res_);
    delete this;
}
<commit_msg>Add ULL postfix to large number constants to avoid compile error on 32-bit systems<commit_after>/*
 * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
 */

#include "base/logging.h"
#include "redis_processor_vizd.h"
#include "redis_connection.h"
#include <boost/assign/list_of.hpp>

#include "seqnum_lua.cpp"
#include "delrequest_lua.cpp"
#include "uveupdate_lua.cpp"
#include "uveupdate_st_lua.cpp"
#include "uvedelete_lua.cpp"
#include "expiredgens_lua.cpp"
#include "withdrawgen_lua.cpp"

using std::string;
using std::vector;
using std::pair;
using std::map;
using std::make_pair;
using boost::assign::list_of;

void
RedisProcessorExec::RefreshGenerator(RedisAsyncConnection * rac,
            const std::string &source, const std::string &module,
            const std::string &coll, int timeout) {
    string gkey = string("GENERATOR:") + source + ":" + module;
    std::ostringstream tstr;
    tstr << timeout;

    assert(timeout);
    rac->RedisAsyncArgCmd(NULL,
            list_of(string("SET"))(gkey)(coll)(string("EX"))(tstr.str()));

}

void
RedisProcessorExec::WithdrawGenerator(RedisAsyncConnection * rac,
            const std::string &source, const std::string &module,
            const std::string &coll) {

    string lua_scr(reinterpret_cast<char *>(withdrawgen_lua), withdrawgen_lua_len);
    rac->RedisAsyncArgCmd(NULL,
        list_of(string("EVAL"))(lua_scr)("0")(source)(module)(coll));
}

void
RedisProcessorExec::UVEUpdate(RedisAsyncConnection * rac, RedisProcessorIf *rpi,
                       const std::string &type, const std::string &attr,
                       const std::string &source, const std::string &module,
                       const std::string &key, const std::string &msg,
                       int32_t seq, const std::string &agg,
                       const std::string &hist, int64_t ts) {

    size_t sep = key.find(":");
    string table = key.substr(0, sep);
    std::ostringstream seqstr;
    seqstr << seq;
    std::ostringstream tsstr;
    tsstr << "{\"ts\":" << ts << "}";

    if (agg == "stats") {
        std::string sc,sp,ss;
        int64_t tsbin = (ts / 3600000000ULL) * 3600000000ULL;
        std::ostringstream tsbinstr;
        tsbinstr << tsbin;
        std::string lhist = hist;
        if (hist == "") {
            lhist = string("1");
        }
        ss = string("HISTORY-10:") + key + ":" + source + ":" + module + ":" + type + ":" + attr;
        sc = string("S-3600-TOPVALS:") + key + ":" + source + ":" + module + ":" + type + ":" + attr + ":" + tsbinstr.str();
        sp = string("S-3600-SUMMARY:") + key + ":" + source + ":" + module + ":" + type + ":" + attr + ":" + tsbinstr.str();

        string lua_scr(reinterpret_cast<char *>(uveupdate_st_lua), uveupdate_st_lua_len);
        rac->RedisAsyncArgCmd(rpi,
            list_of(string("EVAL"))(lua_scr)("8")(
                string("TYPES:") + source + ":" + module)(
                string("ORIGINS:") + key)(
                string("TABLE:") + table)(
                string("UVES:") + source + ":" + module + ":" + type)(
                string("VALUES:") + key + ":" + source + ":" + module + ":" + type)(
                ss)(sc)(sp)(
                source)(module)(type)(attr)(key)(seqstr.str())(lhist)(tsstr.str())(msg));

    } else {

        string lua_scr(reinterpret_cast<char *>(uveupdate_lua), uveupdate_lua_len);
        rac->RedisAsyncArgCmd(rpi,
            list_of(string("EVAL"))(lua_scr)("5")(
                string("TYPES:") + source + ":" + module)(
                string("ORIGINS:") + key)(
                string("TABLE:") + table)(
                string("UVES:") + source + ":" + module + ":" + type)(
                string("VALUES:") + key + ":" + source + ":" + module + ":" + type)(
                source)(module)(type)(attr)(key)(seqstr.str())(msg));
    }
}

void
RedisProcessorExec::UVEDelete(RedisAsyncConnection * rac, RedisProcessorIf *rpi,
        const std::string &type,
        const std::string &source, const std::string &module,
        const string &key, const int32_t seq) {

    size_t sep = key.find(":");
    string table = key.substr(0, sep);

    std::ostringstream seqstr;
    seqstr << seq;

    string lua_scr(reinterpret_cast<char *>(uvedelete_lua), uvedelete_lua_len);
    rac->RedisAsyncArgCmd(rpi,
        list_of(string("EVAL"))(lua_scr)("6")(
            string("DEL:") + key + ":" + source + ":" + module + ":" + type + ":" + seqstr.str())(
            string("VALUES:") + key + ":" + source + ":" + module + ":" + type)(
            string("UVES:") + source + ":" + module + ":" + type)(
            string("ORIGINS:") + key)(
            string("TABLE:") + table)(
            string("DELETED"))(
            source)(module)(type)(key));

}

void RedisProcessorIf::ChildSpawn(const vector<RedisProcessorIf *> & vch) {
    for (vector<RedisProcessorIf *>::const_iterator it = vch.begin();
            it != vch.end(); it++) {
        if ((*it)->RedisSend()) {
            childMap_.insert(std::make_pair((*it)->Key(), (void *)NULL));
        }
    }
    replyCount_ = 0;
    if (vch.size() == 0) FinalResult();
}

void RedisProcessorIf::ChildResult(const string& key, void * childRes) {
    map<string,void *>::iterator mapentry = childMap_.find(key);
    assert(mapentry != childMap_.end());

    mapentry->second = childRes;
    if ((size_t)(++replyCount_) == childMap_.size()) {
        FinalResult();
    }
}

DelRequest::DelRequest(RedisAsyncConnection * rac, finFn fn,
        const std::string& source, const std::string& module,
        const std::string& coll, int timeout) :
            rac_(rac), fin_(fn), source_(source), module_(module),
            coll_(coll), timeout_(timeout) {}

string DelRequest::Key() {
    string vkey = "TYPES:" + source_ + ":" + module_;
    return vkey;
}

bool DelRequest::RedisSend() {
    if (!rac_->IsConnUp())
        return false;

    std::ostringstream tstr;
    tstr << timeout_;
    string lua_scr(reinterpret_cast<char *>(delrequest_lua), delrequest_lua_len);
    rac_->RedisAsyncArgCmd(this,
        list_of(string("EVAL"))(lua_scr)("0")(source_)(module_)(coll_)(tstr.str()));
    return true;
}

void DelRequest::ProcessCallback(redisReply *reply) {
    if (reply->type == REDIS_REPLY_INTEGER) {
        res_ = true;
    } else {
        res_ = false;
    }
    FinalResult();
}

void DelRequest::FinalResult() {
    (fin_)(Key(), res_);
    delete this;
}

TypesQuery::TypesQuery(RedisAsyncConnection * rac, finFn fn,
        const std::string& source, const std::string& module,
        const std::string& coll, int timeout) :
            rac_(rac), fin_(fn), source_(source), module_(module),
            coll_(coll), timeout_(timeout) {}

string TypesQuery::Key() {
    string vkey = "TYPES:" + source_ + ":" + module_;
    return vkey;
}

bool TypesQuery::RedisSend() {
    if (!rac_->IsConnUp())
        return false;

    std::ostringstream tstr;
    tstr << timeout_;
    string lua_scr(reinterpret_cast<char *>(seqnum_lua), seqnum_lua_len);
    rac_->RedisAsyncArgCmd(this,
        list_of(string("EVAL"))(lua_scr)("0")(source_)(module_)(coll_)(tstr.str()));
    return true;
}

void TypesQuery::ProcessCallback(redisReply *reply) {

    if (reply->type == REDIS_REPLY_ARRAY) {
        for (uint iter=0; iter < reply->elements; iter+=2) {
            LOG(INFO, "SeqQuery : " << reply->element[iter]->str <<
                    " seq " << reply->element[iter+1]->str);
            res_.insert(make_pair(reply->element[iter]->str,
                    atoi(reply->element[iter+1]->str)));
        }
    }
    FinalResult();
}

void TypesQuery::FinalResult() {
    (fin_)(Key(), res_);
    delete this;
}

struct GenClear : public RedisProcessorIf {
    typedef boost::function<void(const std::string &, bool)> finFn;
    // callback function to parent
    GenClear(RedisAsyncConnection * rac, finFn fn,
            const std::string& source, const std::string& module) :
            rac_(rac), fin_(fn), source_(source), module_(module) {}

    string Key() {
        string vkey = "GENERATOR:" + source_ + ":" + module_ ;
        return vkey;
    }

    bool RedisSend() {

        if (!rac_->IsConnUp())
            return false;
        string lua_scr(reinterpret_cast<char *>(delrequest_lua), delrequest_lua_len);

        return rac_->RedisAsyncArgCmd(this,
            list_of(string("EVAL"))(lua_scr)("0")(source_)(module_)(
                "")("-1"));
    }

    void ProcessCallback(redisReply *reply) {
        res_ = false;

        if (reply->type == REDIS_REPLY_INTEGER) {
            LOG(INFO,"GenClear successful for " << Key());
            res_ = true;
        }
        FinalResult();
    }

    void FinalResult() {
        (fin_)(Key(), res_);
        delete this;
    }

private:
    RedisAsyncConnection * rac_;
    finFn fin_;
    std::string source_;
    std::string module_;
    bool res_;
};

GenCleanupReq::GenCleanupReq (RedisAsyncConnection * rac,
        finFn fn) : rac_(rac), fin_(fn), res_(0) {}

string GenCleanupReq::Key() {
    string vkey = "GENERATOR_CLEANUP";
    return vkey;
}

bool GenCleanupReq::RedisSend() {
    if (!rac_->IsConnUp())
        return false;

    string lua_scr(reinterpret_cast<char *>(expiredgens_lua), expiredgens_lua_len);
    rac_->RedisAsyncArgCmd(this,
        list_of(string("EVAL"))(lua_scr)("0"));
    return true;
}

void GenCleanupReq::ProcessCallback(redisReply *reply) {

    vector<RedisProcessorIf *> vch;

    if (reply->type == REDIS_REPLY_ARRAY) {
        assert((reply->elements % 2) == 0);
        for (uint iter=0; iter < reply->elements; iter+=2) {
            GenClear * gc = new GenClear(rac_,
                boost::bind(&GenCleanupReq::CallbackFromChild, this, _1, _2),
                reply->element[iter]->str, reply->element[iter+1]->str);
            vch.push_back(gc);
            LOG(INFO, "Creating GenClear for " << gc->Key());
        }
    }
    rac_->GetEVM()->io_service()->post(boost::bind(&RedisProcessorIf::ChildSpawn, this, vch));
}

void GenCleanupReq::CallbackFromChild(const std::string & key, bool res) {
    bool  *p = new bool(res);
    ChildResult(key, (void *)p);
}

void GenCleanupReq::FinalResult() {

    for (map<string,void *>::iterator it = childMap_.begin();
            it != childMap_.end(); it++) {
        bool *p = (bool *)it->second;
        if  (*p) res_++;
        delete p;
    }
    if (res_) {
        LOG(INFO, "GenCleanupReq attempted " << childMap_.size() <<
                " succeeded " << res_);
    }
    (fin_)(Key(), res_);
    delete this;
}
<|endoftext|>