<commit_before>/*********************************************************************
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2013, Unbounded Robotics Inc.
 *  Copyright (c) 2012, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the Willow Garage nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************/

/* Author: Michael Ferguson, Ioan Sucan, E. Gil Jones */

#include <moveit_simple_controller_manager/follow_joint_trajectory_controller_handle.h>
#include <moveit/utils/xmlrpc_casts.h>

using namespace moveit::core;
static const std::string LOGNAME("SimpleControllerManager");

namespace moveit_simple_controller_manager
{
bool FollowJointTrajectoryControllerHandle::sendTrajectory(const moveit_msgs::RobotTrajectory& trajectory)
{
  ROS_DEBUG_STREAM_NAMED(LOGNAME, "new trajectory to " << name_);

  if (!controller_action_client_)
    return false;

  if (!trajectory.multi_dof_joint_trajectory.points.empty())
  {
    ROS_WARN_NAMED(LOGNAME, "%s cannot execute multi-dof trajectories.", name_.c_str());
  }

  if (done_)
    ROS_DEBUG_STREAM_NAMED(LOGNAME, "sending trajectory to " << name_);
  else
    ROS_DEBUG_STREAM_NAMED(LOGNAME, "sending continuation for the currently executed trajectory to " << name_);

  control_msgs::FollowJointTrajectoryGoal goal = goal_template_;
  goal.trajectory = trajectory.joint_trajectory;
  controller_action_client_->sendGoal(
      goal, boost::bind(&FollowJointTrajectoryControllerHandle::controllerDoneCallback, this, _1, _2),
      boost::bind(&FollowJointTrajectoryControllerHandle::controllerActiveCallback, this),
      boost::bind(&FollowJointTrajectoryControllerHandle::controllerFeedbackCallback, this, _1));
  done_ = false;
  last_exec_ = moveit_controller_manager::ExecutionStatus::RUNNING;
  return true;
}

void FollowJointTrajectoryControllerHandle::configure(XmlRpc::XmlRpcValue& config)
{
  if (config.hasMember("path_tolerance"))
    configure(config["path_tolerance"], "path_tolerance", goal_template_.path_tolerance);
  if (config.hasMember("goal_tolerance"))
    configure(config["goal_tolerance"], "goal_tolerance", goal_template_.goal_tolerance);
  if (config.hasMember("goal_time_tolerance"))
    goal_template_.goal_time_tolerance = ros::Duration(parseDouble(config["goal_time_tolerance"]));
}

namespace
{
enum ToleranceVariables
{
  POSITION,
  VELOCITY,
  ACCELERATION
};
template <ToleranceVariables>
double& variable(control_msgs::JointTolerance& msg);

template <>
inline double& variable<POSITION>(control_msgs::JointTolerance& msg)
{
  return msg.position;
}
template <>
inline double& variable<VELOCITY>(control_msgs::JointTolerance& msg)
{
  return msg.velocity;
}
template <>
inline double& variable<ACCELERATION>(control_msgs::JointTolerance& msg)
{
  return msg.acceleration;
}

static std::map<ToleranceVariables, std::string> VAR_NAME = { { POSITION, "position" },
                                                              { VELOCITY, "velocity" },
                                                              { ACCELERATION, "acceleration" } };
static std::map<ToleranceVariables, decltype(&variable<POSITION>)> VAR_ACCESS = { { POSITION, &variable<POSITION> },
                                                                                { VELOCITY, &variable<VELOCITY> },
                                                                                { ACCELERATION,
                                                                                &variable<ACCELERATION> } };

const char* errorCodeToMessage(int error_code)
{
  switch (error_code)
  {
    case control_msgs::FollowJointTrajectoryResult::SUCCESSFUL:
      return "SUCCESSFUL";
    case control_msgs::FollowJointTrajectoryResult::INVALID_GOAL:
      return "INVALID_GOAL";
    case control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS:
      return "INVALID_JOINTS";
    case control_msgs::FollowJointTrajectoryResult::OLD_HEADER_TIMESTAMP:
      return "OLD_HEADER_TIMESTAMP";
    case control_msgs::FollowJointTrajectoryResult::PATH_TOLERANCE_VIOLATED:
      return "PATH_TOLERANCE_VIOLATED";
    case control_msgs::FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED:
      return "GOAL_TOLERANCE_VIOLATED";
    default:
      return "unknown error";
  }
}
}  // namespace

void FollowJointTrajectoryControllerHandle::configure(XmlRpc::XmlRpcValue& config, const std::string& config_name,
                                                      std::vector<control_msgs::JointTolerance>& tolerances)
{
  if (isStruct(config))  // config should be either a struct of position, velocity, acceleration
  {
    for (ToleranceVariables var : { POSITION, VELOCITY, ACCELERATION })
    {
      if (!config.hasMember(VAR_NAME[var]))
        continue;
      XmlRpc::XmlRpcValue values = config[VAR_NAME[var]];
      if (isArray(values, joints_.size()))
      {
        size_t i = 0;
        for (const auto& joint_name : joints_)
          VAR_ACCESS[var](getTolerance(tolerances, joint_name)) = parseDouble(values[i++]);
      }
      else
      {  // use common value for all joints
        double value = parseDouble(values);
        for (const auto& joint_name : joints_)
          VAR_ACCESS[var](getTolerance(tolerances, joint_name)) = value;
      }
    }
  }
  else if (isArray(config))  // or an array of JointTolerance msgs
  {
    for (int i = 0; i < config.size(); ++i)  // NOLINT(modernize-loop-convert)
    {
      control_msgs::JointTolerance& tol = getTolerance(tolerances, config[i]["name"]);
      for (ToleranceVariables var : { POSITION, VELOCITY, ACCELERATION })
      {
        if (!config[i].hasMember(VAR_NAME[var]))
          continue;
        VAR_ACCESS[var](tol) = parseDouble(config[i][VAR_NAME[var]]);
      }
    }
  }
  else
    ROS_WARN_STREAM_NAMED(LOGNAME, "Invalid " << config_name);
}

control_msgs::JointTolerance& FollowJointTrajectoryControllerHandle::getTolerance(
    std::vector<control_msgs::JointTolerance>& tolerances, const std::string& name)
{
  auto it =
      std::lower_bound(tolerances.begin(), tolerances.end(), name,
                       [](const control_msgs::JointTolerance& lhs, const std::string& rhs) { return lhs.name < rhs; });
  if (it == tolerances.cend() || it->name != name)
  {  // insert new entry if not yet available
    it = tolerances.insert(it, control_msgs::JointTolerance());
    it->name = name;
  }
  return *it;
}

void FollowJointTrajectoryControllerHandle::controllerDoneCallback(
    const actionlib::SimpleClientGoalState& state, const control_msgs::FollowJointTrajectoryResultConstPtr& result)
{
  // Output custom error message for FollowJointTrajectoryResult if necessary
  if (!result)
    ROS_WARN_STREAM_NAMED(LOGNAME, "Controller " << name_ << " done, no result returned");
  else if (result->error_code == control_msgs::FollowJointTrajectoryResult::SUCCESSFUL)
    ROS_INFO_STREAM_NAMED(LOGNAME, "Controller " << name_ << " successfully finished");
  else
    ROS_WARN_STREAM_NAMED(LOGNAME, "Controller " << name_ << "failed with error "
                                                 << errorCodeToMessage(result->error_code) << ": "
                                                 << result->error_string);
  finishControllerExecution(state);
}

void FollowJointTrajectoryControllerHandle::controllerActiveCallback()
{
  ROS_DEBUG_STREAM_NAMED(LOGNAME, name_ << " started execution");
}

void FollowJointTrajectoryControllerHandle::controllerFeedbackCallback(
    const control_msgs::FollowJointTrajectoryFeedbackConstPtr& feedback)
{
}

}  // end namespace moveit_simple_controller_manager
<commit_msg>add missing space to log (#1477)<commit_after>/*********************************************************************
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2013, Unbounded Robotics Inc.
 *  Copyright (c) 2012, Willow Garage, Inc.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *   * Neither the name of the Willow Garage nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************/

/* Author: Michael Ferguson, Ioan Sucan, E. Gil Jones */

#include <moveit_simple_controller_manager/follow_joint_trajectory_controller_handle.h>
#include <moveit/utils/xmlrpc_casts.h>

using namespace moveit::core;
static const std::string LOGNAME("SimpleControllerManager");

namespace moveit_simple_controller_manager
{
bool FollowJointTrajectoryControllerHandle::sendTrajectory(const moveit_msgs::RobotTrajectory& trajectory)
{
  ROS_DEBUG_STREAM_NAMED(LOGNAME, "new trajectory to " << name_);

  if (!controller_action_client_)
    return false;

  if (!trajectory.multi_dof_joint_trajectory.points.empty())
  {
    ROS_WARN_NAMED(LOGNAME, "%s cannot execute multi-dof trajectories.", name_.c_str());
  }

  if (done_)
    ROS_DEBUG_STREAM_NAMED(LOGNAME, "sending trajectory to " << name_);
  else
    ROS_DEBUG_STREAM_NAMED(LOGNAME, "sending continuation for the currently executed trajectory to " << name_);

  control_msgs::FollowJointTrajectoryGoal goal = goal_template_;
  goal.trajectory = trajectory.joint_trajectory;
  controller_action_client_->sendGoal(
      goal, boost::bind(&FollowJointTrajectoryControllerHandle::controllerDoneCallback, this, _1, _2),
      boost::bind(&FollowJointTrajectoryControllerHandle::controllerActiveCallback, this),
      boost::bind(&FollowJointTrajectoryControllerHandle::controllerFeedbackCallback, this, _1));
  done_ = false;
  last_exec_ = moveit_controller_manager::ExecutionStatus::RUNNING;
  return true;
}

void FollowJointTrajectoryControllerHandle::configure(XmlRpc::XmlRpcValue& config)
{
  if (config.hasMember("path_tolerance"))
    configure(config["path_tolerance"], "path_tolerance", goal_template_.path_tolerance);
  if (config.hasMember("goal_tolerance"))
    configure(config["goal_tolerance"], "goal_tolerance", goal_template_.goal_tolerance);
  if (config.hasMember("goal_time_tolerance"))
    goal_template_.goal_time_tolerance = ros::Duration(parseDouble(config["goal_time_tolerance"]));
}

namespace
{
enum ToleranceVariables
{
  POSITION,
  VELOCITY,
  ACCELERATION
};
template <ToleranceVariables>
double& variable(control_msgs::JointTolerance& msg);

template <>
inline double& variable<POSITION>(control_msgs::JointTolerance& msg)
{
  return msg.position;
}
template <>
inline double& variable<VELOCITY>(control_msgs::JointTolerance& msg)
{
  return msg.velocity;
}
template <>
inline double& variable<ACCELERATION>(control_msgs::JointTolerance& msg)
{
  return msg.acceleration;
}

static std::map<ToleranceVariables, std::string> VAR_NAME = { { POSITION, "position" },
                                                              { VELOCITY, "velocity" },
                                                              { ACCELERATION, "acceleration" } };
static std::map<ToleranceVariables, decltype(&variable<POSITION>)> VAR_ACCESS = { { POSITION, &variable<POSITION> },
                                                                                { VELOCITY, &variable<VELOCITY> },
                                                                                { ACCELERATION,
                                                                                &variable<ACCELERATION> } };

const char* errorCodeToMessage(int error_code)
{
  switch (error_code)
  {
    case control_msgs::FollowJointTrajectoryResult::SUCCESSFUL:
      return "SUCCESSFUL";
    case control_msgs::FollowJointTrajectoryResult::INVALID_GOAL:
      return "INVALID_GOAL";
    case control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS:
      return "INVALID_JOINTS";
    case control_msgs::FollowJointTrajectoryResult::OLD_HEADER_TIMESTAMP:
      return "OLD_HEADER_TIMESTAMP";
    case control_msgs::FollowJointTrajectoryResult::PATH_TOLERANCE_VIOLATED:
      return "PATH_TOLERANCE_VIOLATED";
    case control_msgs::FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED:
      return "GOAL_TOLERANCE_VIOLATED";
    default:
      return "unknown error";
  }
}
}  // namespace

void FollowJointTrajectoryControllerHandle::configure(XmlRpc::XmlRpcValue& config, const std::string& config_name,
                                                      std::vector<control_msgs::JointTolerance>& tolerances)
{
  if (isStruct(config))  // config should be either a struct of position, velocity, acceleration
  {
    for (ToleranceVariables var : { POSITION, VELOCITY, ACCELERATION })
    {
      if (!config.hasMember(VAR_NAME[var]))
        continue;
      XmlRpc::XmlRpcValue values = config[VAR_NAME[var]];
      if (isArray(values, joints_.size()))
      {
        size_t i = 0;
        for (const auto& joint_name : joints_)
          VAR_ACCESS[var](getTolerance(tolerances, joint_name)) = parseDouble(values[i++]);
      }
      else
      {  // use common value for all joints
        double value = parseDouble(values);
        for (const auto& joint_name : joints_)
          VAR_ACCESS[var](getTolerance(tolerances, joint_name)) = value;
      }
    }
  }
  else if (isArray(config))  // or an array of JointTolerance msgs
  {
    for (int i = 0; i < config.size(); ++i)  // NOLINT(modernize-loop-convert)
    {
      control_msgs::JointTolerance& tol = getTolerance(tolerances, config[i]["name"]);
      for (ToleranceVariables var : { POSITION, VELOCITY, ACCELERATION })
      {
        if (!config[i].hasMember(VAR_NAME[var]))
          continue;
        VAR_ACCESS[var](tol) = parseDouble(config[i][VAR_NAME[var]]);
      }
    }
  }
  else
    ROS_WARN_STREAM_NAMED(LOGNAME, "Invalid " << config_name);
}

control_msgs::JointTolerance& FollowJointTrajectoryControllerHandle::getTolerance(
    std::vector<control_msgs::JointTolerance>& tolerances, const std::string& name)
{
  auto it =
      std::lower_bound(tolerances.begin(), tolerances.end(), name,
                       [](const control_msgs::JointTolerance& lhs, const std::string& rhs) { return lhs.name < rhs; });
  if (it == tolerances.cend() || it->name != name)
  {  // insert new entry if not yet available
    it = tolerances.insert(it, control_msgs::JointTolerance());
    it->name = name;
  }
  return *it;
}

void FollowJointTrajectoryControllerHandle::controllerDoneCallback(
    const actionlib::SimpleClientGoalState& state, const control_msgs::FollowJointTrajectoryResultConstPtr& result)
{
  // Output custom error message for FollowJointTrajectoryResult if necessary
  if (!result)
    ROS_WARN_STREAM_NAMED(LOGNAME, "Controller " << name_ << " done, no result returned");
  else if (result->error_code == control_msgs::FollowJointTrajectoryResult::SUCCESSFUL)
    ROS_INFO_STREAM_NAMED(LOGNAME, "Controller " << name_ << " successfully finished");
  else
    ROS_WARN_STREAM_NAMED(LOGNAME, "Controller " << name_ << " failed with error "
                                                 << errorCodeToMessage(result->error_code) << ": "
                                                 << result->error_string);
  finishControllerExecution(state);
}

void FollowJointTrajectoryControllerHandle::controllerActiveCallback()
{
  ROS_DEBUG_STREAM_NAMED(LOGNAME, name_ << " started execution");
}

void FollowJointTrajectoryControllerHandle::controllerFeedbackCallback(
    const control_msgs::FollowJointTrajectoryFeedbackConstPtr& feedback)
{
}

}  // end namespace moveit_simple_controller_manager
<|endoftext|>


<commit_before>/* Copyright (c) 2014 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any purpose
 * with or without fee is hereby granted, provided that the above copyright
 * notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "TimeTrace.h"

namespace RAMCloud {
TimeTrace* TimeTrace::globalTimeTrace;

/**
 * Construct a TimeTrace.
 */
TimeTrace::TimeTrace()
    : events()
    , nextIndex(0)
    , readerActive(false)
{
    // Mark all of the events invalid.
    for (int i = 0; i < BUFFER_SIZE; i++) {
        events[i].message = NULL;
    }
    globalTimeTrace = this;
}

/**
 * Destructor for TimeTrace.
 */
TimeTrace::~TimeTrace()
{
}

/**
 * Record an event in the trace.
 *
 * \param message
 *      A short human-readable string identifying what happened, or the
 *      point in the code where this event was logged. This message is
 *      included in printouts of the time trace. This pointer is stored
 *      in the time trace, so either the string must be static, or the caller
 *      must ensure that its contents will not change over its lifetime
 *      in the trace.
 * \param timestamp
 *      Identifies the time at which the event occurred.
 */
void TimeTrace::record(const char* message, uint64_t timestamp)
{
    if (readerActive) {
        return;
    }
    int current;

    // This loop allocates a slot for the new event and handles
    // synchronization between threads; if there is a race, it may
    // execute multiple times.
    while (1) {
        current = nextIndex;

        // Don't use % to increment current with wraparound: it's too slow.
        int newNext = current + 1;
        if (newNext == BUFFER_SIZE) {
            newNext = 0;
        }
        if (nextIndex.compareExchange(current, newNext) == current) {
            break;
        }
    }

    events[current].timestamp = timestamp;
    events[current].message = message;
}

/**
 * Return a string containing a printout of the records in the trace.
 */
string TimeTrace::getTrace()
{
    string s;
    printInternal(&s);
    return s;
}

/**
 * Print all existing trace records to the system log.
 */
void TimeTrace::printToLog()
{
    printInternal(NULL);
}

/**
 * Discard any existing trace records.
 */
void TimeTrace::reset()
{
    for (int i = 0; i < BUFFER_SIZE; i++) {
        if (events[i].message == NULL) {
            break;
        }
        events[i].message = NULL;
    }
    nextIndex = 0;
}

/**
 * This private method does most of the work for both printToLog and
 * getTrace.
 *
 * \param s
 *      If non-NULL, refers to a string that will hold a printout of the
 *      time trace. If NULL, the trace will be printed on the system log.
 */
void TimeTrace::printInternal(string* s)
{
    readerActive = true;

    // Find the oldest event that we still have (either events[nextIndex],
    // or events[0] if we never completely filled the buffer).
    int i = nextIndex;
    if (events[i].message == NULL) {
        i = 0;
        if (events[0].message == NULL) {
            if (s != NULL) {
                s->append("No time trace events to print");
            } else {
                RAMCLOUD_LOG(NOTICE, "No time trace events to print");
            }
            readerActive = false;
            return;
        }
    }

    // Retrieve a "starting time" so we can print individual event times
    // relative to the starting time.
    uint64_t start = events[i].timestamp;
    double prevTime = 0.0;

    // Each iteration through this loop processes one event from the trace.
    do {
        double ns = Cycles::toSeconds(events[i].timestamp - start) * 1e09;
        if (s != NULL) {
            char buffer[200];
            if (s->length() != 0) {
                s->append("\n");
            }
            snprintf(buffer, sizeof(buffer), "%8.1f ns (+%6.1f ns): %s",
                    ns, ns - prevTime, events[i].message);
            s->append(buffer);
        } else {
            RAMCLOUD_LOG(NOTICE, "%8.1f ns (+%6.1f ns): %s", ns, ns - prevTime,
                    events[i].message);
        }
        i = (i+1)%BUFFER_SIZE;
        prevTime = ns;
        // The NULL test below is only needed for testing.
    } while ((i != nextIndex) && (events[i].message != NULL));
    readerActive = false;
}

} // namespace RAMCloud
<commit_msg>TimeTrace.cc: warn about concurrent log dumps, and skip<commit_after>/* Copyright (c) 2014 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any purpose
 * with or without fee is hereby granted, provided that the above copyright
 * notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "TimeTrace.h"

namespace RAMCloud {
TimeTrace* TimeTrace::globalTimeTrace;

/**
 * Construct a TimeTrace.
 */
