<commit_before>// Copyright 2017 The Ray Authors.
//
// 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 "ray/gcs/gcs_server/gcs_node_manager.h"

#include <utility>

#include "ray/common/ray_config.h"
#include "ray/gcs/pb_util.h"
#include "ray/stats/stats.h"
#include "ray/util/event.h"
#include "ray/util/event_label.h"
#include "src/ray/protobuf/gcs.pb.h"

namespace ray {
namespace gcs {

//////////////////////////////////////////////////////////////////////////////////////////
GcsNodeManager::GcsNodeManager(
    std::shared_ptr<GcsPublisher> gcs_publisher,
    std::shared_ptr<gcs::GcsTableStorage> gcs_table_storage,
    std::shared_ptr<rpc::NodeManagerClientPool> raylet_client_pool)
    : gcs_publisher_(std::move(gcs_publisher)),
      gcs_table_storage_(std::move(gcs_table_storage)),
      raylet_client_pool_(std::move(raylet_client_pool)) {}

void GcsNodeManager::HandleRegisterNode(const rpc::RegisterNodeRequest &request,
                                        rpc::RegisterNodeReply *reply,
                                        rpc::SendReplyCallback send_reply_callback) {
  NodeID node_id = NodeID::FromBinary(request.node_info().node_id());
  RAY_LOG(INFO) << "Registering node info, node id = " << node_id
                << ", address = " << request.node_info().node_manager_address()
                << ", node name = " << request.node_info().node_name();
  auto on_done = [this, node_id, request, reply, send_reply_callback](
                     const Status &status) {
    RAY_CHECK_OK(status);
    RAY_LOG(INFO) << "Finished registering node info, node id = " << node_id
                  << ", address = " << request.node_info().node_manager_address()
                  << ", node name = " << request.node_info().node_name();
    RAY_CHECK_OK(gcs_publisher_->PublishNodeInfo(node_id, request.node_info(), nullptr));
    AddNode(std::make_shared<rpc::GcsNodeInfo>(request.node_info()));
    GCS_RPC_SEND_REPLY(send_reply_callback, reply, status);
  };
  RAY_CHECK_OK(
      gcs_table_storage_->NodeTable().Put(node_id, request.node_info(), on_done));
  ++counts_[CountType::REGISTER_NODE_REQUEST];
}

void GcsNodeManager::HandleDrainNode(const rpc::DrainNodeRequest &request,
                                     rpc::DrainNodeReply *reply,
                                     rpc::SendReplyCallback send_reply_callback) {
  auto num_drain_request = request.drain_node_data_size();
  for (auto i = 0; i < num_drain_request; i++) {
    const auto &node_drain_request = request.drain_node_data(i);
    const auto node_id = NodeID::FromBinary(node_drain_request.node_id());

    RAY_LOG(INFO) << "Draining node info, node id = " << node_id;
    DrainNode(node_id);
    auto drain_node_status = reply->add_drain_node_status();
    drain_node_status->set_node_id(node_id.Binary());
  };
  GCS_RPC_SEND_REPLY(send_reply_callback, reply, Status::OK());
  ++counts_[CountType::DRAIN_NODE_REQUEST];
}

void GcsNodeManager::DrainNode(const NodeID &node_id) {
  auto node = RemoveNode(node_id, /* is_intended = */ true);
  if (!node) {
    RAY_LOG(INFO) << "Node " << node_id << " is already removed";
    return;
  }

  // Do the procedure to drain a node.
  node->set_state(rpc::GcsNodeInfo::DEAD);
  node->set_timestamp(current_sys_time_ms());
  AddDeadNodeToCache(node);
  auto node_info_delta = std::make_shared<rpc::GcsNodeInfo>();
  node_info_delta->set_node_id(node->node_id());
  node_info_delta->set_state(node->state());
  node_info_delta->set_timestamp(node->timestamp());
  // Set the address.
  rpc::Address remote_address;
  remote_address.set_raylet_id(node->node_id());
  remote_address.set_ip_address(node->node_manager_address());
  remote_address.set_port(node->node_manager_port());
  auto on_put_done = [this,
                      remote_address = remote_address,
                      node_id,
                      node_info_delta = node_info_delta](const Status &status) {
    auto on_resource_update_done = [this,
                                    remote_address = std::move(remote_address),
                                    node_id,
                                    node_info_delta =
                                        node_info_delta](const Status &status) {
      auto raylet_client = raylet_client_pool_->GetOrConnectByAddress(remote_address);
      RAY_CHECK(raylet_client);
      // NOTE(sang): Drain API is not supposed to kill the raylet, but we are doing
      // this until the proper "drain" behavior is implemented. Currently, before
      // raylet is killed, it sends a drain request to GCS. That said, this can
      // happen;
      // - GCS updates the drain state and kills a raylet gracefully.
      // - Raylet kills itself and send a drain request of itself to GCS.
      // - Drain request will become a no-op in GCS.
      // This behavior is redundant, but harmless. We'll keep this behavior until we
      // implement the right drain behavior for the simplicity. Check
      // https://github.com/ray-project/ray/pull/19350 for more details.
      raylet_client->ShutdownRaylet(
          node_id,
          /*graceful*/ true,
          [this, node_id, node_info_delta = node_info_delta](
              const Status &status, const rpc::ShutdownRayletReply &reply) {
            RAY_LOG(INFO) << "Raylet " << node_id << " is drained. Status " << status
                          << ". The information will be published to the cluster.";
            /// Once the raylet is shutdown, inform all nodes that the raylet is dead.
            RAY_CHECK_OK(
                gcs_publisher_->PublishNodeInfo(node_id, *node_info_delta, nullptr));
          });
    };
    RAY_CHECK_OK(
        gcs_table_storage_->NodeResourceTable().Delete(node_id, on_resource_update_done));
  };
  // Update node state to DEAD instead of deleting it.
  RAY_CHECK_OK(gcs_table_storage_->NodeTable().Put(node_id, *node, on_put_done));
}

void GcsNodeManager::HandleGetAllNodeInfo(const rpc::GetAllNodeInfoRequest &request,
                                          rpc::GetAllNodeInfoReply *reply,
                                          rpc::SendReplyCallback send_reply_callback) {
  // Here the unsafe allocate is safe here, because entry.second's life cycle is longer
  // then reply.
  // The request will be sent when call send_reply_callback and after that, reply will
  // not be used any more. But entry is still valid.
  for (const auto &entry : alive_nodes_) {
    reply->mutable_node_info_list()->UnsafeArenaAddAllocated(entry.second.get());
  }
  for (const auto &entry : dead_nodes_) {
    reply->mutable_node_info_list()->UnsafeArenaAddAllocated(entry.second.get());
  }
  GCS_RPC_SEND_REPLY(send_reply_callback, reply, Status::OK());
  ++counts_[CountType::GET_ALL_NODE_INFO_REQUEST];
}

void GcsNodeManager::HandleGetInternalConfig(const rpc::GetInternalConfigRequest &request,
                                             rpc::GetInternalConfigReply *reply,
                                             rpc::SendReplyCallback send_reply_callback) {
  auto get_system_config = [reply, send_reply_callback](
                               const ray::Status &status,
                               const boost::optional<rpc::StoredConfig> &config) {
    if (config.has_value()) {
      reply->set_config(config.get().config());
    }
    GCS_RPC_SEND_REPLY(send_reply_callback, reply, status);
  };
  RAY_CHECK_OK(
      gcs_table_storage_->InternalConfigTable().Get(UniqueID::Nil(), get_system_config));
  ++counts_[CountType::GET_INTERNAL_CONFIG_REQUEST];
}

absl::optional<std::shared_ptr<rpc::GcsNodeInfo>> GcsNodeManager::GetAliveNode(
    const ray::NodeID &node_id) const {
  auto iter = alive_nodes_.find(node_id);
  if (iter == alive_nodes_.end()) {
    return {};
  }

  return iter->second;
}

void GcsNodeManager::AddNode(std::shared_ptr<rpc::GcsNodeInfo> node) {
  auto node_id = NodeID::FromBinary(node->node_id());
  auto iter = alive_nodes_.find(node_id);
  if (iter == alive_nodes_.end()) {
    alive_nodes_.emplace(node_id, node);

    // Notify all listeners.
    for (auto &listener : node_added_listeners_) {
      listener(node);
    }
  }
}

std::shared_ptr<rpc::GcsNodeInfo> GcsNodeManager::RemoveNode(
    const ray::NodeID &node_id, bool is_intended /*= false*/) {
  std::shared_ptr<rpc::GcsNodeInfo> removed_node;
  auto iter = alive_nodes_.find(node_id);
  if (iter != alive_nodes_.end()) {
    removed_node = std::move(iter->second);
    RAY_LOG(INFO) << "Removing node, node id = " << node_id
                  << ", node name = " << removed_node->node_name();
    // Record stats that there's a new removed node.
    stats::NodeFailureTotal.Record(1);
    // Remove from alive nodes.
    alive_nodes_.erase(iter);
    if (!is_intended) {
      // Broadcast a warning to all of the drivers indicating that the node
      // has been marked as dead.
      // TODO(rkn): Define this constant somewhere else.
      std::string type = "node_removed";
      std::ostringstream error_message;
      error_message << "The node with node id: " << node_id
                    << " and address: " << removed_node->node_manager_address()
                    << " and node name: " << removed_node->node_name()
                    << " has been marked dead because the detector"
                    << " has missed too many heartbeats from it. This can happen when a "
                       "raylet crashes unexpectedly or has lagging heartbeats.";
      RAY_EVENT(ERROR, EL_RAY_NODE_REMOVED)
              .WithField("node_id", node_id.Hex())
              .WithField("ip", removed_node->node_manager_address())
          << error_message.str();
      RAY_LOG(WARNING) << error_message.str();
      auto error_data_ptr =
          gcs::CreateErrorTableData(type, error_message.str(), current_time_ms());
      RAY_CHECK_OK(gcs_publisher_->PublishError(node_id.Hex(), *error_data_ptr, nullptr));
    }

    // Notify all listeners.
    for (auto &listener : node_removed_listeners_) {
      listener(removed_node);
    }
  }
  return removed_node;
}

void GcsNodeManager::OnNodeFailure(const NodeID &node_id) {
  if (auto node = RemoveNode(node_id, /* is_intended = */ false)) {
    node->set_state(rpc::GcsNodeInfo::DEAD);
    node->set_timestamp(current_sys_time_ms());
    AddDeadNodeToCache(node);
    auto node_info_delta = std::make_shared<rpc::GcsNodeInfo>();
    node_info_delta->set_node_id(node->node_id());
    node_info_delta->set_state(node->state());
    node_info_delta->set_timestamp(node->timestamp());

    auto on_done = [this, node_id, node_info_delta](const Status &status) {
      auto on_done = [this, node_id, node_info_delta](const Status &status) {
        RAY_CHECK_OK(gcs_publisher_->PublishNodeInfo(node_id, *node_info_delta, nullptr));
      };
      RAY_CHECK_OK(gcs_table_storage_->NodeResourceTable().Delete(node_id, on_done));
    };
    RAY_CHECK_OK(gcs_table_storage_->NodeTable().Put(node_id, *node, on_done));
  }
}

void GcsNodeManager::Initialize(const GcsInitData &gcs_init_data) {
  for (const auto &[node_id, node_info] : gcs_init_data.Nodes()) {
    if (node_info.state() == rpc::GcsNodeInfo::ALIVE) {
      AddNode(std::make_shared<rpc::GcsNodeInfo>(node_info));

      // Ask the raylet to do initialization in case of GCS restart.
      // The protocol is correct because when a new node joined, Raylet will do:
      //    - RegisterNode (write node to the node table)
      //    - Setup subscription
      // With this, it means we only need to ask the node registered to do resubscription.
      // And for the node failed to register, they will crash on the client side due to
      // registeration failure.
      rpc::Address remote_address;
      remote_address.set_raylet_id(node_info.node_id());
      remote_address.set_ip_address(node_info.node_manager_address());
      remote_address.set_port(node_info.node_manager_port());
      auto raylet_client = raylet_client_pool_->GetOrConnectByAddress(remote_address);
      raylet_client->NotifyGCSRestart(nullptr);
    } else if (node_info.state() == rpc::GcsNodeInfo::DEAD) {
      dead_nodes_.emplace(node_id, std::make_shared<rpc::GcsNodeInfo>(node_info));
      sorted_dead_node_list_.emplace_back(node_id, node_info.timestamp());
    }
  }
  sorted_dead_node_list_.sort(
      [](const std::pair<NodeID, int64_t> &left,
         const std::pair<NodeID, int64_t> &right) { return left.second < right.second; });
}

void GcsNodeManager::AddDeadNodeToCache(std::shared_ptr<rpc::GcsNodeInfo> node) {
  if (dead_nodes_.size() >= RayConfig::instance().maximum_gcs_dead_node_cached_count()) {
    const auto &node_id = sorted_dead_node_list_.begin()->first;
    RAY_CHECK_OK(gcs_table_storage_->NodeTable().Delete(node_id, nullptr));
    dead_nodes_.erase(sorted_dead_node_list_.begin()->first);
    sorted_dead_node_list_.erase(sorted_dead_node_list_.begin());
  }
  auto node_id = NodeID::FromBinary(node->node_id());
  dead_nodes_.emplace(node_id, node);
  sorted_dead_node_list_.emplace_back(node_id, node->timestamp());
}

std::string GcsNodeManager::DebugString() const {
  std::ostringstream stream;
  stream << "GcsNodeManager: "
         << "\n- RegisterNode request count: "
         << counts_[CountType::REGISTER_NODE_REQUEST]
         << "\n- DrainNode request count: " << counts_[CountType::DRAIN_NODE_REQUEST]
         << "\n- GetAllNodeInfo request count: "
         << counts_[CountType::GET_ALL_NODE_INFO_REQUEST]
         << "\n- GetInternalConfig request count: "
         << counts_[CountType::GET_INTERNAL_CONFIG_REQUEST];
  return stream.str();
}

}  // namespace gcs
}  // namespace ray
<commit_msg>[Core] add-better-error-msg-on-node-failure (#25269)<commit_after>// Copyright 2017 The Ray Authors.
//
// 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 "ray/gcs/gcs_server/gcs_node_manager.h"

#include <utility>

#include "ray/common/ray_config.h"
#include "ray/gcs/pb_util.h"
#include "ray/stats/stats.h"
#include "ray/util/event.h"
#include "ray/util/event_label.h"
#include "src/ray/protobuf/gcs.pb.h"

namespace ray {
namespace gcs {

//////////////////////////////////////////////////////////////////////////////////////////
GcsNodeManager::GcsNodeManager(
    std::shared_ptr<GcsPublisher> gcs_publisher,
    std::shared_ptr<gcs::GcsTableStorage> gcs_table_storage,
    std::shared_ptr<rpc::NodeManagerClientPool> raylet_client_pool)
    : gcs_publisher_(std::move(gcs_publisher)),
      gcs_table_storage_(std::move(gcs_table_storage)),
      raylet_client_pool_(std::move(raylet_client_pool)) {}

void GcsNodeManager::HandleRegisterNode(const rpc::RegisterNodeRequest &request,
                                        rpc::RegisterNodeReply *reply,
                                        rpc::SendReplyCallback send_reply_callback) {
  NodeID node_id = NodeID::FromBinary(request.node_info().node_id());
  RAY_LOG(INFO) << "Registering node info, node id = " << node_id
                << ", address = " << request.node_info().node_manager_address()
                << ", node name = " << request.node_info().node_name();
  auto on_done = [this, node_id, request, reply, send_reply_callback](
                     const Status &status) {
    RAY_CHECK_OK(status);
    RAY_LOG(INFO) << "Finished registering node info, node id = " << node_id
                  << ", address = " << request.node_info().node_manager_address()
                  << ", node name = " << request.node_info().node_name();
    RAY_CHECK_OK(gcs_publisher_->PublishNodeInfo(node_id, request.node_info(), nullptr));
    AddNode(std::make_shared<rpc::GcsNodeInfo>(request.node_info()));
    GCS_RPC_SEND_REPLY(send_reply_callback, reply, status);
  };
  RAY_CHECK_OK(
      gcs_table_storage_->NodeTable().Put(node_id, request.node_info(), on_done));
  ++counts_[CountType::REGISTER_NODE_REQUEST];
}

void GcsNodeManager::HandleDrainNode(const rpc::DrainNodeRequest &request,
                                     rpc::DrainNodeReply *reply,
                                     rpc::SendReplyCallback send_reply_callback) {
  auto num_drain_request = request.drain_node_data_size();
  for (auto i = 0; i < num_drain_request; i++) {
    const auto &node_drain_request = request.drain_node_data(i);
    const auto node_id = NodeID::FromBinary(node_drain_request.node_id());

    RAY_LOG(INFO) << "Draining node info, node id = " << node_id;
    DrainNode(node_id);
    auto drain_node_status = reply->add_drain_node_status();
    drain_node_status->set_node_id(node_id.Binary());
  };
  GCS_RPC_SEND_REPLY(send_reply_callback, reply, Status::OK());
  ++counts_[CountType::DRAIN_NODE_REQUEST];
}

void GcsNodeManager::DrainNode(const NodeID &node_id) {
  auto node = RemoveNode(node_id, /* is_intended = */ true);
  if (!node) {
    RAY_LOG(INFO) << "Node " << node_id << " is already removed";
    return;
  }

  // Do the procedure to drain a node.
  node->set_state(rpc::GcsNodeInfo::DEAD);
  node->set_timestamp(current_sys_time_ms());
  AddDeadNodeToCache(node);
  auto node_info_delta = std::make_shared<rpc::GcsNodeInfo>();
  node_info_delta->set_node_id(node->node_id());
  node_info_delta->set_state(node->state());
  node_info_delta->set_timestamp(node->timestamp());
  // Set the address.
  rpc::Address remote_address;
  remote_address.set_raylet_id(node->node_id());
  remote_address.set_ip_address(node->node_manager_address());
  remote_address.set_port(node->node_manager_port());
  auto on_put_done = [this,
                      remote_address = remote_address,
                      node_id,
                      node_info_delta = node_info_delta](const Status &status) {
    auto on_resource_update_done = [this,
                                    remote_address = std::move(remote_address),
                                    node_id,
                                    node_info_delta =
                                        node_info_delta](const Status &status) {
      auto raylet_client = raylet_client_pool_->GetOrConnectByAddress(remote_address);
      RAY_CHECK(raylet_client);
      // NOTE(sang): Drain API is not supposed to kill the raylet, but we are doing
      // this until the proper "drain" behavior is implemented. Currently, before
      // raylet is killed, it sends a drain request to GCS. That said, this can
      // happen;
      // - GCS updates the drain state and kills a raylet gracefully.
      // - Raylet kills itself and send a drain request of itself to GCS.
      // - Drain request will become a no-op in GCS.
      // This behavior is redundant, but harmless. We'll keep this behavior until we
      // implement the right drain behavior for the simplicity. Check
      // https://github.com/ray-project/ray/pull/19350 for more details.
      raylet_client->ShutdownRaylet(
          node_id,
          /*graceful*/ true,
          [this, node_id, node_info_delta = node_info_delta](
              const Status &status, const rpc::ShutdownRayletReply &reply) {
            RAY_LOG(INFO) << "Raylet " << node_id << " is drained. Status " << status
                          << ". The information will be published to the cluster.";
            /// Once the raylet is shutdown, inform all nodes that the raylet is dead.
            RAY_CHECK_OK(
                gcs_publisher_->PublishNodeInfo(node_id, *node_info_delta, nullptr));
          });
    };
    RAY_CHECK_OK(
        gcs_table_storage_->NodeResourceTable().Delete(node_id, on_resource_update_done));
  };
  // Update node state to DEAD instead of deleting it.
  RAY_CHECK_OK(gcs_table_storage_->NodeTable().Put(node_id, *node, on_put_done));
}

void GcsNodeManager::HandleGetAllNodeInfo(const rpc::GetAllNodeInfoRequest &request,
                                          rpc::GetAllNodeInfoReply *reply,
                                          rpc::SendReplyCallback send_reply_callback) {
  // Here the unsafe allocate is safe here, because entry.second's life cycle is longer
  // then reply.
  // The request will be sent when call send_reply_callback and after that, reply will
  // not be used any more. But entry is still valid.
  for (const auto &entry : alive_nodes_) {
    reply->mutable_node_info_list()->UnsafeArenaAddAllocated(entry.second.get());
  }
  for (const auto &entry : dead_nodes_) {
    reply->mutable_node_info_list()->UnsafeArenaAddAllocated(entry.second.get());
  }
  GCS_RPC_SEND_REPLY(send_reply_callback, reply, Status::OK());
  ++counts_[CountType::GET_ALL_NODE_INFO_REQUEST];
}

void GcsNodeManager::HandleGetInternalConfig(const rpc::GetInternalConfigRequest &request,
                                             rpc::GetInternalConfigReply *reply,
                                             rpc::SendReplyCallback send_reply_callback) {
  auto get_system_config = [reply, send_reply_callback](
                               const ray::Status &status,
                               const boost::optional<rpc::StoredConfig> &config) {
    if (config.has_value()) {
      reply->set_config(config.get().config());
    }
    GCS_RPC_SEND_REPLY(send_reply_callback, reply, status);
  };
  RAY_CHECK_OK(
      gcs_table_storage_->InternalConfigTable().Get(UniqueID::Nil(), get_system_config));
  ++counts_[CountType::GET_INTERNAL_CONFIG_REQUEST];
}

absl::optional<std::shared_ptr<rpc::GcsNodeInfo>> GcsNodeManager::GetAliveNode(
    const ray::NodeID &node_id) const {
  auto iter = alive_nodes_.find(node_id);
  if (iter == alive_nodes_.end()) {
    return {};
  }

  return iter->second;
}

void GcsNodeManager::AddNode(std::shared_ptr<rpc::GcsNodeInfo> node) {
  auto node_id = NodeID::FromBinary(node->node_id());
  auto iter = alive_nodes_.find(node_id);
  if (iter == alive_nodes_.end()) {
    alive_nodes_.emplace(node_id, node);

    // Notify all listeners.
    for (auto &listener : node_added_listeners_) {
      listener(node);
    }
  }
}

std::shared_ptr<rpc::GcsNodeInfo> GcsNodeManager::RemoveNode(
    const ray::NodeID &node_id, bool is_intended /*= false*/) {
  std::shared_ptr<rpc::GcsNodeInfo> removed_node;
  auto iter = alive_nodes_.find(node_id);
  if (iter != alive_nodes_.end()) {
    removed_node = std::move(iter->second);
    RAY_LOG(INFO) << "Removing node, node id = " << node_id
                  << ", node name = " << removed_node->node_name();
    // Record stats that there's a new removed node.
    stats::NodeFailureTotal.Record(1);
    // Remove from alive nodes.
    alive_nodes_.erase(iter);
    if (!is_intended) {
      // Broadcast a warning to all of the drivers indicating that the node
      // has been marked as dead.
      // TODO(rkn): Define this constant somewhere else.
      std::string type = "node_removed";
      std::ostringstream error_message;
      error_message
          << "The node with node id: " << node_id
          << " and address: " << removed_node->node_manager_address()
          << " and node name: " << removed_node->node_name()
          << " has been marked dead because the detector"
          << " has missed too many heartbeats from it. This can happen when a "
             "\t(1) raylet crashes unexpectedly (OOM, preempted node, etc.) \n"
          << "\t(2) raylet has lagging heartbeats due to slow network or busy workload.";
      RAY_EVENT(ERROR, EL_RAY_NODE_REMOVED)
              .WithField("node_id", node_id.Hex())
              .WithField("ip", removed_node->node_manager_address())
          << error_message.str();
      RAY_LOG(WARNING) << error_message.str();
      auto error_data_ptr =
          gcs::CreateErrorTableData(type, error_message.str(), current_time_ms());
      RAY_CHECK_OK(gcs_publisher_->PublishError(node_id.Hex(), *error_data_ptr, nullptr));
    }

    // Notify all listeners.
    for (auto &listener : node_removed_listeners_) {
      listener(removed_node);
    }
  }
  return removed_node;
}

void GcsNodeManager::OnNodeFailure(const NodeID &node_id) {
  if (auto node = RemoveNode(node_id, /* is_intended = */ false)) {
    node->set_state(rpc::GcsNodeInfo::DEAD);
    node->set_timestamp(current_sys_time_ms());
    AddDeadNodeToCache(node);
    auto node_info_delta = std::make_shared<rpc::GcsNodeInfo>();
    node_info_delta->set_node_id(node->node_id());
    node_info_delta->set_state(node->state());
    node_info_delta->set_timestamp(node->timestamp());

    auto on_done = [this, node_id, node_info_delta](const Status &status) {
      auto on_done = [this, node_id, node_info_delta](const Status &status) {
        RAY_CHECK_OK(gcs_publisher_->PublishNodeInfo(node_id, *node_info_delta, nullptr));
      };
      RAY_CHECK_OK(gcs_table_storage_->NodeResourceTable().Delete(node_id, on_done));
    };
    RAY_CHECK_OK(gcs_table_storage_->NodeTable().Put(node_id, *node, on_done));
  }
}

void GcsNodeManager::Initialize(const GcsInitData &gcs_init_data) {
  for (const auto &[node_id, node_info] : gcs_init_data.Nodes()) {
    if (node_info.state() == rpc::GcsNodeInfo::ALIVE) {
      AddNode(std::make_shared<rpc::GcsNodeInfo>(node_info));

      // Ask the raylet to do initialization in case of GCS restart.
      // The protocol is correct because when a new node joined, Raylet will do:
      //    - RegisterNode (write node to the node table)
      //    - Setup subscription
      // With this, it means we only need to ask the node registered to do resubscription.
      // And for the node failed to register, they will crash on the client side due to
      // registeration failure.
      rpc::Address remote_address;
      remote_address.set_raylet_id(node_info.node_id());
      remote_address.set_ip_address(node_info.node_manager_address());
      remote_address.set_port(node_info.node_manager_port());
      auto raylet_client = raylet_client_pool_->GetOrConnectByAddress(remote_address);
      raylet_client->NotifyGCSRestart(nullptr);
    } else if (node_info.state() == rpc::GcsNodeInfo::DEAD) {
      dead_nodes_.emplace(node_id, std::make_shared<rpc::GcsNodeInfo>(node_info));
      sorted_dead_node_list_.emplace_back(node_id, node_info.timestamp());
    }
  }
  sorted_dead_node_list_.sort(
      [](const std::pair<NodeID, int64_t> &left,
         const std::pair<NodeID, int64_t> &right) { return left.second < right.second; });
}

void GcsNodeManager::AddDeadNodeToCache(std::shared_ptr<rpc::GcsNodeInfo> node) {
  if (dead_nodes_.size() >= RayConfig::instance().maximum_gcs_dead_node_cached_count()) {
    const auto &node_id = sorted_dead_node_list_.begin()->first;
    RAY_CHECK_OK(gcs_table_storage_->NodeTable().Delete(node_id, nullptr));
    dead_nodes_.erase(sorted_dead_node_list_.begin()->first);
    sorted_dead_node_list_.erase(sorted_dead_node_list_.begin());
  }
  auto node_id = NodeID::FromBinary(node->node_id());
  dead_nodes_.emplace(node_id, node);
  sorted_dead_node_list_.emplace_back(node_id, node->timestamp());
}

std::string GcsNodeManager::DebugString() const {
  std::ostringstream stream;
  stream << "GcsNodeManager: "
         << "\n- RegisterNode request count: "
         << counts_[CountType::REGISTER_NODE_REQUEST]
         << "\n- DrainNode request count: " << counts_[CountType::DRAIN_NODE_REQUEST]
         << "\n- GetAllNodeInfo request count: "
         << counts_[CountType::GET_ALL_NODE_INFO_REQUEST]
         << "\n- GetInternalConfig request count: "
         << counts_[CountType::GET_INTERNAL_CONFIG_REQUEST];
  return stream.str();
}

}  // namespace gcs
}  // namespace ray
<|endoftext|>


<commit_before>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
*
* inspired notably from kdelibs/kdeui/color/kcolorutils.h
* Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Zack Rusin <zack@kde.org>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or( at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygengtkicons.h"
#include "config.h"

#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>

namespace Oxygen
{

    //_________________________________________
    GtkIcons::GtkIcons( void ):
        _dirty( true )
    {

        // initialize sizes
        _sizes.push_back( std::make_pair( "panel-menu", 16 ) );
        _sizes.push_back( std::make_pair( "panel", 32 ) );
        _sizes.push_back( std::make_pair( "gtk-small-toolbar", 22 ) );
        _sizes.push_back( std::make_pair( "gtk-large-toolbar", 22 ) );
        _sizes.push_back( std::make_pair( "gtk-dnd", 48 ) );
        _sizes.push_back( std::make_pair( "gtk-button", 16 ) );
        _sizes.push_back( std::make_pair( "gtk-menu", 16 ) );
        _sizes.push_back( std::make_pair( "gtk-dialog", 32 ) );
        _sizes.push_back( std::make_pair( "", 16 ) );

    }

    //_________________________________________
    void GtkIcons::setIconSize( const std::string& tag, unsigned int value )
    {
        SizeMap::iterator iter( std::find_if( _sizes.begin(), _sizes.end(), SameTagFTor( tag ) ) );
        if( iter == _sizes.end() ) {

            std::cerr << "GtkIcons::setIconSize - no match for" << tag << "," << value << std::endl;

        } else if( iter->second != value ) {

            iter->second = value;
            _dirty = true;

        }

    }

    //_________________________________________
    void GtkIcons::loadTranslations( const std::string& filename )
    {

        if( filename == _filename )
        { return; }

        _filename = filename;
        _dirty = true;
        _icons.clear();

        std::ifstream in( filename.c_str() );
        if( !in )
        {
            std::cerr << "Oxygen::GtkIcons::loadTranslations - could not open " << filename << std::endl;
            return;
        }

        std::string line;
        while( std::getline( in, line, '\n' ) )
        {

            if( line.empty() ) continue;

            IconPair iconPair;
            std::istringstream stream( line.c_str() );
            stream >> iconPair.first >> iconPair.second;
            if( ( stream.rdstate() & std::ios::failbit ) != 0 )
            { continue; }

            _icons.insert( iconPair );

        }

    }

    //_________________________________________
    Gtk::RC GtkIcons::generate( const PathList& pathList )
    {

        if( (!_dirty) && _pathList == pathList ) return _rc;

        // #if OXYGEN_DEBUG
        std::cerr << "Oxygen::GtkIcons::generate - regenerating translation tables" << std::endl;
        // #endif

        _pathList = pathList;

        // generate icon size string
        std::ostringstream iconSizesStr;
        iconSizesStr << "gtk-icon-sizes = \"";
        for( SizeMap::const_iterator iter = _sizes.begin(); iter != _sizes.end(); ++iter )
        {
            if( iter->first.empty() ) continue;
            if( iter != _sizes.begin() ) iconSizesStr << ": ";
            iconSizesStr << iter->first << " = " << iter->second << "," << iter->second;
        }
        iconSizesStr << "\"";
        _rc.addToHeaderSection( iconSizesStr.str() );

        // generate pixmap path
        // this must be passed to gtk before any icon settings, otherwise
        // other icons are not recognized
        std::ostringstream pixmapPathStr;
        pixmapPathStr << "pixmap_path \"";
        for( PathList::const_iterator iter = pathList.begin(); iter != pathList.end(); ++iter )
        {
            #if OXYGEN_DEBUG
            std::cerr << "Oxygen::GtkIcons::generate - adding path: " << *iter << std::endl;
            #endif

            if( iter != pathList.begin() ) pixmapPathStr << ":";
            pixmapPathStr << *iter;
        }
        pixmapPathStr << "\"";
        _rc.addToHeaderSection( pixmapPathStr.str() );

        // loop over icons
        _rc.setCurrentSection( Gtk::RC::defaultSection() );
        std::ostringstream notFoundOut;
        for( IconMap::const_iterator iconIter = _icons.begin(); iconIter != _icons.end(); ++iconIter )
        {

            std::string stock( generate( iconIter->first, iconIter->second, pathList ) );
            if( stock.empty() ) notFoundOut << "#  stock[\"" << iconIter->first << "\"]=<No matching KDE icon>" << std::endl;
            else _rc.addToCurrentSection( stock );

        }

        // add list of not found icons to the bottom of the list
        _rc.addToCurrentSection( notFoundOut.str() );

        // extra settings for entries
        std::string stock( generate( "gtk-clear", "actions/edit-clear-locationbar-rtl.png", pathList ) );
        if( !stock.empty() )
        {
            _rc.addSection( "oxygen-icons-editor", Gtk::RC::defaultSection() );
            _rc.addToCurrentSection( stock );
            _rc.addToRootSection( "class \"*Entry*\" style \"oxygen-icons-editor\"" );
        }

        _dirty = false;
        return _rc;

    }

    //__________________________________________________________________
    std::string GtkIcons::generate(
        const std::string& gtkIconName,
        const std::string& kdeIconName,
        const PathList& pathList ) const
    {

        if( kdeIconName == "NONE" ) return std::string();

        bool empty( true );
        std::ostringstream stockOut;

        // new stock
        stockOut << "  stock[\"" << gtkIconName << "\"]={" << std::endl;

        // loop over iconSizes
        for( SizeMap::const_iterator sizeIter = _sizes.begin(); sizeIter != _sizes.end(); ++sizeIter )
        {

            // generate full icon name
            std::ostringstream iconFileStream;
            iconFileStream << sizeIter->second << "x" << sizeIter->second << "/" << kdeIconName;

            // loop over provided path to see if at least one icon is found
            bool found( false );
            for( PathList::const_iterator pathIter = pathList.begin(); pathIter != pathList.end(); ++pathIter )
            {
                std::string filename( *pathIter + '/' + iconFileStream.str() );
                if( !std::ifstream( filename.c_str() ) ) continue;
                found = true;
                break;
            }

            if( !found ) continue;
            empty = false;
            if( sizeIter->first.empty() ) stockOut << "    { \"" << iconFileStream.str() << "\" }" << std::endl;
            else stockOut << "    { \"" << iconFileStream.str() << "\", *, *, \"" << sizeIter->first << "\" }," << std::endl;

        }

        stockOut << "  }" << std::endl;

        return empty ? std::string() : stockOut.str();

    }

}
<commit_msg>added missing GtkRC clear call.<commit_after>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
*
* inspired notably from kdelibs/kdeui/color/kcolorutils.h
* Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
* Copyright (C) 2007 Zack Rusin <zack@kde.org>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or( at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygengtkicons.h"
#include "config.h"

#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>

namespace Oxygen
{

    //_________________________________________
    GtkIcons::GtkIcons( void ):
        _dirty( true )
    {

        // initialize sizes
        _sizes.push_back( std::make_pair( "panel-menu", 16 ) );
        _sizes.push_back( std::make_pair( "panel", 32 ) );
        _sizes.push_back( std::make_pair( "gtk-small-toolbar", 22 ) );
        _sizes.push_back( std::make_pair( "gtk-large-toolbar", 22 ) );
        _sizes.push_back( std::make_pair( "gtk-dnd", 48 ) );
        _sizes.push_back( std::make_pair( "gtk-button", 16 ) );
        _sizes.push_back( std::make_pair( "gtk-menu", 16 ) );
        _sizes.push_back( std::make_pair( "gtk-dialog", 32 ) );
        _sizes.push_back( std::make_pair( "", 16 ) );

    }

    //_________________________________________
    void GtkIcons::setIconSize( const std::string& tag, unsigned int value )
    {
        SizeMap::iterator iter( std::find_if( _sizes.begin(), _sizes.end(), SameTagFTor( tag ) ) );
        if( iter == _sizes.end() ) {

            std::cerr << "GtkIcons::setIconSize - no match for" << tag << "," << value << std::endl;

        } else if( iter->second != value ) {

            iter->second = value;
            _dirty = true;

        }

    }

    //_________________________________________
    void GtkIcons::loadTranslations( const std::string& filename )
    {

        if( filename == _filename )
        { return; }

        _filename = filename;
        _dirty = true;
        _icons.clear();

        std::ifstream in( filename.c_str() );
        if( !in )
        {
            std::cerr << "Oxygen::GtkIcons::loadTranslations - could not open " << filename << std::endl;
            return;
        }

        std::string line;
        while( std::getline( in, line, '\n' ) )
        {

            if( line.empty() ) continue;

            IconPair iconPair;
            std::istringstream stream( line.c_str() );
            stream >> iconPair.first >> iconPair.second;
            if( ( stream.rdstate() & std::ios::failbit ) != 0 )
            { continue; }

            _icons.insert( iconPair );

        }

    }

    //_________________________________________
    Gtk::RC GtkIcons::generate( const PathList& pathList )
    {

        if( (!_dirty) && _pathList == pathList ) return _rc;

        // #if OXYGEN_DEBUG
        std::cerr << "Oxygen::GtkIcons::generate - regenerating translation tables" << std::endl;
        // #endif

        _pathList = pathList;
        _rc.clear();

        // generate icon size string
        std::ostringstream iconSizesStr;
        iconSizesStr << "gtk-icon-sizes = \"";
        for( SizeMap::const_iterator iter = _sizes.begin(); iter != _sizes.end(); ++iter )
        {
            if( iter->first.empty() ) continue;
            if( iter != _sizes.begin() ) iconSizesStr << ": ";
            iconSizesStr << iter->first << " = " << iter->second << "," << iter->second;
        }
        iconSizesStr << "\"";
        _rc.addToHeaderSection( iconSizesStr.str() );

        // generate pixmap path
        // this must be passed to gtk before any icon settings, otherwise
        // other icons are not recognized
        std::ostringstream pixmapPathStr;
        pixmapPathStr << "pixmap_path \"";
        for( PathList::const_iterator iter = pathList.begin(); iter != pathList.end(); ++iter )
        {
            #if OXYGEN_DEBUG
            std::cerr << "Oxygen::GtkIcons::generate - adding path: " << *iter << std::endl;
            #endif

            if( iter != pathList.begin() ) pixmapPathStr << ":";
            pixmapPathStr << *iter;
        }
        pixmapPathStr << "\"";
        _rc.addToHeaderSection( pixmapPathStr.str() );

        // loop over icons
        _rc.setCurrentSection( Gtk::RC::defaultSection() );
        std::ostringstream notFoundOut;
        for( IconMap::const_iterator iconIter = _icons.begin(); iconIter != _icons.end(); ++iconIter )
        {

            std::string stock( generate( iconIter->first, iconIter->second, pathList ) );
            if( stock.empty() ) notFoundOut << "#  stock[\"" << iconIter->first << "\"]=<No matching KDE icon>" << std::endl;
            else _rc.addToCurrentSection( stock );

        }

        // add list of not found icons to the bottom of the list
        _rc.addToCurrentSection( notFoundOut.str() );

        // extra settings for entries
        std::string stock( generate( "gtk-clear", "actions/edit-clear-locationbar-rtl.png", pathList ) );
        if( !stock.empty() )
        {
            _rc.addSection( "oxygen-icons-editor", Gtk::RC::defaultSection() );
            _rc.addToCurrentSection( stock );
            _rc.addToRootSection( "class \"*Entry*\" style \"oxygen-icons-editor\"" );
        }

        _dirty = false;
        return _rc;

    }

    //__________________________________________________________________
    std::string GtkIcons::generate(
        const std::string& gtkIconName,
        const std::string& kdeIconName,
        const PathList& pathList ) const
    {

        if( kdeIconName == "NONE" ) return std::string();

        bool empty( true );
        std::ostringstream stockOut;

        // new stock
        stockOut << "  stock[\"" << gtkIconName << "\"]={" << std::endl;

        // loop over iconSizes
        for( SizeMap::const_iterator sizeIter = _sizes.begin(); sizeIter != _sizes.end(); ++sizeIter )
        {

            // generate full icon name
            std::ostringstream iconFileStream;
            iconFileStream << sizeIter->second << "x" << sizeIter->second << "/" << kdeIconName;

            // loop over provided path to see if at least one icon is found
            bool found( false );
            for( PathList::const_iterator pathIter = pathList.begin(); pathIter != pathList.end(); ++pathIter )
            {
                std::string filename( *pathIter + '/' + iconFileStream.str() );
                if( !std::ifstream( filename.c_str() ) ) continue;
                found = true;
                break;
            }

            if( !found ) continue;
            empty = false;
            if( sizeIter->first.empty() ) stockOut << "    { \"" << iconFileStream.str() << "\" }" << std::endl;
            else stockOut << "    { \"" << iconFileStream.str() << "\", *, *, \"" << sizeIter->first << "\" }," << std::endl;

        }

        stockOut << "  }" << std::endl;

        return empty ? std::string() : stockOut.str();

    }

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: openclose.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 07:55:43 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef CSV_OPENCLOSE_HXX
#define CSV_OPENCLOSE_HXX

namespace csv
{

// Open modes for storages:
enum E_RWMode
{
    rwDefault   = 0x0000,       //  Keep old settings. If there are none, set default.
    rwRead      = 0x0001,       //  Reads only
    rwWrite     = 0x0002,       //  Writes only
    rwReadWrite = 0x0003        //  Reads and writes.
};

enum E_OpenMode
{
    omCreateIfNecessary     = 0x0000,   // Creates a new file only, if file does not exist.
    omCreateNot             = 0x0010,   // Open fails, if file does not exist.
    omCreate                = 0x0020    // Existing file will be deleted.
};
enum E_ShareMode
{
    shmShareNot     = 0x0000,       // Allow others nothing
    shmShareRead    = 0x0004,       // Allow others to read
    shmShareAll     = 0x000C        // Allow others to read and write
};

/** Constants for filemode combinations
    These combinations are the only ones, guaranteed to be supported.
*/
const UINT32    CFM_RW      = rwReadWrite;
const UINT32    CFM_CREATE  = rwReadWrite | omCreate;
const UINT32    CFM_READ    = rwRead | omCreateNot | shmShareRead;

class OpenClose
{
  public:
    bool                open(
                            UINT32          in_nOpenModeInfo = 0 ); /// Combination of values of E_RWMode and E_ShareMode und E_OpenMode. 0 := Keep existing mode.
    void                close();

    bool                is_open() const;

  private:
    virtual bool        do_open(
                            UINT32          in_nOpenModeInfo ) = 0;
    virtual void        do_close() = 0;
    virtual bool        inq_is_open() const = 0;
};

class OpenCloseGuard
{
  public:
                        OpenCloseGuard(
                            OpenClose &         i_rOpenClose,
                            UINT32              i_nOpenModeInfo = 0 );
                        ~OpenCloseGuard();
                        operator bool() const;

  private:
    // Forbidden:
                        OpenCloseGuard(OpenCloseGuard&);
    OpenCloseGuard &    operator=(OpenCloseGuard&);

    // DATA
    OpenClose &         rOpenClose;
};

// IMPLEMENTATION

inline bool
OpenClose::open( UINT32 i_nOpenModeInfo )
    { return do_open(i_nOpenModeInfo); }
inline void
OpenClose::close()
    { do_close(); }
inline bool
OpenClose::is_open() const
    { return inq_is_open(); }

inline
OpenCloseGuard::OpenCloseGuard( OpenClose & i_rOpenClose,
                                UINT32      i_nOpenModeInfo )
    :   rOpenClose(i_rOpenClose)
    { rOpenClose.open(i_nOpenModeInfo); }
inline
OpenCloseGuard::~OpenCloseGuard()
    { if (rOpenClose.is_open()) rOpenClose.close(); }
inline
OpenCloseGuard::operator bool() const
    { return rOpenClose.is_open(); }

}   // namespace csv

#endif

<commit_msg>INTEGRATION: CWS warnings01 (1.2.120); FILE MERGED 2005/11/07 12:03:07 sb 1.2.120.3: #i53898# Made code warning-free (GCC 4.0.1). 2005/09/22 22:09:53 sb 1.2.120.2: RESYNC: (1.2-1.3); FILE MERGED 2005/08/30 13:13:55 sb 1.2.120.1: #i53898# Made code warning-free.<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: openclose.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 14:29:56 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef CSV_OPENCLOSE_HXX
#define CSV_OPENCLOSE_HXX

namespace csv
{

// Open modes for storages:
enum E_RWMode
{
    rwDefault   = 0x0000,       //  Keep old settings. If there are none, set default.
    rwRead      = 0x0001,       //  Reads only
    rwWrite     = 0x0002,       //  Writes only
    rwReadWrite = 0x0003        //  Reads and writes.
};

enum E_OpenMode
{
    omCreateIfNecessary     = 0x0000,   // Creates a new file only, if file does not exist.
    omCreateNot             = 0x0010,   // Open fails, if file does not exist.
    omCreate                = 0x0020    // Existing file will be deleted.
};
enum E_ShareMode
{
    shmShareNot     = 0x0000,       // Allow others nothing
    shmShareRead    = 0x0004,       // Allow others to read
    shmShareAll     = 0x000C        // Allow others to read and write
};

/** Constants for filemode combinations
    These combinations are the only ones, guaranteed to be supported.
*/
const UINT32    CFM_RW      = rwReadWrite;
const UINT32    CFM_CREATE  =
    static_cast< UINT32 >(rwReadWrite) | static_cast< UINT32 >(omCreate);
const UINT32    CFM_READ    =
    static_cast< UINT32 >(rwRead) | static_cast< UINT32 >(omCreateNot) |
    static_cast< UINT32 >(shmShareRead);

class OpenClose
{
  public:
    virtual ~OpenClose() {}

    bool                open(
                            UINT32          in_nOpenModeInfo = 0 ); /// Combination of values of E_RWMode and E_ShareMode und E_OpenMode. 0 := Keep existing mode.
    void                close();

    bool                is_open() const;

  private:
    virtual bool        do_open(
                            UINT32          in_nOpenModeInfo ) = 0;
    virtual void        do_close() = 0;
    virtual bool        inq_is_open() const = 0;
};

class OpenCloseGuard
{
  public:
                        OpenCloseGuard(
                            OpenClose &         i_rOpenClose,
                            UINT32              i_nOpenModeInfo = 0 );
                        ~OpenCloseGuard();
                        operator bool() const;

  private:
    // Forbidden:
                        OpenCloseGuard(OpenCloseGuard&);
    OpenCloseGuard &    operator=(OpenCloseGuard&);

    // DATA
    OpenClose &         rOpenClose;
};

// IMPLEMENTATION

inline bool
OpenClose::open( UINT32 i_nOpenModeInfo )
    { return do_open(i_nOpenModeInfo); }
inline void
OpenClose::close()
    { do_close(); }
inline bool
OpenClose::is_open() const
    { return inq_is_open(); }

inline
OpenCloseGuard::OpenCloseGuard( OpenClose & i_rOpenClose,
                                UINT32      i_nOpenModeInfo )
    :   rOpenClose(i_rOpenClose)
    { rOpenClose.open(i_nOpenModeInfo); }
inline
OpenCloseGuard::~OpenCloseGuard()
    { if (rOpenClose.is_open()) rOpenClose.close(); }
inline
OpenCloseGuard::operator bool() const
    { return rOpenClose.is_open(); }

}   // namespace csv

#endif

<|endoftext|>


<commit_before>/*
 * IceWM
 *
 * Copyright (C) 1997-2002 Marko Macek
 */
#include "config.h"

#ifndef NO_CONFIGURE_MENUS
#include "themes.h"

#include "yapp.h"
#include "ymenu.h"
#include "wmmgr.h"
#include "wmprog.h"
#include "ymenuitem.h"
#include "sysdep.h"
#include "base.h"
#include "prefs.h"
#include "yprefs.h"
#include <dirent.h>
#include "wmapp.h"
#include "ascii.h"
#include "wmconfig.h"
#include "wmaction.h"
#include "appnames.h"

#include "intl.h"

void setDefaultTheme(const char *theme) {
    const char *buf = cstrJoin("Theme=\"", theme, "\"\n", NULL);

    setDefault("theme", buf);

    delete [] buf;
}

DTheme::DTheme(IApp *app, YSMListener *smActionListener, const ustring &label, const ustring &theme):
    DObject(app, label, null), fTheme(theme)
{
    this->app = app;
    this->smActionListener = smActionListener;
}

DTheme::~DTheme() {
}

void DTheme::open() {
    if (fTheme == null)
        return;

    cstring cTheme(fTheme);
    setDefaultTheme(cTheme.c_str());

    const char *bg[] = { ICEWMBGEXE, "-r", 0 };
    int pid = app->runProgram(bg[0], bg);
    app->waitProgram(pid);

    smActionListener->handleSMAction(ICEWM_ACTION_RESTARTWM);
}

ThemesMenu::ThemesMenu(IApp *app, YSMListener *smActionListener, YActionListener *wmActionListener, YWindow *parent): ObjectMenu(wmActionListener, parent) {
    this->app = app;
    this->smActionListener = smActionListener;
    this->wmActionListener = wmActionListener;
}

void ThemesMenu::updatePopup() {
    refresh();
}

void ThemesMenu::refresh() {
    removeAll();

    pstring themes("/themes/");
    upath libThemes = YApplication::getLibDir() + themes;
    upath cnfThemes = YApplication::getConfigDir() + themes;
    upath prvThemes = YApplication::getPrivConfDir() + themes;
    upath xdgThemes = YApplication::getXdgConfDir() + themes;

    if (nestedThemeMenuMinNumber)
        themeCount =
            countThemes(libThemes) +
            countThemes(cnfThemes) +
            countThemes(prvThemes) +
            countThemes(xdgThemes);

    findThemes(libThemes, this);
    findThemes(cnfThemes, this);
    findThemes(prvThemes, this);
    findThemes(xdgThemes, this);

    addSeparator();
    add(newThemeItem(app, smActionListener, _("Default"), CONFIG_DEFAULT_THEME));
}

int ThemesMenu::countThemes(const upath& path) {
    DIR *dir(opendir(path.string()));
    int ret=0;

    if (dir != NULL) {
        struct dirent *de;
        while ((de = readdir(dir)) != NULL) {
           // assume that OS caches this. Otherwise, construct a new
           // object to just store the string and an YArrayList of them,
           // read the entries once and work with List contents later

           if(de->d_name[0] == '.')
              continue;
           ret += (path + de->d_name + "default.theme").access(R_OK);
        }
        closedir(dir);
    }
    // this just assumes that there is no other trash
    return ret;
}

ThemesMenu::~ThemesMenu() {
}

YMenuItem * ThemesMenu::newThemeItem(IApp *app, YSMListener *smActionListener, char const *label, char const *relThemeName) {
    DTheme *dtheme = new DTheme(app, smActionListener, label, relThemeName);

    if (dtheme) {
        YMenuItem *item(new DObjectMenuItem(dtheme));

        if (item) {
            item->setChecked(themeName && 0 == strcmp(themeName, relThemeName));
            return item;
        }
    }
    return NULL;
}

void ThemesMenu::findThemes(const upath& path, YMenu *container) {
    char const *const tname("/default.theme");

    bool bNesting = nestedThemeMenuMinNumber && themeCount>nestedThemeMenuMinNumber;

    DIR *dir(opendir(path.string()));

    if (dir != NULL) {
        struct dirent *de;
        while ((de = readdir(dir)) != NULL) {

            if (de->d_name[0] == '.')
                continue;

            YMenuItem *im(NULL);
            upath subdir = path + de->d_name;
            upath npath = subdir + tname;

            if (npath.access(R_OK) == 0) {
                char *relThemeName = cstrJoin(de->d_name, tname, NULL);
                im = newThemeItem(app, smActionListener, de->d_name, relThemeName);
                if (im) {
                    if (bNesting)
                    {
                        char fLetter = ASCII::toUpper(de->d_name[0]);

                        int targetItem = container->findFirstLetRef(fLetter, 0, 1);

                        if (targetItem < 0) // no submenu for us yet, create one
                        {
                            char *smname = strdup("....");
                            smname[0] = fLetter;

                            YMenu *smenu = new YMenu();
                            YMenuItem *smItem = new YMenuItem(smname, 0, null, NULL, smenu);
                            if(smItem && smenu)
                                container->addSorted(smItem, false);
                            targetItem = container->findFirstLetRef(fLetter, 0, 1);
                            if (targetItem < 0)
                            {
                                warn("Failed to add submenu");
                                return;
                            }
                        }
                        container->getItem(targetItem)->getSubmenu()->addSorted(im, false);
                    } else //the default method without Extra SubMenues
                        container->addSorted(im, false);
                }
                delete [] relThemeName;
            }

            if (im) {
                findThemeAlternatives(app, smActionListener, subdir, de->d_name, im);
            }
        }

        closedir(dir);
    }
}

void ThemesMenu::findThemeAlternatives(
    IApp *app,
    YSMListener *smActionListener,
    const upath& path,
    const char *relName,
    YMenuItem *item)
{
    DIR *dir(opendir(path.string()));

    if (dir != NULL) {
        struct dirent *de;
        while ((de = readdir(dir)) != NULL) {

            if (de->d_name[0] == '.')
                continue;

            char const *ext(strstr(de->d_name, ".theme"));

            if (ext != NULL && 0 == strcmp(ext, ".theme") &&
                strcmp(de->d_name, "default.theme"))
            {
                upath npath(path + de->d_name);

                if (npath.access(R_OK) == 0) {
                    YMenu *sub(item->getSubmenu());

                    if (sub == NULL)
                        item->setSubmenu(sub = new YMenu());

                    if (sub) {
                        ustring tname(de->d_name, ext - de->d_name);
                        ustring relThemeName = upath(relName) + de->d_name;
                        sub->add(newThemeItem(app, smActionListener,
                                    cstring(tname), cstring(relThemeName)));
                    }
                }
            }
        }
        closedir(dir);
    }
}
#endif
<commit_msg>correction: access == 0 means yes it exists.<commit_after>/*
 * IceWM
 *
 * Copyright (C) 1997-2002 Marko Macek
 */
#include "config.h"

#ifndef NO_CONFIGURE_MENUS
#include "themes.h"

#include "yapp.h"
#include "ymenu.h"
#include "wmmgr.h"
#include "wmprog.h"
#include "ymenuitem.h"
#include "sysdep.h"
#include "base.h"
#include "prefs.h"
#include "yprefs.h"
#include <dirent.h>
#include "wmapp.h"
#include "ascii.h"
#include "wmconfig.h"
#include "wmaction.h"
#include "appnames.h"

#include "intl.h"

void setDefaultTheme(const char *theme) {
    const char *buf = cstrJoin("Theme=\"", theme, "\"\n", NULL);

    setDefault("theme", buf);

    delete [] buf;
}

DTheme::DTheme(IApp *app, YSMListener *smActionListener, const ustring &label, const ustring &theme):
    DObject(app, label, null), fTheme(theme)
{
    this->app = app;
    this->smActionListener = smActionListener;
}

DTheme::~DTheme() {
}

void DTheme::open() {
    if (fTheme == null)
        return;

    cstring cTheme(fTheme);
    setDefaultTheme(cTheme.c_str());

    const char *bg[] = { ICEWMBGEXE, "-r", 0 };
    int pid = app->runProgram(bg[0], bg);
    app->waitProgram(pid);

    smActionListener->handleSMAction(ICEWM_ACTION_RESTARTWM);
}

ThemesMenu::ThemesMenu(IApp *app, YSMListener *smActionListener, YActionListener *wmActionListener, YWindow *parent): ObjectMenu(wmActionListener, parent) {
    this->app = app;
    this->smActionListener = smActionListener;
    this->wmActionListener = wmActionListener;
}

void ThemesMenu::updatePopup() {
    refresh();
}

void ThemesMenu::refresh() {
    removeAll();

    pstring themes("/themes/");
    upath libThemes = YApplication::getLibDir() + themes;
    upath cnfThemes = YApplication::getConfigDir() + themes;
    upath prvThemes = YApplication::getPrivConfDir() + themes;
    upath xdgThemes = YApplication::getXdgConfDir() + themes;

    if (nestedThemeMenuMinNumber)
        themeCount =
            countThemes(libThemes) +
            countThemes(cnfThemes) +
            countThemes(prvThemes) +
            countThemes(xdgThemes);

    findThemes(libThemes, this);
    findThemes(cnfThemes, this);
    findThemes(prvThemes, this);
    findThemes(xdgThemes, this);

    addSeparator();
    add(newThemeItem(app, smActionListener, _("Default"), CONFIG_DEFAULT_THEME));
}

int ThemesMenu::countThemes(const upath& path) {
    DIR *dir(opendir(path.string()));
    int ret=0;

    if (dir != NULL) {
        struct dirent *de;
        while ((de = readdir(dir)) != NULL) {
           // assume that OS caches this. Otherwise, construct a new
           // object to just store the string and an YArrayList of them,
           // read the entries once and work with List contents later

           if(de->d_name[0] == '.')
              continue;
           ret += (path + de->d_name + "default.theme").access(R_OK) == 0;
        }
        closedir(dir);
    }
    // this just assumes that there is no other trash
    return ret;
}

ThemesMenu::~ThemesMenu() {
}

YMenuItem * ThemesMenu::newThemeItem(IApp *app, YSMListener *smActionListener, char const *label, char const *relThemeName) {
    DTheme *dtheme = new DTheme(app, smActionListener, label, relThemeName);

    if (dtheme) {
        YMenuItem *item(new DObjectMenuItem(dtheme));

        if (item) {
            item->setChecked(themeName && 0 == strcmp(themeName, relThemeName));
            return item;
        }
    }
    return NULL;
}

void ThemesMenu::findThemes(const upath& path, YMenu *container) {
    char const *const tname("/default.theme");

    bool bNesting = nestedThemeMenuMinNumber && themeCount>nestedThemeMenuMinNumber;

    DIR *dir(opendir(path.string()));

    if (dir != NULL) {
        struct dirent *de;
        while ((de = readdir(dir)) != NULL) {

            if (de->d_name[0] == '.')
                continue;

            YMenuItem *im(NULL);
            upath subdir = path + de->d_name;
            upath npath = subdir + tname;

            if (npath.access(R_OK) == 0) {
                char *relThemeName = cstrJoin(de->d_name, tname, NULL);
                im = newThemeItem(app, smActionListener, de->d_name, relThemeName);
                if (im) {
                    if (bNesting)
                    {
                        char fLetter = ASCII::toUpper(de->d_name[0]);

                        int targetItem = container->findFirstLetRef(fLetter, 0, 1);

                        if (targetItem < 0) // no submenu for us yet, create one
                        {
                            char *smname = strdup("....");
                            smname[0] = fLetter;

                            YMenu *smenu = new YMenu();
                            YMenuItem *smItem = new YMenuItem(smname, 0, null, NULL, smenu);
                            if(smItem && smenu)
                                container->addSorted(smItem, false);
                            targetItem = container->findFirstLetRef(fLetter, 0, 1);
                            if (targetItem < 0)
                            {
                                warn("Failed to add submenu");
                                return;
                            }
                        }
                        container->getItem(targetItem)->getSubmenu()->addSorted(im, false);
                    } else //the default method without Extra SubMenues
                        container->addSorted(im, false);
                }
                delete [] relThemeName;
            }

            if (im) {
                findThemeAlternatives(app, smActionListener, subdir, de->d_name, im);
            }
        }

        closedir(dir);
    }
}

void ThemesMenu::findThemeAlternatives(
    IApp *app,
    YSMListener *smActionListener,
    const upath& path,
    const char *relName,
    YMenuItem *item)
{
    DIR *dir(opendir(path.string()));

    if (dir != NULL) {
        struct dirent *de;
        while ((de = readdir(dir)) != NULL) {

            if (de->d_name[0] == '.')
                continue;

            char const *ext(strstr(de->d_name, ".theme"));

            if (ext != NULL && 0 == strcmp(ext, ".theme") &&
                strcmp(de->d_name, "default.theme"))
            {
                upath npath(path + de->d_name);

                if (npath.access(R_OK) == 0) {
                    YMenu *sub(item->getSubmenu());

                    if (sub == NULL)
                        item->setSubmenu(sub = new YMenu());

                    if (sub) {
                        ustring tname(de->d_name, ext - de->d_name);
                        ustring relThemeName = upath(relName) + de->d_name;
                        sub->add(newThemeItem(app, smActionListener,
                                    cstring(tname), cstring(relThemeName)));
                    }
                }
            }
        }
        closedir(dir);
    }
}
#endif
<|endoftext|>


<commit_before>#include <ctime>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <thread>

#include "vast/filesystem.h"
#include "vast/logger.h"
#include "vast/time.h"
#include "vast/util/assert.h"
#include "vast/util/color.h"
#include "vast/util/queue.h"
#include "vast/util/string.h"
#include "vast/util/system.h"

namespace vast {
namespace {

// TODO: replace with thread_local once the compilers implement it.
__thread size_t call_depth = 0;

// TODO: fix potential bugs due to `operator<</>>` occurring in return type via
// decltype.
std::string prettify(char const* pretty_func) {
  auto paren = pretty_func;
  auto c = pretty_func;
  auto templates = 0;
  while (*c && (*c != ' ' || templates > 0)) {
    switch (*c) {
      default:
        break;
      case 'v': {
        static char const* vi = "virtual";
        auto v = vi;
        auto i = c;
        while (*++v == *++i)
          ;
        if (*v == '\0')
          c += 7;
      } break;
      case 't': {
        static char const* tn = "typename";
        auto t = tn;
        auto i = c;
        while (*++t == *++i)
          ;
        if (*t == '\0')
          c += 8;
      } break;
      case '<':
        ++templates;
        break;
      case '>':
        --templates;
        break;
      case '(': {
        VAST_ASSERT(paren == pretty_func);
        paren = c;
      } break;
    }
    ++c;
  }
  // No whitespace found, could be (con|des)tructor.
  if (!c)
    return {pretty_func, c};
  if (*paren != '(')
    while (*paren != '(')
      ++paren;
  // The space occurs before the '(', so we have a return type.
  if (++c < paren)
    return {c, paren};
  // If we went beyond the left parenthesis, we're in a (con|des)tructor.
  while (*paren && *paren != '(')
    ++paren;
  return {pretty_func, paren};
}

} // namespace <anonymous>

struct logger::impl {
  impl() {
    auto start = std::to_string(std::time(nullptr));
    auto pid = std::to_string(util::process_id());
    auto lvl = static_cast<logger::level>(VAST_LOG_LEVEL);
    if (!file(lvl, "vast-log-" + start + '-' + pid + ".log"))
      throw std::runtime_error{"failed to default-initialize logger"};
  }

  bool file(level verbosity, std::string const& filename) {
    // Close and delete existing file first.
    if (log_file_.is_open()) {
      auto empty = log_file_.tellp() == 0;
      log_file_.close();
      if (empty)
        rm(filename_);
    }
    // Set new log file.
    file_level_ = verbosity;
    filename_ = filename;
    if (file_level_ != quiet) {
      if (!filename_.parent().empty() && !exists(filename_.parent())
          && !mkdir(filename_.parent()))
        return false;
      log_file_.open(filename_.str());
      if (!log_file_.is_open())
        return false;
    }
    if (!log_thread_.joinable())
      log_thread_ = std::thread([=] { run(); });
    return log_thread_.joinable();
  }

  bool console(level verbosity, bool colorized) {
    console_level_ = verbosity;
    colorized_ = colorized;
    console_ = true;
    if (!log_thread_.joinable())
      log_thread_ = std::thread([=] { run(); });
    return log_thread_.joinable();
  }

  bool takes(logger::level lvl) const {
    return lvl <= std::max(file_level_, console_level_);
  }

  void log(message&& msg) {
    messages_.push(std::move(msg));
  }

  void run() {
    while (true) {
      auto m = messages_.pop();
      // A quit message is the termination signal, as it would have already
      // been filtered out beforehand.
      if (m.lvl() == quiet) {
        auto empty = log_file_.tellp() == 0;
        if (log_file_.is_open())
          log_file_.close();
        if (empty)
          rm(filename_);
        return;
      }
      // If the message contains newlines, split it up into multiple ones to
      // preserve the correct indentation.
      auto msg = m.msg();
      for (auto& split : util::split(msg.begin(), msg.end(), "\n")) {
        auto line = std::string{split.first, split.second};
        if (log_file_ && m.lvl() <= file_level_) {
          log_file_ << std::setprecision(15) << std::setw(16) << std::left
                    << std::setfill('0') << m.timestamp() << " 0x"
                    << std::setw(14) << std::setfill(' ') << m.thread_id()
                    << ' ' << m.lvl() << ' ';
          if (! m.context().empty())
            log_file_ << m.context() << ' ';
          log_file_ << line << std::endl;
        }
        if (console_ && m.lvl() <= console_level_) {
          if (colorized_) {
            switch (m.lvl()) {
              default:
                break;
              case error:
                std::cerr << util::color::bold_red;
                break;
              case warn:
                std::cerr << util::color::bold_yellow;
                break;
              case info:
                std::cerr << util::color::bold_green;
                break;
              case verbose:
                std::cerr << util::color::bold_cyan;
                break;
              case debug:
              case trace:
                std::cerr << util::color::bold_blue;
                break;
            }
          }
          std::cerr << "::";
          if (colorized_) {
            std::cerr << util::color::reset;
            if (! m.context().empty()) {
              std::cerr << ' ' << util::color::cyan << m.context();
              std::cerr << util::color::reset;
            }
          }
          std::cerr << ' ' << line << std::endl;
        }
      }
    }
  }

  void stop() {
    // A quiet message never arrives at the log thread, hence we use it to
    // signal termination.
    messages_.push({});
    log_thread_.join();
  }

  level console_level_ = logger::quiet;
  level file_level_ = logger::quiet;
  path filename_;
  std::ofstream log_file_;
  bool console_ = false;
  bool colorized_ = false;
  std::thread log_thread_;
  util::queue<message> messages_;
};

logger::message::message(level lvl) : lvl_{lvl} {
  std::ostringstream ss;
  ss << std::hex << std::this_thread::get_id();
  thread_id_ = ss.str();
}

logger::message::message(message const& other)
  : lvl_{other.lvl_},
    timestamp_{other.timestamp_},
    thread_id_{other.thread_id_},
    context_{other.context_},
    function_{other.function_} {
  ss_ << other.msg();
}

void logger::message::coin() {
  timestamp_ = time::now().time_since_epoch().double_seconds();
}

void logger::message::function(char const* f) {
  function_ = prettify(f);
}

void logger::message::clear() {
  ss_.str("");
  ss_.clear();
}

bool logger::message::empty() {
  return ss_.tellp() == 0;
}

logger::level logger::message::lvl() const {
  return lvl_;
}

double logger::message::timestamp() const {
  return timestamp_;
}

std::string const& logger::message::thread_id() const {
  return thread_id_;
}

std::string const& logger::message::context() const {
  return context_;
}

std::string const& logger::message::function() const {
  return function_;
}

std::string logger::message::msg() const {
  return ss_.str();
}

logger::message& operator<<(logger::message& msg, std::nullptr_t) {
  return msg;
}

std::ostream& operator<<(std::ostream& stream, logger::level lvl) {
  switch (lvl) {
    default:
      stream << "invalid";
      break;
    case logger::quiet:
      stream << "quiet  ";
      break;
    case logger::error:
      stream << "error  ";
      break;
    case logger::warn:
      stream << "warning";
      break;
    case logger::info:
      stream << "info   ";
      break;
    case logger::verbose:
      stream << "verbose";
      break;
    case logger::debug:
      stream << "debug  ";
      break;
    case logger::trace:
      stream << "trace  ";
      break;
  }
  return stream;
}

logger::tracer::tracer(message&& msg) : msg_{std::move(msg)} {
  ++call_depth;
  fill(right_arrow);
  msg_ << "::" << msg_.function() << ' ';
}

void logger::tracer::fill(fill_type t) {
  VAST_ASSERT(call_depth >= 1);
  std::string f(3 + call_depth, '-');
  f[f.size() - 1] = ' ';
  f[0] = '|';
  if (t == right_arrow)
    f[f.size() - 2] = '\\';
  else if (t == left_arrow)
    f[f.size() - 2] = '/';
  else if (t == bar)
    f[f.size() - 2] = '|';
  msg_ << f << ' ';
}

void logger::tracer::commit() {
  msg_.coin();
  instance()->log(msg_);
  msg_.clear();
}

void logger::tracer::reset(bool exit) {
  msg_.clear();

  if (exit) {
    fill(left_arrow);
    msg_ << "::" << msg_.function() << ' ';
  } else {
    fill(bar);
  }
}

logger::tracer::~tracer() {
  if (msg_.empty()) {
    fill(left_arrow);
    msg_ << "::" << msg_.function();
  }
  commit();
  --call_depth;
}

bool logger::file(level verbosity, std::string const& filename) {
  return instance()->impl_->file(verbosity, filename);
}

bool logger::console(level verbosity) {
  return instance()->impl_->console(verbosity, false);
}

bool logger::console_colorized(level verbosity) {
  return instance()->impl_->console(verbosity, true);
}

void logger::log(message msg) {
  instance()->impl_->log(std::move(msg));
}

bool logger::takes(logger::level lvl) {
  return instance()->impl_->takes(lvl);
}

logger::message logger::make_message(logger::level lvl, std::string ctx,
                                     char const* fun) {
  VAST_ASSERT(instance()->impl_);
  auto& impl = instance()->impl_;
  message m{lvl};
  if (impl->console_level_ == trace || impl->file_level_ == trace)
    m.function_ = prettify(fun);
  m.context_ = std::move(ctx);
  m.coin();
  return m;
}

logger* logger::create() {
  return new logger;
}

void logger::initialize() {
  impl_ = std::make_unique<impl>();
}

void logger::destroy() {
  impl_->stop();
  delete this;
}

void logger::dispose() {
  delete this;
}

} // namespace vast
<commit_msg>Do not flush log file with every line<commit_after>#include <ctime>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <thread>

#include "vast/filesystem.h"
#include "vast/logger.h"
#include "vast/time.h"
#include "vast/util/assert.h"
#include "vast/util/color.h"
#include "vast/util/queue.h"
#include "vast/util/string.h"
#include "vast/util/system.h"

namespace vast {
namespace {

// TODO: replace with thread_local once the compilers implement it.
__thread size_t call_depth = 0;

// TODO: fix potential bugs due to `operator<</>>` occurring in return type via
// decltype.
std::string prettify(char const* pretty_func) {
  auto paren = pretty_func;
  auto c = pretty_func;
  auto templates = 0;
  while (*c && (*c != ' ' || templates > 0)) {
    switch (*c) {
      default:
        break;
      case 'v': {
        static char const* vi = "virtual";
        auto v = vi;
        auto i = c;
        while (*++v == *++i)
          ;
        if (*v == '\0')
          c += 7;
      } break;
      case 't': {
        static char const* tn = "typename";
        auto t = tn;
        auto i = c;
        while (*++t == *++i)
          ;
        if (*t == '\0')
          c += 8;
      } break;
      case '<':
        ++templates;
        break;
      case '>':
        --templates;
        break;
      case '(': {
        VAST_ASSERT(paren == pretty_func);
        paren = c;
      } break;
    }
    ++c;
  }
  // No whitespace found, could be (con|des)tructor.
  if (!c)
    return {pretty_func, c};
  if (*paren != '(')
    while (*paren != '(')
      ++paren;
  // The space occurs before the '(', so we have a return type.
  if (++c < paren)
    return {c, paren};
  // If we went beyond the left parenthesis, we're in a (con|des)tructor.
  while (*paren && *paren != '(')
    ++paren;
  return {pretty_func, paren};
}

} // namespace <anonymous>

struct logger::impl {
  impl() {
    auto start = std::to_string(std::time(nullptr));
    auto pid = std::to_string(util::process_id());
    auto lvl = static_cast<logger::level>(VAST_LOG_LEVEL);
    if (!file(lvl, "vast-log-" + start + '-' + pid + ".log"))
      throw std::runtime_error{"failed to default-initialize logger"};
  }

  bool file(level verbosity, std::string const& filename) {
    // Close and delete existing file first.
    if (log_file_.is_open()) {
      auto empty = log_file_.tellp() == 0;
      log_file_.close();
      if (empty)
        rm(filename_);
    }
    // Set new log file.
    file_level_ = verbosity;
    filename_ = filename;
    if (file_level_ != quiet) {
      if (!filename_.parent().empty() && !exists(filename_.parent())
          && !mkdir(filename_.parent()))
        return false;
      log_file_.open(filename_.str());
      if (!log_file_.is_open())
        return false;
    }
    if (!log_thread_.joinable())
      log_thread_ = std::thread([=] { run(); });
    return log_thread_.joinable();
  }

  bool console(level verbosity, bool colorized) {
    console_level_ = verbosity;
    colorized_ = colorized;
    console_ = true;
    if (!log_thread_.joinable())
      log_thread_ = std::thread([=] { run(); });
    return log_thread_.joinable();
  }

  bool takes(logger::level lvl) const {
    return lvl <= std::max(file_level_, console_level_);
  }

  void log(message&& msg) {
    messages_.push(std::move(msg));
  }

  void run() {
    while (true) {
      auto m = messages_.pop();
      // A quit message is the termination signal, as it would have already
      // been filtered out beforehand.
      if (m.lvl() == quiet) {
        auto empty = log_file_.tellp() == 0;
        if (log_file_.is_open())
          log_file_.close();
        if (empty)
          rm(filename_);
        return;
      }
      // If the message contains newlines, split it up into multiple ones to
      // preserve the correct indentation.
      auto msg = m.msg();
      for (auto& split : util::split(msg.begin(), msg.end(), "\n")) {
        auto line = std::string{split.first, split.second};
        if (log_file_ && m.lvl() <= file_level_) {
          log_file_ << std::setprecision(15) << std::setw(16) << std::left
                    << std::setfill('0') << m.timestamp() << " 0x"
                    << std::setw(14) << std::setfill(' ') << m.thread_id()
                    << ' ' << m.lvl() << ' ';
          if (! m.context().empty())
            log_file_ << m.context() << ' ';
          log_file_ << line << '\n';
        }
        if (console_ && m.lvl() <= console_level_) {
          if (colorized_) {
            switch (m.lvl()) {
              default:
                break;
              case error:
                std::cerr << util::color::bold_red;
                break;
              case warn:
                std::cerr << util::color::bold_yellow;
                break;
              case info:
                std::cerr << util::color::bold_green;
                break;
              case verbose:
                std::cerr << util::color::bold_cyan;
                break;
              case debug:
              case trace:
                std::cerr << util::color::bold_blue;
                break;
            }
          }
          std::cerr << "::";
          if (colorized_) {
            std::cerr << util::color::reset;
            if (! m.context().empty()) {
              std::cerr << ' ' << util::color::cyan << m.context();
              std::cerr << util::color::reset;
            }
          }
          std::cerr << ' ' << line << std::endl;
        }
      }
    }
  }

  void stop() {
    // A quiet message never arrives at the log thread, hence we use it to
    // signal termination.
    messages_.push({});
    log_thread_.join();
  }

  level console_level_ = logger::quiet;
  level file_level_ = logger::quiet;
  path filename_;
  std::ofstream log_file_;
  bool console_ = false;
  bool colorized_ = false;
  std::thread log_thread_;
  util::queue<message> messages_;
};

logger::message::message(level lvl) : lvl_{lvl} {
  std::ostringstream ss;
  ss << std::hex << std::this_thread::get_id();
  thread_id_ = ss.str();
}

logger::message::message(message const& other)
  : lvl_{other.lvl_},
    timestamp_{other.timestamp_},
    thread_id_{other.thread_id_},
    context_{other.context_},
    function_{other.function_} {
  ss_ << other.msg();
}

void logger::message::coin() {
  timestamp_ = time::now().time_since_epoch().double_seconds();
}

void logger::message::function(char const* f) {
  function_ = prettify(f);
}

void logger::message::clear() {
  ss_.str("");
  ss_.clear();
}

bool logger::message::empty() {
  return ss_.tellp() == 0;
}

logger::level logger::message::lvl() const {
  return lvl_;
}

double logger::message::timestamp() const {
  return timestamp_;
}

std::string const& logger::message::thread_id() const {
  return thread_id_;
}

std::string const& logger::message::context() const {
  return context_;
}

std::string const& logger::message::function() const {
  return function_;
}

std::string logger::message::msg() const {
  return ss_.str();
}

logger::message& operator<<(logger::message& msg, std::nullptr_t) {
  return msg;
}

std::ostream& operator<<(std::ostream& stream, logger::level lvl) {
  switch (lvl) {
    default:
      stream << "invalid";
      break;
    case logger::quiet:
      stream << "quiet  ";
      break;
    case logger::error:
      stream << "error  ";
      break;
    case logger::warn:
      stream << "warning";
      break;
    case logger::info:
      stream << "info   ";
      break;
    case logger::verbose:
      stream << "verbose";
      break;
    case logger::debug:
      stream << "debug  ";
      break;
    case logger::trace:
      stream << "trace  ";
      break;
  }
  return stream;
}

logger::tracer::tracer(message&& msg) : msg_{std::move(msg)} {
  ++call_depth;
  fill(right_arrow);
  msg_ << "::" << msg_.function() << ' ';
}

void logger::tracer::fill(fill_type t) {
  VAST_ASSERT(call_depth >= 1);
  std::string f(3 + call_depth, '-');
  f[f.size() - 1] = ' ';
  f[0] = '|';
  if (t == right_arrow)
    f[f.size() - 2] = '\\';
  else if (t == left_arrow)
    f[f.size() - 2] = '/';
  else if (t == bar)
    f[f.size() - 2] = '|';
  msg_ << f << ' ';
}

void logger::tracer::commit() {
  msg_.coin();
  instance()->log(msg_);
  msg_.clear();
}

void logger::tracer::reset(bool exit) {
  msg_.clear();

  if (exit) {
    fill(left_arrow);
    msg_ << "::" << msg_.function() << ' ';
  } else {
    fill(bar);
  }
}

logger::tracer::~tracer() {
  if (msg_.empty()) {
    fill(left_arrow);
    msg_ << "::" << msg_.function();
  }
  commit();
  --call_depth;
}

bool logger::file(level verbosity, std::string const& filename) {
  return instance()->impl_->file(verbosity, filename);
}

bool logger::console(level verbosity) {
  return instance()->impl_->console(verbosity, false);
}

bool logger::console_colorized(level verbosity) {
  return instance()->impl_->console(verbosity, true);
}

void logger::log(message msg) {
  instance()->impl_->log(std::move(msg));
}

bool logger::takes(logger::level lvl) {
  return instance()->impl_->takes(lvl);
}

logger::message logger::make_message(logger::level lvl, std::string ctx,
                                     char const* fun) {
  VAST_ASSERT(instance()->impl_);
  auto& impl = instance()->impl_;
  message m{lvl};
  if (impl->console_level_ == trace || impl->file_level_ == trace)
    m.function_ = prettify(fun);
  m.context_ = std::move(ctx);
  m.coin();
  return m;
}

logger* logger::create() {
  return new logger;
}

void logger::initialize() {
  impl_ = std::make_unique<impl>();
}

void logger::destroy() {
  impl_->stop();
  delete this;
}

void logger::dispose() {
  delete this;
}

} // namespace vast
<|endoftext|>


<commit_before>// Copyright (c) 2020 ASMlover. 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 ofconditions 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 materialsprovided with the
//    distribution.
//
// 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 HOLDER 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.
#include "chunk.hh"

namespace tadpole {
}
<commit_msg>:construction: chore(chunk): add helper for chunk<commit_after>// Copyright (c) 2020 ASMlover. 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 ofconditions 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 materialsprovided with the
//    distribution.
//
// 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 HOLDER 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.
#include <iostream>
#include "chunk.hh"

namespace tadpole {

inline sz_t dis_compound(
    Chunk* chunk, const char* msg, sz_t i, bool with_constant = false) noexcept {
  auto c = chunk->get_code(i + 1);
  std::fprintf(stdout, "%-16s %4d", msg, c);
  if (with_constant)
    std::cout << " `" << chunk->get_constant(c) << "`";
  std::cout << std::endl;

  return i + 2;
}

inline sz_t dis_simple(Chunk* chunk, const char* msg, sz_t i, int n = 0) noexcept {
  std::cout << msg;
  if (n > 0)
    std::cout << "_" << n;
  std::cout << std::endl;

  return i + 1;
}

void Chunk::dis(strv_t prompt) {
  std::cout << "========= [" << prompt << "] =========" << std::endl;
  for (sz_t offset = 0; offset < codes_count();)
    offset = dis_code(offset);
}

sz_t Chunk::dis_code(sz_t offset) {
  return offset;
}

}
<|endoftext|>