TimeTrace::TimeTrace()
    : events()
    , nextIndex(0)
    , readerActive(false)
{
    // Mark all of the events invalid.
    for (int i = 0; i < BUFFER_SIZE; i++) {
        events[i].message = NULL;
    }
    globalTimeTrace = this;
}

/**
 * Destructor for TimeTrace.
 */
TimeTrace::~TimeTrace()
{
}

/**
 * Record an event in the trace.
 *
 * \param message
 *      A short human-readable string identifying what happened, or the
 *      point in the code where this event was logged. This message is
 *      included in printouts of the time trace. This pointer is stored
 *      in the time trace, so either the string must be static, or the caller
 *      must ensure that its contents will not change over its lifetime
 *      in the trace.
 * \param timestamp
 *      Identifies the time at which the event occurred.
 */
void TimeTrace::record(const char* message, uint64_t timestamp)
{
    if (readerActive) {
        return;
    }
    int current;

    // This loop allocates a slot for the new event and handles
    // synchronization between threads; if there is a race, it may
    // execute multiple times.
    while (1) {
        current = nextIndex;

        // Don't use % to increment current with wraparound: it's too slow.
        int newNext = current + 1;
        if (newNext == BUFFER_SIZE) {
            newNext = 0;
        }
        if (nextIndex.compareExchange(current, newNext) == current) {
            break;
        }
    }

    events[current].timestamp = timestamp;
    events[current].message = message;
}

/**
 * Return a string containing a printout of the records in the trace.
 */
string TimeTrace::getTrace()
{
    string s;
    printInternal(&s);
    return s;
}

/**
 * Print all existing trace records to the system log.
 */
void TimeTrace::printToLog()
{
    printInternal(NULL);
}

/**
 * Discard any existing trace records.
 */
void TimeTrace::reset()
{
    for (int i = 0; i < BUFFER_SIZE; i++) {
        if (events[i].message == NULL) {
            break;
        }
        events[i].message = NULL;
    }
    nextIndex = 0;
}

/**
 * This private method does most of the work for both printToLog and
 * getTrace.
 *
 * \param s
 *      If non-NULL, refers to a string that will hold a printout of the
 *      time trace. If NULL, the trace will be printed on the system log.
 */
void TimeTrace::printInternal(string* s)
{
    if (readerActive) {
        RAMCLOUD_LOG(WARNING,
                "printInternal already active; skipping this call");
        return;
    }
    readerActive = true;

    // Find the oldest event that we still have (either events[nextIndex],
    // or events[0] if we never completely filled the buffer).
    int i = nextIndex;
    if (events[i].message == NULL) {
        i = 0;
        if (events[0].message == NULL) {
            if (s != NULL) {
                s->append("No time trace events to print");
            } else {
                RAMCLOUD_LOG(NOTICE, "No time trace events to print");
            }
            readerActive = false;
            return;
        }
    }

    // Retrieve a "starting time" so we can print individual event times
    // relative to the starting time.
    uint64_t start = events[i].timestamp;
    double prevTime = 0.0;

    // Each iteration through this loop processes one event from the trace.
    do {
        double ns = Cycles::toSeconds(events[i].timestamp - start) * 1e09;
        if (s != NULL) {
            char buffer[200];
            if (s->length() != 0) {
                s->append("\n");
            }
            snprintf(buffer, sizeof(buffer), "%8.1f ns (+%6.1f ns): %s",
                    ns, ns - prevTime, events[i].message);
            s->append(buffer);
        } else {
            RAMCLOUD_LOG(NOTICE, "%8.1f ns (+%6.1f ns): %s", ns, ns - prevTime,
                    events[i].message);
        }
        i = (i+1)%BUFFER_SIZE;
        prevTime = ns;
        // The NULL test below is only needed for testing.
    } while ((i != nextIndex) && (events[i].message != NULL));
    readerActive = false;
}

} // namespace RAMCloud
<|endoftext|>


<commit_before><commit_msg>survive "TextBox" having no value<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2013, Mozilla Foundation and contributors
 *
 * 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 "WMFUtils.h"

#include <stdio.h>

#define INITGUID
#include <guiddef.h>

#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "wmcodecdspuuid")
#pragma comment(lib, "mfplat.lib")

void LOG(const char* format, ...)
{
#ifdef WMF_DECODER_LOG
  va_list args;
  va_start(args, format);
  vprintf(format, args);
#endif
}

#ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID
// Some SDK versions don't define the AAC decoder CLSID.
// {32D186A7-218F-4C75-8876-DD77273A8999}
DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99);
#endif

namespace wmf {

#define MFPLAT_FUNC(_func) \
  decltype(::_func)* _func;
#include "WMFSymbols.h"
#undef MFPLAT_FUNC

static bool
LinkMfplat()
{
  static bool sInitDone = false;
  static bool sInitOk = false;
  if (!sInitDone) {
    sInitDone = true;
    auto handle = GetModuleHandleA("mfplat.dll");
#define MFPLAT_FUNC(_func) \
    if (!(_func = (decltype(_func))(GetProcAddress(handle, #_func)))) { \
      return false; \
    }
#include "WMFSymbols.h"
#undef MFPLAT_FUNC
    sInitOk = true;
  }
  return sInitOk;
}

bool
EnsureLibs()
{
  static bool sInitDone = false;
  static bool sInitOk = false;
  if (!sInitDone) {
    sInitOk = LinkMfplat() &&
      !!GetModuleHandleA("msauddecmft.dll") &&
      !!GetModuleHandleA("msmpeg2adec.dll") &&
      !!GetModuleHandleA("msmpeg2vdec.dll");
    sInitDone = true;
  }
  return sInitOk;
}

int32_t
MFOffsetToInt32(const MFOffset& aOffset)
{
  return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
}

// Gets the sub-region of the video frame that should be displayed.
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
HRESULT
GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion)
{
  // Determine if "pan and scan" is enabled for this media. If it is, we
  // only display a region of the video frame, not the entire frame.
  BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);

  // If pan and scan mode is enabled. Try to get the display region.
  HRESULT hr = E_FAIL;
  MFVideoArea videoArea;
  memset(&videoArea, 0, sizeof(MFVideoArea));
  if (panScan) {
    hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE,
                             (UINT8*)&videoArea,
                             sizeof(MFVideoArea),
                             NULL);
  }

  // If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
  // check for a minimimum display aperture.
  if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
    hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
                             (UINT8*)&videoArea,
                             sizeof(MFVideoArea),
                             NULL);
  }

  if (hr == MF_E_ATTRIBUTENOTFOUND) {
    // Minimum display aperture is not set, for "backward compatibility with
    // some components", check for a geometric aperture.
    hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
                             (UINT8*)&videoArea,
                             sizeof(MFVideoArea),
                             NULL);
  }

  if (SUCCEEDED(hr)) {
    // The media specified a picture region, return it.
    aOutPictureRegion = IntRect(MFOffsetToInt32(videoArea.OffsetX),
                                  MFOffsetToInt32(videoArea.OffsetY),
                                  videoArea.Area.cx,
                                  videoArea.Area.cy);
    return S_OK;
  }

  // No picture region defined, fall back to using the entire video area.
  UINT32 width = 0, height = 0;
  hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
  ENSURE(SUCCEEDED(hr), hr);
  aOutPictureRegion = IntRect(0, 0, width, height);
  return S_OK;
}

HRESULT
GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
{
  // Try to get the default stride from the media type.
  HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride);
  if (SUCCEEDED(hr)) {
    return S_OK;
  }

  // Stride attribute not set, calculate it.
  GUID subtype = GUID_NULL;
  uint32_t width = 0;
  uint32_t height = 0;

  hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
  ENSURE(SUCCEEDED(hr), hr);

  hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
  ENSURE(SUCCEEDED(hr), hr);

  hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride));
  ENSURE(SUCCEEDED(hr), hr);

  return hr;
}

void dump(const uint8_t* data, uint32_t len, const char* filename)
{
  FILE* f = 0;
  fopen_s(&f, filename, "wb");
  fwrite(data, len, 1, f);
  fclose(f);
}

HRESULT
CreateMFT(const CLSID& clsid,
          const char* aDllName,
          CComPtr<IMFTransform>& aOutMFT)
{
  HMODULE module = ::GetModuleHandleA(aDllName);
  if (!module) {
    LOG("Failed to get %S\n", aDllName);
    return E_FAIL;
  }

  typedef HRESULT (WINAPI* DllGetClassObjectFnPtr)(const CLSID& clsid,
                                                   const IID& iid,
                                                   void** object);

  DllGetClassObjectFnPtr GetClassObjPtr =
    reinterpret_cast<DllGetClassObjectFnPtr>(GetProcAddress(module, "DllGetClassObject"));
  if (!GetClassObjPtr) {
    LOG("Failed to get DllGetClassObject\n");
    return E_FAIL;
  }

  CComPtr<IClassFactory> classFactory;
  HRESULT hr = GetClassObjPtr(clsid,
                              __uuidof(IClassFactory),
                              reinterpret_cast<void**>(static_cast<IClassFactory**>(&classFactory)));
  if (FAILED(hr)) {
    LOG("Failed to get H264 IClassFactory\n");
    return E_FAIL;
  }

  hr = classFactory->CreateInstance(NULL,
                                    __uuidof(IMFTransform),
                                    reinterpret_cast<void**>(static_cast<IMFTransform**>(&aOutMFT)));
  if (FAILED(hr)) {
    LOG("Failed to get create MFT\n");
    return E_FAIL;
  }

  return S_OK;
}

} // namespace
<commit_msg>Look for either msauddecmft.dll or msmpeg2adec.dll rather than both when deciding whether WMF can decode, as Win7 only supports one.<commit_after>/*
 * Copyright 2013, Mozilla Foundation and contributors
 *
 * 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 "WMFUtils.h"

#include <stdio.h>

#define INITGUID
#include <guiddef.h>

#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "wmcodecdspuuid")
#pragma comment(lib, "mfplat.lib")

void LOG(const char* format, ...)
{
#ifdef WMF_DECODER_LOG
  va_list args;
  va_start(args, format);
  vprintf(format, args);
#endif
}

#ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID
// Some SDK versions don't define the AAC decoder CLSID.
// {32D186A7-218F-4C75-8876-DD77273A8999}
DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99);
#endif

namespace wmf {

#define MFPLAT_FUNC(_func) \
  decltype(::_func)* _func;
#include "WMFSymbols.h"
#undef MFPLAT_FUNC

static bool
LinkMfplat()
{
  static bool sInitDone = false;
  static bool sInitOk = false;
  if (!sInitDone) {
    sInitDone = true;
    auto handle = GetModuleHandleA("mfplat.dll");
#define MFPLAT_FUNC(_func) \
    if (!(_func = (decltype(_func))(GetProcAddress(handle, #_func)))) { \
      return false; \
    }
#include "WMFSymbols.h"
#undef MFPLAT_FUNC
    sInitOk = true;
  }
  return sInitOk;
}

bool
EnsureLibs()
{
  static bool sInitDone = false;
  static bool sInitOk = false;
  if (!sInitDone) {
    // Note: For AAC decoding, we need to use msauddecmft.dll on Win8,
    // and msmpeg2adec.dll on earlier Windows. So if we have at least
    // one of these, assume we can decode.
    sInitOk = LinkMfplat() &&
      (!!GetModuleHandleA("msauddecmft.dll") || !!GetModuleHandleA("msmpeg2adec.dll")) &&
      !!GetModuleHandleA("msmpeg2vdec.dll");
    sInitDone = true;
  }
  return sInitOk;
}

int32_t
MFOffsetToInt32(const MFOffset& aOffset)
{
  return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
}

// Gets the sub-region of the video frame that should be displayed.
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
HRESULT
GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion)
{
  // Determine if "pan and scan" is enabled for this media. If it is, we
  // only display a region of the video frame, not the entire frame.
  BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);

  // If pan and scan mode is enabled. Try to get the display region.
  HRESULT hr = E_FAIL;
  MFVideoArea videoArea;
  memset(&videoArea, 0, sizeof(MFVideoArea));
  if (panScan) {
    hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE,
                             (UINT8*)&videoArea,
                             sizeof(MFVideoArea),
                             NULL);
  }

  // If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
  // check for a minimimum display aperture.
  if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
    hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
                             (UINT8*)&videoArea,
                             sizeof(MFVideoArea),
                             NULL);
  }

  if (hr == MF_E_ATTRIBUTENOTFOUND) {
    // Minimum display aperture is not set, for "backward compatibility with
    // some components", check for a geometric aperture.
    hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
                             (UINT8*)&videoArea,
                             sizeof(MFVideoArea),
                             NULL);
  }

  if (SUCCEEDED(hr)) {
    // The media specified a picture region, return it.
    aOutPictureRegion = IntRect(MFOffsetToInt32(videoArea.OffsetX),
                                  MFOffsetToInt32(videoArea.OffsetY),
                                  videoArea.Area.cx,
                                  videoArea.Area.cy);
    return S_OK;
  }

  // No picture region defined, fall back to using the entire video area.
  UINT32 width = 0, height = 0;
  hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
  ENSURE(SUCCEEDED(hr), hr);
  aOutPictureRegion = IntRect(0, 0, width, height);
  return S_OK;
}

HRESULT
GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
{
  // Try to get the default stride from the media type.
  HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride);
  if (SUCCEEDED(hr)) {
    return S_OK;
  }

  // Stride attribute not set, calculate it.
  GUID subtype = GUID_NULL;
  uint32_t width = 0;
  uint32_t height = 0;

  hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
  ENSURE(SUCCEEDED(hr), hr);

  hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
  ENSURE(SUCCEEDED(hr), hr);

  hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, (LONG*)(aOutStride));
  ENSURE(SUCCEEDED(hr), hr);

  return hr;
}

void dump(const uint8_t* data, uint32_t len, const char* filename)
{
  FILE* f = 0;
  fopen_s(&f, filename, "wb");
  fwrite(data, len, 1, f);
  fclose(f);
}

HRESULT
CreateMFT(const CLSID& clsid,
          const char* aDllName,
          CComPtr<IMFTransform>& aOutMFT)
{
  HMODULE module = ::GetModuleHandleA(aDllName);
  if (!module) {
    LOG("Failed to get %S\n", aDllName);
    return E_FAIL;
  }

  typedef HRESULT (WINAPI* DllGetClassObjectFnPtr)(const CLSID& clsid,
                                                   const IID& iid,
                                                   void** object);

  DllGetClassObjectFnPtr GetClassObjPtr =
    reinterpret_cast<DllGetClassObjectFnPtr>(GetProcAddress(module, "DllGetClassObject"));
  if (!GetClassObjPtr) {
    LOG("Failed to get DllGetClassObject\n");
    return E_FAIL;
  }

  CComPtr<IClassFactory> classFactory;
  HRESULT hr = GetClassObjPtr(clsid,
                              __uuidof(IClassFactory),
                              reinterpret_cast<void**>(static_cast<IClassFactory**>(&classFactory)));
  if (FAILED(hr)) {
    LOG("Failed to get H264 IClassFactory\n");
    return E_FAIL;
  }

  hr = classFactory->CreateInstance(NULL,
                                    __uuidof(IMFTransform),
                                    reinterpret_cast<void**>(static_cast<IMFTransform**>(&aOutMFT)));
  if (FAILED(hr)) {
    LOG("Failed to get create MFT\n");
    return E_FAIL;
  }

  return S_OK;
}

} // namespace
<|endoftext|>


<commit_before>#include "Zenderer/Zenderer.hpp"

using zen::util::zLog;
using zen::util::LogMode;

using zen::sfx::zAudioManager;

bool zen::Init()
{
    // Seed random number generator.
    srand(time(nullptr));

    if(!zLog::GetEngineLog().Init())
        return false;

    zLog& Log = zLog::GetEngineLog();

    Log << zLog::endl;
    Log << Log.SetMode(LogMode::ZEN_INFO) << Log.SetSystem("Zenderer")
        << "Initializing Zenderer." << zLog::endl;

    Log << "Initializing GLFW: ";
    if(glfwInit() == GL_FALSE)
    {
        Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED." << zLog::endl;
        return false;
    }
    else
    {
        Log << "SUCCESS." << zLog::endl;
    }

    Log << "Initializing OpenAL: ";
    if(!zAudioManager::Init())
    {
        Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED." << zLog::endl;
        return false;
    }
    else
    {
        Log << "SUCCESS." << util::zLog::endl;
    }

    Log << "Initializing FreeType: ";
    if(!gui::zFontLibrary::InitFreetype().IsInit())
    {
        Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED." << zLog::endl;
        return false;
    }
    else
    {
        Log << "SUCCESS." << util::zLog::endl;
    }

    Log << "Initializing ";

#ifdef _WIN32
    Log << "WinSock: ";
#else
    Log << "socket API: ";
#endif // _WIN32
    if(!net::zSocket::InitializeLibrary())
    {
        Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED." << zLog::endl;
        return false;
    }
    else
    {
        Log << "SUCCESS." << util::zLog::endl;
    }

    for(auto& i : zSubsystem::sp_allSystems)
    {
        Log << "Initializing global subsystem (" << i->GetName() << "): ";
        if(!i->Init())
        {
            i->Destroy();
            Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED."
                << zLog::endl;
            return false;
        }
        else
        {
            Log << "SUCCESS." << zLog::endl;
        }
    }

    return true;
}

void zen::Quit()
{
    zLog& Log = zLog::GetEngineLog();

    Log << Log.SetMode(LogMode::ZEN_INFO) << Log.SetSystem("Zenderer")
        << "Destroying components." << zLog::endl;

    for(auto it  = zen::zSubsystem::sp_allSystems.rbegin();
             it != zen::zSubsystem::sp_allSystems.rend();
           ++it)
    {
        auto& i = *(*it);
        Log << Log.SetMode(LogMode::ZEN_INFO)
            << Log.SetSystem(i.GetName())
            << "Destroying component." << zLog::endl;

        if(!i.Destroy())
        {
            Log.SetMode(LogMode::ZEN_ERROR);
            Log << "Failed to destroy component." << zLog::endl;
        }
    }

    Log << Log.SetMode(LogMode::ZEN_INFO) << Log.SetSystem("Zenderer")
        << "Destroying OpenGL components." << zLog::endl;

    for(auto it  = zen::gfxcore::zGLSubsystem::sp_allGLSystems.rbegin();
             it != zen::gfxcore::zGLSubsystem::sp_allGLSystems.rend();
           ++it)
    {
        auto& sys = *it;
        Log << Log.SetMode(LogMode::ZEN_INFO)
            << Log.SetSystem(sys->GetName())
            << "Destroying component." << zLog::endl;

        if(!sys->Destroy())
        {
            Log.SetMode(LogMode::ZEN_ERROR);
            Log << "Failed to destroy component." << zLog::endl;
        }
    }

    glfwTerminate();
    Log.Destroy();
}
<commit_msg>Quit OpenAL on exit.<commit_after>#include "Zenderer/Zenderer.hpp"

using zen::util::zLog;
using zen::util::LogMode;

using zen::sfx::zAudioManager;

bool zen::Init()
{
    // Seed random number generator.
    srand(time(nullptr));

    if(!zLog::GetEngineLog().Init())
        return false;

    zLog& Log = zLog::GetEngineLog();

    Log << zLog::endl;
    Log << Log.SetMode(LogMode::ZEN_INFO) << Log.SetSystem("Zenderer")
        << "Initializing Zenderer." << zLog::endl;

    Log << "Initializing GLFW: ";
    if(glfwInit() == GL_FALSE)
    {
        Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED." << zLog::endl;
        return false;
    }
    else
    {
        Log << "SUCCESS." << zLog::endl;
    }

    Log << "Initializing OpenAL: ";
    if(!zAudioManager::Init())
    {
        Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED." << zLog::endl;
        return false;
    }
    else
    {
        Log << "SUCCESS." << util::zLog::endl;
    }

    Log << "Initializing FreeType: ";
    if(!gui::zFontLibrary::InitFreetype().IsInit())
    {
        Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED." << zLog::endl;
        return false;
    }
    else
    {
        Log << "SUCCESS." << util::zLog::endl;
    }

    Log << "Initializing ";

#ifdef _WIN32
    Log << "WinSock: ";
#else
    Log << "socket API: ";
#endif // _WIN32
    if(!net::zSocket::InitializeLibrary())
    {
        Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED." << zLog::endl;
        return false;
    }
    else
    {
        Log << "SUCCESS." << util::zLog::endl;
    }

    for(auto& i : zSubsystem::sp_allSystems)
    {
        Log << "Initializing global subsystem (" << i->GetName() << "): ";
        if(!i->Init())
        {
            i->Destroy();
            Log << Log.SetMode(LogMode::ZEN_FATAL) << "FAILED."
                << zLog::endl;
            return false;
        }
        else
        {
            Log << "SUCCESS." << zLog::endl;
        }
    }

    return true;
}

void zen::Quit()
{
    zLog& Log = zLog::GetEngineLog();

    Log << Log.SetMode(LogMode::ZEN_INFO) << Log.SetSystem("Zenderer")
        << "Destroying components." << zLog::endl;

    for(auto it  = zen::zSubsystem::sp_allSystems.rbegin();
             it != zen::zSubsystem::sp_allSystems.rend();
           ++it)
    {
        auto& i = *(*it);
        Log << Log.SetMode(LogMode::ZEN_INFO)
            << Log.SetSystem(i.GetName())
            << "Destroying component." << zLog::endl;

        if(!i.Destroy())
        {
            Log.SetMode(LogMode::ZEN_ERROR);
            Log << "Failed to destroy component." << zLog::endl;
        }
    }

    Log << Log.SetMode(LogMode::ZEN_INFO) << Log.SetSystem("Zenderer")
        << "Destroying OpenGL components." << zLog::endl;

    for(auto it  = zen::gfxcore::zGLSubsystem::sp_allGLSystems.rbegin();
             it != zen::gfxcore::zGLSubsystem::sp_allGLSystems.rend();
           ++it)
    {
        auto& sys = *it;
        Log << Log.SetMode(LogMode::ZEN_INFO)
            << Log.SetSystem(sys->GetName())
            << "Destroying component." << zLog::endl;

        if(!sys->Destroy())
        {
            Log.SetMode(LogMode::ZEN_ERROR);
            Log << "Failed to destroy component." << zLog::endl;
        }
    }

    alutExit();
    glfwTerminate();
    Log.Destroy();
}
<|endoftext|>


<commit_before>/**
 *  @copyright Copyright 2016 The J-PET Framework 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 find a copy of the License in the LICENCE file.
 *
 *  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.
 *
 *  @file JPetTaskExecutor.cpp
 */

#include "JPetTaskExecutor.h"
#include <cassert>
#include "../JPetTaskInterface/JPetTaskInterface.h"
#include "../JPetScopeLoader/JPetScopeLoader.h"
#include "../JPetTaskLoader/JPetTaskLoader.h"
#include "../JPetParamGetterAscii/JPetParamGetterAscii.h"
#include "../JPetParamGetterAscii/JPetParamSaverAscii.h"
#include "../JPetLoggerInclude.h"

JPetTaskExecutor::JPetTaskExecutor(TaskGeneratorChain* taskGeneratorChain, int processedFileId, JPetOptions opt) :
  fProcessedFile(processedFileId),
  ftaskGeneratorChain(taskGeneratorChain),
  fOptions(opt)
{
  if (fOptions.isLocalDB()) {
    fParamManager = new JPetParamManager(new JPetParamGetterAscii(fOptions.getLocalDB()));
  } else {
    fParamManager = new JPetParamManager();
  }
  if (taskGeneratorChain) {
    for (auto taskGenerator : *ftaskGeneratorChain) {
      auto task = taskGenerator();
      task->setParamManager(fParamManager); // maybe that should not be here
      fTasks.push_back(task);
    }
  } else {
    ERROR("taskGeneratorChain is null while constructing JPetTaskExecutor");
  }
}

bool JPetTaskExecutor::process()
{
  if(!processFromCmdLineArgs(fProcessedFile)) {
    ERROR("Error in processFromCmdLineArgs");
    return false;
  }
  for (auto currentTask = fTasks.begin(); currentTask != fTasks.end(); currentTask++) {
    JPetOptions::Options currOpts = fOptions.getOptions();
    if (currentTask != fTasks.begin()) {
    /// Ignore the event range options for all but the first task.
      currOpts = JPetOptions::resetEventRange(currOpts);
   /// For all but the first task,
   /// the input path must be changed if
   /// the output path argument -o was given, because the input
   /// data for them will lay in the location defined by -o.
      auto outPath  = currOpts.at("outputPath");
      if (!outPath.empty()) {
        currOpts.at("inputFile") = outPath + JPetCommonTools::extractPathFromFile(currOpts.at("inputFile")) + JPetCommonTools::extractFileNameFromFullPath(currOpts.at("inputFile"));
      }
    }

    INFO(Form("Starting task: %s", dynamic_cast<JPetTaskLoader*>(*currentTask)->getSubTask()->GetName()));
    (*currentTask)->init(currOpts);
    (*currentTask)->exec();
    (*currentTask)->terminate();
    INFO(Form("Finished task: %s", dynamic_cast<JPetTaskLoader*>(*currentTask)->getSubTask()->GetName()));
  }
  return true;
}

void* JPetTaskExecutor::processProxy(void* runner)
{
  assert(runner);
  static_cast<JPetTaskExecutor*>(runner)->process();
  return 0;
}

TThread* JPetTaskExecutor::run()
{
  TThread* thread = new TThread(to_string(fProcessedFile).c_str(), processProxy, (void*)this);
  assert(thread);
  thread->Run();
  return thread;
}

bool JPetTaskExecutor::processFromCmdLineArgs(int)
{
  auto runNum = fOptions.getRunNumber();
  if (runNum >= 0) {
    try {
      fParamManager->fillParameterBank(runNum);
    } catch (...) {
      ERROR("Param bank was not generated correctly.\n The run number used:" + JPetCommonTools::intToString(runNum));
      return false;
    }
    if (fOptions.isLocalDBCreate()) {
      JPetParamSaverAscii saver;
      saver.saveParamBank(fParamManager->getParamBank(), runNum, fOptions.getLocalDBCreate());
    }
  }

  auto inputFileType = fOptions.getInputFileType();
  auto inputFile = fOptions.getInputFile();
  if (inputFileType == JPetOptions::kScope) {
    createScopeTaskAndAddToTaskList();
  } else if (inputFileType == JPetOptions::kHld) {
    long long nevents = fOptions.getTotalEvents();
    unpackFile(inputFile, nevents);
  }
  else if( inputFileType == JPetOptions::kZip){
    INFO( std::string("Unzipping file before unpacking") );
    JPetCommonTools::unzipFile(inputFile);
    long long nevents = fOptions.getTotalEvents();
    INFO( std::string("Unpacking") );
    unpackFile( JPetCommonTools::stripFileNameSuffix( std::string(inputFile) ).c_str(), nevents);
  }

  if(fOptions.getInputFileType() == JPetOptions::kUndefinedFileType)
    ERROR( Form("Unknown file type provided for file: %s", fOptions.getInputFile()) );

  return true;
}

void JPetTaskExecutor::createScopeTaskAndAddToTaskList()
{
  JPetScopeLoader* module = new JPetScopeLoader(new JPetScopeTask("JPetScopeReader", "Process Oscilloscope ASCII data into JPetRecoSignal structures."));
  assert(module);
  module->setParamManager(fParamManager);
  auto scopeFile = fOptions.getScopeConfigFile();
  if (!fParamManager->getParametersFromScopeConfig(scopeFile)) {
    ERROR("Unable to generate Param Bank from Scope Config");
  }
  fTasks.push_front(module);
}

void JPetTaskExecutor::unpackFile(const char* filename, const long long nevents)
{
    if (nevents > 0) {
      fUnpacker.setParams( filename, nevents);
      WARNING(std::string("Even though the range of events was set, only the first ") + JPetCommonTools::intToString(nevents) + std::string(" will be unpacked by the unpacker. \n The unpacker always starts from the beginning of the file."));
    } else {
      fUnpacker.setParams( filename );
    }
    fUnpacker.exec();
}

JPetTaskExecutor::~JPetTaskExecutor()
{
  for (auto & task : fTasks) {
    if (task) {
      delete task;
      task = 0;
    }
  }
}
<commit_msg>Added small comment, that I assume that all zipped files are HLD format<commit_after>/**
 *  @copyright Copyright 2016 The J-PET Framework 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 find a copy of the License in the LICENCE file.
 *
 *  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.
 *
 *  @file JPetTaskExecutor.cpp
 */

#include "JPetTaskExecutor.h"
#include <cassert>
#include "../JPetTaskInterface/JPetTaskInterface.h"
#include "../JPetScopeLoader/JPetScopeLoader.h"
#include "../JPetTaskLoader/JPetTaskLoader.h"
#include "../JPetParamGetterAscii/JPetParamGetterAscii.h"
#include "../JPetParamGetterAscii/JPetParamSaverAscii.h"
#include "../JPetLoggerInclude.h"

JPetTaskExecutor::JPetTaskExecutor(TaskGeneratorChain* taskGeneratorChain, int processedFileId, JPetOptions opt) :
  fProcessedFile(processedFileId),
  ftaskGeneratorChain(taskGeneratorChain),
  fOptions(opt)
{
  if (fOptions.isLocalDB()) {
    fParamManager = new JPetParamManager(new JPetParamGetterAscii(fOptions.getLocalDB()));
  } else {
    fParamManager = new JPetParamManager();
  }
  if (taskGeneratorChain) {
    for (auto taskGenerator : *ftaskGeneratorChain) {
      auto task = taskGenerator();
      task->setParamManager(fParamManager); // maybe that should not be here
      fTasks.push_back(task);
    }
  } else {
    ERROR("taskGeneratorChain is null while constructing JPetTaskExecutor");
  }
}

bool JPetTaskExecutor::process()
{
  if(!processFromCmdLineArgs(fProcessedFile)) {
    ERROR("Error in processFromCmdLineArgs");
    return false;
  }
  for (auto currentTask = fTasks.begin(); currentTask != fTasks.end(); currentTask++) {
    JPetOptions::Options currOpts = fOptions.getOptions();
    if (currentTask != fTasks.begin()) {
    /// Ignore the event range options for all but the first task.
      currOpts = JPetOptions::resetEventRange(currOpts);
   /// For all but the first task,
   /// the input path must be changed if
   /// the output path argument -o was given, because the input
   /// data for them will lay in the location defined by -o.
      auto outPath  = currOpts.at("outputPath");
      if (!outPath.empty()) {
        currOpts.at("inputFile") = outPath + JPetCommonTools::extractPathFromFile(currOpts.at("inputFile")) + JPetCommonTools::extractFileNameFromFullPath(currOpts.at("inputFile"));
      }
    }

    INFO(Form("Starting task: %s", dynamic_cast<JPetTaskLoader*>(*currentTask)->getSubTask()->GetName()));
    (*currentTask)->init(currOpts);
    (*currentTask)->exec();
    (*currentTask)->terminate();
    INFO(Form("Finished task: %s", dynamic_cast<JPetTaskLoader*>(*currentTask)->getSubTask()->GetName()));
  }
  return true;
}

void* JPetTaskExecutor::processProxy(void* runner)
{
  assert(runner);
  static_cast<JPetTaskExecutor*>(runner)->process();
  return 0;
}

TThread* JPetTaskExecutor::run()
{
  TThread* thread = new TThread(to_string(fProcessedFile).c_str(), processProxy, (void*)this);
  assert(thread);
  thread->Run();
  return thread;
}

bool JPetTaskExecutor::processFromCmdLineArgs(int)
{
  auto runNum = fOptions.getRunNumber();
  if (runNum >= 0) {
    try {
      fParamManager->fillParameterBank(runNum);
    } catch (...) {
      ERROR("Param bank was not generated correctly.\n The run number used:" + JPetCommonTools::intToString(runNum));
      return false;
    }
    if (fOptions.isLocalDBCreate()) {
      JPetParamSaverAscii saver;
      saver.saveParamBank(fParamManager->getParamBank(), runNum, fOptions.getLocalDBCreate());
    }
  }

  auto inputFileType = fOptions.getInputFileType();
  auto inputFile = fOptions.getInputFile();
  if (inputFileType == JPetOptions::kScope) {
    createScopeTaskAndAddToTaskList();
  } else if (inputFileType == JPetOptions::kHld) {
    long long nevents = fOptions.getTotalEvents();
    unpackFile(inputFile, nevents);
  }
  //Assumption that only HLD files can be zipped!!!!!! - Sz.N.
  else if( inputFileType == JPetOptions::kZip){
    INFO( std::string("Unzipping file before unpacking") );
    JPetCommonTools::unzipFile(inputFile);
    long long nevents = fOptions.getTotalEvents();
    INFO( std::string("Unpacking") );
    unpackFile( JPetCommonTools::stripFileNameSuffix( std::string(inputFile) ).c_str(), nevents);
  }

  if(fOptions.getInputFileType() == JPetOptions::kUndefinedFileType)
    ERROR( Form("Unknown file type provided for file: %s", fOptions.getInputFile()) );

  return true;
}

void JPetTaskExecutor::createScopeTaskAndAddToTaskList()
{
  JPetScopeLoader* module = new JPetScopeLoader(new JPetScopeTask("JPetScopeReader", "Process Oscilloscope ASCII data into JPetRecoSignal structures."));
  assert(module);
  module->setParamManager(fParamManager);
  auto scopeFile = fOptions.getScopeConfigFile();
  if (!fParamManager->getParametersFromScopeConfig(scopeFile)) {
    ERROR("Unable to generate Param Bank from Scope Config");
  }
  fTasks.push_front(module);
}

void JPetTaskExecutor::unpackFile(const char* filename, const long long nevents)
{
    if (nevents > 0) {
      fUnpacker.setParams( filename, nevents);
      WARNING(std::string("Even though the range of events was set, only the first ") + JPetCommonTools::intToString(nevents) + std::string(" will be unpacked by the unpacker. \n The unpacker always starts from the beginning of the file."));
    } else {
      fUnpacker.setParams( filename );
    }
    fUnpacker.exec();
}

JPetTaskExecutor::~JPetTaskExecutor()
{
  for (auto & task : fTasks) {
    if (task) {
      delete task;
      task = 0;
    }
  }
}
<|endoftext|>


<commit_before>// Required by building CPP dynamic library via Makefile of GPDB.
#define PGDLLIMPORT "C"

extern "C" {
#include "postgres.h"

#include "access/extprotocol.h"
#include "access/xact.h"
#include "catalog/pg_exttable.h"
#include "catalog/pg_proc.h"
#include "fmgr.h"
#include "funcapi.h"
#include "port.h"  //for pg_strncasecmp
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
}

#include "gpreader.h"
#include "gpwriter.h"

/* Do the module magic dance */
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(s3_export);
PG_FUNCTION_INFO_V1(s3_import);

extern "C" {
Datum s3_export(PG_FUNCTION_ARGS);
Datum s3_import(PG_FUNCTION_ARGS);
}

string s3extErrorMessage;

/*
 * To detect the query interruption event (triggered by user), we should
 * consider both QueryCancelPending variable and transaction status. Here
 * QueryCancelPending is not sufficient because it will be reset before the
 * extprotocol last call, then it is hard to distinguish normal exit/finish
 * from abnormal transaction abort.
 *
 * IsAbortInProgress() can also be reset by GPDB while downloading file, we
 * must save the state
 */
static bool queryCancelFlag = false;
bool S3QueryIsAbortInProgress(void) {
    bool queryIsBeingCancelled = QueryCancelPending || IsAbortInProgress();

    // We need a thread-safe way to query and set queryCancelFlag.
    // queryCancelFlag is set to TRUE for the first time when cancel signal is
    // detected. If cancel signal is already captured, value will not be
    // swapped and queryIsCancelledAlready is true.
    bool queryIsCancelledAlready =
        !__sync_bool_compare_and_swap(&queryCancelFlag, false, queryIsBeingCancelled);

    return queryIsBeingCancelled || queryIsCancelledAlready;
}

void *S3Alloc(size_t size) {
    return palloc(size);
}

/*
 * MemoryContext will be free automatically when query is completed or aborted.
 * So no need to call pfree() again unless it is really needed inside query execution.
 * Otherwise might free already cleaned memory context for failed query.
 *
 * Currently download/upload buffer are using memory from MemoryContext.
 */
void S3Free(void *p) {
    // pfree(p);
}

/*
 * Set up the thread signal mask, we don't want to run our signal handlers
 * in downloading and uploading threads.
 */
void MaskThreadSignals() {
    sigset_t sigs;

    if (pthread_equal(main_tid, pthread_self())) {
        S3ERROR("thread_mask is called from main thread!");
        return;
    }

    sigemptyset(&sigs);

    /* make our thread to ignore these signals (which should allow that they be
     * delivered to the main thread) */
    sigaddset(&sigs, SIGHUP);
    sigaddset(&sigs, SIGINT);
    sigaddset(&sigs, SIGTERM);
    sigaddset(&sigs, SIGALRM);
    sigaddset(&sigs, SIGUSR1);
    sigaddset(&sigs, SIGUSR2);

    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
}

/*
 * Detect data format
 * used to set file extension on S3 in gpwriter.
 */
static const char *getFormatStr(FunctionCallInfo fcinfo) {
    Relation rel = EXTPROTOCOL_GET_RELATION(fcinfo);
    ExtTableEntry *exttbl = GetExtTableEntry(rel->rd_id);
    char fmtcode = exttbl->fmtcode;

    if (fmttype_is_text(fmtcode)) return "txt";
    if (fmttype_is_csv(fmtcode)) return "csv";
    if (fmttype_is_avro(fmtcode)) return "avro";
    if (fmttype_is_parquet(fmtcode)) return "parquet";
    return S3_DEFAULT_FORMAT;
}

bool hasHeader = false;

char eolString[EOL_CHARS_MAX_LEN + 1] = "\n";  // LF by default

static void parseFormatOpts(FunctionCallInfo fcinfo) {
    Relation rel = EXTPROTOCOL_GET_RELATION(fcinfo);
    ExtTableEntry *exttbl = GetExtTableEntry(rel->rd_id);

    const char fmtcode = exttbl->fmtcode;
    const char *fmtopts = exttbl->fmtopts;

    // only TEXT and CSV have detailed options
    if (fmttype_is_csv(fmtcode) || fmttype_is_text(fmtcode)) {
        if (strstr(fmtopts, "header") != NULL) {
            hasHeader = true;
        } else {
            hasHeader = false;
        }

        // detect end-of-line terminator
        const char *newline_str = strstr(fmtopts, "newline");
        eolString[0] = '\n';
        eolString[1] = '\0';

        if (newline_str != NULL) {
            const char *first = strchr(newline_str, '\'');
            const char *second = strchr(first + 1, '\'');
            size_t len = second - first - 1;
            char newline[EOL_STRING_MAX_LEN + 1];
            len = len > EOL_STRING_MAX_LEN ? EOL_STRING_MAX_LEN : len;  // defensive line

            strncpy(newline, first + 1, len);
            newline[len] = '\0';
            if (pg_strcasecmp(newline, "crlf") == 0) {
                eolString[0] = '\r';
                eolString[1] = '\n';
                eolString[2] = '\0';
            } else if (pg_strcasecmp(newline, "cr") == 0) {
                eolString[0] = '\r';
                eolString[1] = '\0';
            } else if (pg_strcasecmp(newline, "lf") == 0) {
                eolString[0] = '\n';
                eolString[1] = '\0';
            } else {  // should never come here
                ereport(ERROR, (0, errmsg("invalid value for NEWLINE (%s), "
                                          "valid options are: 'LF', 'CRLF', 'CR'",
                                          newline)));
            }
        }
    }
}

typedef struct gpcloudResHandle {
    GPReader *gpreader;
    GPWriter *gpwriter;

    ResourceOwner owner; /* owner of this handle */

    struct gpcloudResHandle *next;
    struct gpcloudResHandle *prev;
} gpcloudResHandle;

// Linked list of opened "handles", which are allocated in TopMemoryContext, and tracked by resource
// owners.
static gpcloudResHandle *openedResHandles;

static bool isGpcloudResReleaseCallbackRegistered;

static gpcloudResHandle *createGpcloudResHandle(void) {
    gpcloudResHandle *resHandle;

    resHandle = (gpcloudResHandle *)MemoryContextAlloc(TopMemoryContext, sizeof(gpcloudResHandle));
    resHandle->gpreader = NULL;
    resHandle->gpwriter = NULL;

    resHandle->owner = CurrentResourceOwner;
    resHandle->next = openedResHandles;
    resHandle->prev = NULL;

    if (openedResHandles) {
        openedResHandles->prev = resHandle;
    }

    openedResHandles = resHandle;

    return resHandle;
}

static void destroyGpcloudResHandle(gpcloudResHandle *resHandle) {
    if (resHandle == NULL) return;

    /* unlink from linked list first */
    if (resHandle->prev)
        resHandle->prev->next = resHandle->next;
    else
        openedResHandles = resHandle->next;

    if (resHandle->next) {
        resHandle->next->prev = resHandle->prev;
    }

    if (resHandle->gpreader != NULL) {
        if (!reader_cleanup(&resHandle->gpreader)) {
            elog(WARNING, "Failed to cleanup gpcloud extension: %s", s3extErrorMessage.c_str());
        }
    }

    if (resHandle->gpwriter != NULL) {
        if (!writer_cleanup(&resHandle->gpwriter)) {
            elog(WARNING, "Failed to cleanup gpcloud extension: %s", s3extErrorMessage.c_str());
        }
    }

    thread_cleanup();
    pfree(resHandle);
}

/*
 * Close any open handles on abort.
 */
static void gpcloudAbortCallback(ResourceReleasePhase phase, bool isCommit, bool isTopLevel,
                                 void *arg) {
    gpcloudResHandle *curr;
    gpcloudResHandle *next;

    if (phase != RESOURCE_RELEASE_AFTER_LOCKS) return;

    next = openedResHandles;
    while (next) {
        curr = next;
        next = curr->next;

        if (curr->owner == CurrentResourceOwner) {
            if (isCommit)
                elog(WARNING, "gpcloud external table reference leak: %p still referenced", curr);

            destroyGpcloudResHandle(curr);
        }
    }
}

/*
 * Import data into GPDB.
 * invoked by GPDB, be careful with C++ exceptions.
 */
Datum s3_import(PG_FUNCTION_ARGS) {
    /* Must be called via the external table format manager */
    if (!CALLED_AS_EXTPROTOCOL(fcinfo))
        elog(ERROR, "extprotocol_import: not called by external protocol manager");

    /* Get our internal description of the protocol */
    gpcloudResHandle *resHandle = (gpcloudResHandle *)EXTPROTOCOL_GET_USER_CTX(fcinfo);

    /* last call. destroy reader */
    if (EXTPROTOCOL_IS_LAST_CALL(fcinfo)) {
        destroyGpcloudResHandle(resHandle);

        EXTPROTOCOL_SET_USER_CTX(fcinfo, NULL);
        PG_RETURN_INT32(0);
    }

    /* first call. do any desired init */
    if (resHandle == NULL) {
        if (!isGpcloudResReleaseCallbackRegistered) {
            RegisterResourceReleaseCallback(gpcloudAbortCallback, NULL);
            isGpcloudResReleaseCallbackRegistered = true;
        }
        resHandle = createGpcloudResHandle();

        queryCancelFlag = false;
        const char *url_with_options = EXTPROTOCOL_GET_URL(fcinfo);

        // has HEADER? and newline EOL?
        parseFormatOpts(fcinfo);

        thread_setup();

        resHandle->gpreader = reader_init(url_with_options);
        if (!resHandle->gpreader) {
            ereport(ERROR, (0, errmsg("Failed to init gpcloud extension (segid = %d, "
                                      "segnum = %d), please check your "
                                      "configurations and network connection: %s",
                                      s3ext_segid, s3ext_segnum, s3extErrorMessage.c_str())));
        }

        EXTPROTOCOL_SET_USER_CTX(fcinfo, resHandle);
    }

    char *data_buf = EXTPROTOCOL_GET_DATABUF(fcinfo);
    int32 data_len = EXTPROTOCOL_GET_DATALEN(fcinfo);

    if (!reader_transfer_data(resHandle->gpreader, data_buf, data_len)) {
        ereport(ERROR,
                (0, errmsg("s3_import: could not read data: %s", s3extErrorMessage.c_str())));
    }
    PG_RETURN_INT32(data_len);
}

/*
 * Export data out of GPDB.
 * invoked by GPDB, be careful with C++ exceptions.
 */
Datum s3_export(PG_FUNCTION_ARGS) {
    /* Must be called via the external table format manager */
    if (!CALLED_AS_EXTPROTOCOL(fcinfo))
        elog(ERROR, "extprotocol_import: not called by external protocol manager");

    /* Get our internal description of the protocol */
    gpcloudResHandle *resHandle = (gpcloudResHandle *)EXTPROTOCOL_GET_USER_CTX(fcinfo);

    /* last call. destroy writer */
    if (EXTPROTOCOL_IS_LAST_CALL(fcinfo)) {
        destroyGpcloudResHandle(resHandle);

        EXTPROTOCOL_SET_USER_CTX(fcinfo, NULL);
        PG_RETURN_INT32(0);
    }

    /* first call. do any desired init */
    if (resHandle == NULL) {
        if (!isGpcloudResReleaseCallbackRegistered) {
            RegisterResourceReleaseCallback(gpcloudAbortCallback, NULL);
            isGpcloudResReleaseCallbackRegistered = true;
        }
        resHandle = createGpcloudResHandle();

        queryCancelFlag = false;
        const char *url_with_options = EXTPROTOCOL_GET_URL(fcinfo);
        const char *format = getFormatStr(fcinfo);

        thread_setup();

        resHandle->gpwriter = writer_init(url_with_options, format);
        if (!resHandle->gpwriter) {
            ereport(ERROR, (0, errmsg("Failed to init gpcloud extension (segid = %d, "
                                      "segnum = %d), please check your "
                                      "configurations and network connection: %s",
                                      s3ext_segid, s3ext_segnum, s3extErrorMessage.c_str())));
        }

        EXTPROTOCOL_SET_USER_CTX(fcinfo, resHandle);
    }

    char *data_buf = EXTPROTOCOL_GET_DATABUF(fcinfo);
    int32 data_len = EXTPROTOCOL_GET_DATALEN(fcinfo);

    if (!writer_transfer_data(resHandle->gpwriter, data_buf, data_len)) {
        ereport(ERROR,
                (0, errmsg("s3_export: could not write data: %s", s3extErrorMessage.c_str())));
    }

    PG_RETURN_INT32(data_len);
}
<commit_msg>gpcloud: remove PGDLLIMPORT definition<commit_after>extern "C" {
#include "postgres.h"

#include "access/extprotocol.h"
#include "access/xact.h"
#include "catalog/pg_exttable.h"
#include "catalog/pg_proc.h"
#include "fmgr.h"
#include "funcapi.h"
#include "port.h"  //for pg_strncasecmp
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/resowner.h"

/* Do the module magic dance */
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(s3_export);
PG_FUNCTION_INFO_V1(s3_import);

Datum s3_export(PG_FUNCTION_ARGS);
Datum s3_import(PG_FUNCTION_ARGS);
}

#include "gpreader.h"
#include "gpwriter.h"

string s3extErrorMessage;

/*
 * To detect the query interruption event (triggered by user), we should
 * consider both QueryCancelPending variable and transaction status. Here
 * QueryCancelPending is not sufficient because it will be reset before the
 * extprotocol last call, then it is hard to distinguish normal exit/finish
 * from abnormal transaction abort.
 *
 * IsAbortInProgress() can also be reset by GPDB while downloading file, we
 * must save the state
 */
static bool queryCancelFlag = false;
bool S3QueryIsAbortInProgress(void) {
    bool queryIsBeingCancelled = QueryCancelPending || IsAbortInProgress();

    // We need a thread-safe way to query and set queryCancelFlag.
    // queryCancelFlag is set to TRUE for the first time when cancel signal is
    // detected. If cancel signal is already captured, value will not be
    // swapped and queryIsCancelledAlready is true.
    bool queryIsCancelledAlready =
        !__sync_bool_compare_and_swap(&queryCancelFlag, false, queryIsBeingCancelled);

    return queryIsBeingCancelled || queryIsCancelledAlready;
}

void *S3Alloc(size_t size) {
    return palloc(size);
}

/*
 * MemoryContext will be free automatically when query is completed or aborted.
 * So no need to call pfree() again unless it is really needed inside query execution.
 * Otherwise might free already cleaned memory context for failed query.
 *
 * Currently download/upload buffer are using memory from MemoryContext.
 */
void S3Free(void *p) {
    // pfree(p);
}

/*
 * Set up the thread signal mask, we don't want to run our signal handlers
 * in downloading and uploading threads.
 */
void MaskThreadSignals() {
    sigset_t sigs;

    if (pthread_equal(main_tid, pthread_self())) {
        S3ERROR("thread_mask is called from main thread!");
        return;
    }

    sigemptyset(&sigs);

    /* make our thread to ignore these signals (which should allow that they be
     * delivered to the main thread) */
    sigaddset(&sigs, SIGHUP);
    sigaddset(&sigs, SIGINT);
    sigaddset(&sigs, SIGTERM);
    sigaddset(&sigs, SIGALRM);
    sigaddset(&sigs, SIGUSR1);
    sigaddset(&sigs, SIGUSR2);

    pthread_sigmask(SIG_BLOCK, &sigs, NULL);
}

/*
 * Detect data format
 * used to set file extension on S3 in gpwriter.
 */
static const char *getFormatStr(FunctionCallInfo fcinfo) {
    Relation rel = EXTPROTOCOL_GET_RELATION(fcinfo);
    ExtTableEntry *exttbl = GetExtTableEntry(rel->rd_id);
    char fmtcode = exttbl->fmtcode;

    if (fmttype_is_text(fmtcode)) return "txt";
    if (fmttype_is_csv(fmtcode)) return "csv";
    if (fmttype_is_avro(fmtcode)) return "avro";
    if (fmttype_is_parquet(fmtcode)) return "parquet";
    return S3_DEFAULT_FORMAT;
}

bool hasHeader = false;

char eolString[EOL_CHARS_MAX_LEN + 1] = "\n";  // LF by default

static void parseFormatOpts(FunctionCallInfo fcinfo) {
    Relation rel = EXTPROTOCOL_GET_RELATION(fcinfo);
    ExtTableEntry *exttbl = GetExtTableEntry(rel->rd_id);

    const char fmtcode = exttbl->fmtcode;
    const char *fmtopts = exttbl->fmtopts;

    // only TEXT and CSV have detailed options
    if (fmttype_is_csv(fmtcode) || fmttype_is_text(fmtcode)) {
        if (strstr(fmtopts, "header") != NULL) {
            hasHeader = true;
        } else {
            hasHeader = false;
        }

        // detect end-of-line terminator
        const char *newline_str = strstr(fmtopts, "newline");
        eolString[0] = '\n';
        eolString[1] = '\0';

        if (newline_str != NULL) {
            const char *first = strchr(newline_str, '\'');
            const char *second = strchr(first + 1, '\'');
            size_t len = second - first - 1;
            char newline[EOL_STRING_MAX_LEN + 1];
            len = len > EOL_STRING_MAX_LEN ? EOL_STRING_MAX_LEN : len;  // defensive line

            strncpy(newline, first + 1, len);
            newline[len] = '\0';
            if (pg_strcasecmp(newline, "crlf") == 0) {
                eolString[0] = '\r';
                eolString[1] = '\n';
                eolString[2] = '\0';
            } else if (pg_strcasecmp(newline, "cr") == 0) {
                eolString[0] = '\r';
                eolString[1] = '\0';
            } else if (pg_strcasecmp(newline, "lf") == 0) {
                eolString[0] = '\n';
                eolString[1] = '\0';
            } else {  // should never come here
                ereport(ERROR, (0, errmsg("invalid value for NEWLINE (%s), "
                                          "valid options are: 'LF', 'CRLF', 'CR'",
                                          newline)));
            }
        }
    }
}

typedef struct gpcloudResHandle {
    GPReader *gpreader;
    GPWriter *gpwriter;

    ResourceOwner owner; /* owner of this handle */

    struct gpcloudResHandle *next;
    struct gpcloudResHandle *prev;
} gpcloudResHandle;

// Linked list of opened "handles", which are allocated in TopMemoryContext, and tracked by resource
// owners.
static gpcloudResHandle *openedResHandles;

static bool isGpcloudResReleaseCallbackRegistered;

static gpcloudResHandle *createGpcloudResHandle(void) {
    gpcloudResHandle *resHandle;

    resHandle = (gpcloudResHandle *)MemoryContextAlloc(TopMemoryContext, sizeof(gpcloudResHandle));
    resHandle->gpreader = NULL;
    resHandle->gpwriter = NULL;

    resHandle->owner = CurrentResourceOwner;
    resHandle->next = openedResHandles;
    resHandle->prev = NULL;

    if (openedResHandles) {
        openedResHandles->prev = resHandle;
    }

    openedResHandles = resHandle;

    return resHandle;
}

static void destroyGpcloudResHandle(gpcloudResHandle *resHandle) {
    if (resHandle == NULL) return;

    /* unlink from linked list first */
    if (resHandle->prev)
        resHandle->prev->next = resHandle->next;
    else
        openedResHandles = resHandle->next;

    if (resHandle->next) {
        resHandle->next->prev = resHandle->prev;
    }

    if (resHandle->gpreader != NULL) {
        if (!reader_cleanup(&resHandle->gpreader)) {
            elog(WARNING, "Failed to cleanup gpcloud extension: %s", s3extErrorMessage.c_str());
        }
    }

    if (resHandle->gpwriter != NULL) {
        if (!writer_cleanup(&resHandle->gpwriter)) {
            elog(WARNING, "Failed to cleanup gpcloud extension: %s", s3extErrorMessage.c_str());
        }
    }

    thread_cleanup();
    pfree(resHandle);
}

/*
 * Close any open handles on abort.
 */
static void gpcloudAbortCallback(ResourceReleasePhase phase, bool isCommit, bool isTopLevel,
                                 void *arg) {
    gpcloudResHandle *curr;
    gpcloudResHandle *next;

    if (phase != RESOURCE_RELEASE_AFTER_LOCKS) return;

    next = openedResHandles;
    while (next) {
        curr = next;
        next = curr->next;

        if (curr->owner == CurrentResourceOwner) {
            if (isCommit)
                elog(WARNING, "gpcloud external table reference leak: %p still referenced", curr);

            destroyGpcloudResHandle(curr);
        }
    }
}

/*
 * Import data into GPDB.
 * invoked by GPDB, be careful with C++ exceptions.
 */
Datum s3_import(PG_FUNCTION_ARGS) {
    /* Must be called via the external table format manager */
    if (!CALLED_AS_EXTPROTOCOL(fcinfo))
        elog(ERROR, "extprotocol_import: not called by external protocol manager");

    /* Get our internal description of the protocol */
    gpcloudResHandle *resHandle = (gpcloudResHandle *)EXTPROTOCOL_GET_USER_CTX(fcinfo);

    /* last call. destroy reader */
    if (EXTPROTOCOL_IS_LAST_CALL(fcinfo)) {
        destroyGpcloudResHandle(resHandle);

        EXTPROTOCOL_SET_USER_CTX(fcinfo, NULL);
        PG_RETURN_INT32(0);
    }

    /* first call. do any desired init */
    if (resHandle == NULL) {
        if (!isGpcloudResReleaseCallbackRegistered) {
            RegisterResourceReleaseCallback(gpcloudAbortCallback, NULL);
            isGpcloudResReleaseCallbackRegistered = true;
        }
        resHandle = createGpcloudResHandle();

        queryCancelFlag = false;
        const char *url_with_options = EXTPROTOCOL_GET_URL(fcinfo);

        // has HEADER? and newline EOL?
        parseFormatOpts(fcinfo);

        thread_setup();

        resHandle->gpreader = reader_init(url_with_options);
        if (!resHandle->gpreader) {
            ereport(ERROR, (0, errmsg("Failed to init gpcloud extension (segid = %d, "
                                      "segnum = %d), please check your "
                                      "configurations and network connection: %s",
                                      s3ext_segid, s3ext_segnum, s3extErrorMessage.c_str())));
        }

        EXTPROTOCOL_SET_USER_CTX(fcinfo, resHandle);
    }

    char *data_buf = EXTPROTOCOL_GET_DATABUF(fcinfo);
    int32 data_len = EXTPROTOCOL_GET_DATALEN(fcinfo);

    if (!reader_transfer_data(resHandle->gpreader, data_buf, data_len)) {
        ereport(ERROR,
                (0, errmsg("s3_import: could not read data: %s", s3extErrorMessage.c_str())));
    }
    PG_RETURN_INT32(data_len);
}

/*
 * Export data out of GPDB.
 * invoked by GPDB, be careful with C++ exceptions.
 */
Datum s3_export(PG_FUNCTION_ARGS) {
    /* Must be called via the external table format manager */
    if (!CALLED_AS_EXTPROTOCOL(fcinfo))
        elog(ERROR, "extprotocol_import: not called by external protocol manager");

    /* Get our internal description of the protocol */
    gpcloudResHandle *resHandle = (gpcloudResHandle *)EXTPROTOCOL_GET_USER_CTX(fcinfo);

    /* last call. destroy writer */
    if (EXTPROTOCOL_IS_LAST_CALL(fcinfo)) {
        destroyGpcloudResHandle(resHandle);

        EXTPROTOCOL_SET_USER_CTX(fcinfo, NULL);
        PG_RETURN_INT32(0);
    }

    /* first call. do any desired init */
    if (resHandle == NULL) {
        if (!isGpcloudResReleaseCallbackRegistered) {
            RegisterResourceReleaseCallback(gpcloudAbortCallback, NULL);
            isGpcloudResReleaseCallbackRegistered = true;
        }
        resHandle = createGpcloudResHandle();

        queryCancelFlag = false;
        const char *url_with_options = EXTPROTOCOL_GET_URL(fcinfo);
        const char *format = getFormatStr(fcinfo);

        thread_setup();

        resHandle->gpwriter = writer_init(url_with_options, format);
        if (!resHandle->gpwriter) {
            ereport(ERROR, (0, errmsg("Failed to init gpcloud extension (segid = %d, "
                                      "segnum = %d), please check your "
                                      "configurations and network connection: %s",
                                      s3ext_segid, s3ext_segnum, s3extErrorMessage.c_str())));
        }

        EXTPROTOCOL_SET_USER_CTX(fcinfo, resHandle);
    }

    char *data_buf = EXTPROTOCOL_GET_DATABUF(fcinfo);
    int32 data_len = EXTPROTOCOL_GET_DATALEN(fcinfo);

    if (!writer_transfer_data(resHandle->gpwriter, data_buf, data_len)) {
        ereport(ERROR,
                (0, errmsg("s3_export: could not write data: %s", s3extErrorMessage.c_str())));
    }

    PG_RETURN_INT32(data_len);
}
<|endoftext|>