<commit_before>/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "logging/rtc_event_log/rtc_event_log.h"

#include <atomic>
#include <deque>
#include <functional>
#include <limits>
#include <memory>
#include <utility>
#include <vector>

#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h"
#include "logging/rtc_event_log/events/rtc_event_logging_started.h"
#include "logging/rtc_event_log/events/rtc_event_logging_stopped.h"
#include "logging/rtc_event_log/output/rtc_event_log_output_file.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/event.h"
#include "rtc_base/logging.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/safe_conversions.h"
#include "rtc_base/sequenced_task_checker.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/thread_annotations.h"

namespace webrtc {

#ifdef ENABLE_RTC_EVENT_LOG

namespace {
const int kEventsInHistory = 10000;

// Observe a limit on the number of concurrent logs, so as not to run into
// OS-imposed limits on open files and/or threads/task-queues.
// TODO(eladalon): Known issue - there's a race over |rtc_event_log_count|.
std::atomic<int> rtc_event_log_count(0);

// TODO(eladalon): This class exists because C++11 doesn't allow transferring a
// unique_ptr to a lambda (a copy constructor is required). We should get
// rid of this when we move to C++14.
template <typename T>
class ResourceOwningTask final : public rtc::QueuedTask {
 public:
  ResourceOwningTask(std::unique_ptr<T> resource,
                     std::function<void(std::unique_ptr<T>)> handler)
      : resource_(std::move(resource)), handler_(handler) {}

  bool Run() override {
    handler_(std::move(resource_));
    return true;
  }

 private:
  std::unique_ptr<T> resource_;
  std::function<void(std::unique_ptr<T>)> handler_;
};

std::unique_ptr<RtcEventLogEncoder> CreateEncoder(
    RtcEventLog::EncodingType type) {
  switch (type) {
    case RtcEventLog::EncodingType::Legacy:
      return rtc::MakeUnique<RtcEventLogEncoderLegacy>();
    default:
      LOG(LS_ERROR) << "Unknown RtcEventLog encoder type (" << int(type) << ")";
      RTC_NOTREACHED();
      return std::unique_ptr<RtcEventLogEncoder>(nullptr);
  }
}

class RtcEventLogImpl final : public RtcEventLog {
 public:
  explicit RtcEventLogImpl(std::unique_ptr<RtcEventLogEncoder> event_encoder);
  ~RtcEventLogImpl() override;

  // TODO(eladalon): We should change these name to reflect that what we're
  // actually starting/stopping is the output of the log, not the log itself.
  bool StartLogging(std::unique_ptr<RtcEventLogOutput> output) override;
  void StopLogging() override;

  void Log(std::unique_ptr<RtcEvent> event) override;

 private:
  // Appends an event to the output protobuf string, returning true on success.
  // Fails and returns false in case the limit on output size prevents the
  // event from being added; in this case, the output string is left unchanged.
  // The event is encoded before being appended.
  // We could have avoided this, because the output repeats the check, but this
  // way, we minimize the number of lock acquisitions, task switches, etc.,
  // that might be associated with each call to RtcEventLogOutput::Write().
  bool AppendEventToString(const RtcEvent& event,
                           std::string* output_string) RTC_WARN_UNUSED_RESULT;

  void LogToMemory(std::unique_ptr<RtcEvent> event) RTC_RUN_ON(&task_queue_);

  void LogEventsFromMemoryToOutput() RTC_RUN_ON(&task_queue_);
  void LogToOutput(std::unique_ptr<RtcEvent> event) RTC_RUN_ON(&task_queue_);
  void StopOutput() RTC_RUN_ON(&task_queue_);

  void WriteToOutput(const std::string& output_string) RTC_RUN_ON(&task_queue_);

  void StopLoggingInternal() RTC_RUN_ON(&task_queue_);

  // Make sure that the event log is "managed" - created/destroyed, as well
  // as started/stopped - from the same thread/task-queue.
  rtc::SequencedTaskChecker owner_sequence_checker_;

  // History containing all past configuration events.
  std::vector<std::unique_ptr<RtcEvent>> config_history_
      RTC_ACCESS_ON(task_queue_);

  // History containing the most recent (non-configuration) events (~10s).
  std::deque<std::unique_ptr<RtcEvent>> history_ RTC_ACCESS_ON(task_queue_);

  size_t max_size_bytes_ RTC_ACCESS_ON(task_queue_);
  size_t written_bytes_ RTC_ACCESS_ON(task_queue_);

  std::unique_ptr<RtcEventLogEncoder> event_encoder_ RTC_ACCESS_ON(task_queue_);
  std::unique_ptr<RtcEventLogOutput> event_output_ RTC_ACCESS_ON(task_queue_);

  // Keep this last to ensure it destructs first, or else tasks living on the
  // queue might access other members after they've been torn down.
  rtc::TaskQueue task_queue_;

  RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogImpl);
};

RtcEventLogImpl::RtcEventLogImpl(
    std::unique_ptr<RtcEventLogEncoder> event_encoder)
    : max_size_bytes_(std::numeric_limits<decltype(max_size_bytes_)>::max()),
      written_bytes_(0),
      event_encoder_(std::move(event_encoder)),
      task_queue_("rtc_event_log") {}

RtcEventLogImpl::~RtcEventLogImpl() {
  RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);

  // If we're logging to the output, this will stop that. Blocking function.
  StopLogging();

  int count = std::atomic_fetch_sub(&rtc_event_log_count, 1) - 1;
  RTC_DCHECK_GE(count, 0);
}

bool RtcEventLogImpl::StartLogging(std::unique_ptr<RtcEventLogOutput> output) {
  RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);

  if (!output->IsActive()) {
    return false;
  }

  LOG(LS_INFO) << "Starting WebRTC event log.";

  // |start_event| captured by value. This is done here because we want the
  // timestamp to reflect when StartLogging() was called; not the queueing
  // delay of the TaskQueue.
  // This is a bit inefficient - especially since we copy again to get it
  // to comply with LogToOutput()'s signature - but it's a small problem.
  RtcEventLoggingStarted start_event;

  auto start = [this, start_event](std::unique_ptr<RtcEventLogOutput> output) {
    RTC_DCHECK_RUN_ON(&task_queue_);
    RTC_DCHECK(output->IsActive());
    event_output_ = std::move(output);
    LogToOutput(rtc::MakeUnique<RtcEventLoggingStarted>(start_event));
    LogEventsFromMemoryToOutput();
  };

  task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<RtcEventLogOutput>>(
      std::move(output), start));

  return true;
}

void RtcEventLogImpl::StopLogging() {
  RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);

  LOG(LS_INFO) << "Stopping WebRTC event log.";

  rtc::Event output_stopped(true, false);

  task_queue_.PostTask([this, &output_stopped]() {
    RTC_DCHECK_RUN_ON(&task_queue_);
    StopLoggingInternal();
    output_stopped.Set();
  });

  output_stopped.Wait(rtc::Event::kForever);

  LOG(LS_INFO) << "WebRTC event log successfully stopped.";
}

void RtcEventLogImpl::Log(std::unique_ptr<RtcEvent> event) {
  RTC_DCHECK(event);

  auto event_handler = [this](std::unique_ptr<RtcEvent> unencoded_event) {
    RTC_DCHECK_RUN_ON(&task_queue_);
    if (event_output_) {
      LogToOutput(std::move(unencoded_event));
    } else {
      LogToMemory(std::move(unencoded_event));
    }
  };

  task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<RtcEvent>>(
      std::move(event), event_handler));
}

bool RtcEventLogImpl::AppendEventToString(const RtcEvent& event,
                                          std::string* output_string) {
  RTC_DCHECK_RUN_ON(&task_queue_);

  std::string encoded_event = event_encoder_->Encode(event);

  bool appended;
  size_t potential_new_size =
      written_bytes_ + output_string->size() + encoded_event.length();
  if (potential_new_size <= max_size_bytes_) {
    // TODO(eladalon): This is inefficient; fix this in a separate CL.
    *output_string += encoded_event;
    appended = true;
  } else {
    appended = false;
  }

  return appended;
}

void RtcEventLogImpl::LogToMemory(std::unique_ptr<RtcEvent> event) {
  RTC_DCHECK(!event_output_);

  if (event->IsConfigEvent()) {
    config_history_.push_back(std::move(event));
  } else {
    history_.push_back(std::move(event));
    if (history_.size() > kEventsInHistory) {
      history_.pop_front();
    }
  }
}

void RtcEventLogImpl::LogEventsFromMemoryToOutput() {
  RTC_DCHECK(event_output_ && event_output_->IsActive());

  std::string output_string;

  // Serialize the config information for all old streams, including streams
  // which were already logged to previous outputs.
  bool appended = true;
  for (auto& event : config_history_) {
    if (!AppendEventToString(*event, &output_string)) {
      appended = false;
      break;
    }
  }

  // Serialize the events in the event queue.
  while (appended && !history_.empty()) {
    appended = AppendEventToString(*history_.front(), &output_string);
    if (appended) {
      // Known issue - if writing to the output fails, these events will have
      // been lost. If we try to open a new output, these events will be missing
      // from it.
      history_.pop_front();
    }
  }

  WriteToOutput(output_string);

  if (!appended) {
    // Successful partial write to the output. Some events could not be written;
    // the output should be closed, to avoid gaps.
    StopOutput();
  }
}

void RtcEventLogImpl::LogToOutput(std::unique_ptr<RtcEvent> event) {
  RTC_DCHECK(event_output_ && event_output_->IsActive());

  std::string output_string;

  bool appended = AppendEventToString(*event, &output_string);

  if (event->IsConfigEvent()) {
    // Config events need to be kept in memory too, so that they may be
    // rewritten into future outputs, too.
    config_history_.push_back(std::move(event));
  }

  if (!appended) {
    if (!event->IsConfigEvent()) {
      // This event will not fit into the output; push it into |history_|
      // instead, so that it might be logged into the next output (if any).
      history_.push_back(std::move(event));
    }
    StopOutput();
    return;
  }

  WriteToOutput(output_string);
}

void RtcEventLogImpl::StopOutput() {
  max_size_bytes_ = std::numeric_limits<decltype(max_size_bytes_)>::max();
  written_bytes_ = 0;
  event_output_.reset();
}

void RtcEventLogImpl::StopLoggingInternal() {
  if (event_output_) {
    RTC_DCHECK(event_output_->IsActive());
    event_output_->Write(
        event_encoder_->Encode(*rtc::MakeUnique<RtcEventLoggingStopped>()));
  }
  StopOutput();
}

void RtcEventLogImpl::WriteToOutput(const std::string& output_string) {
  RTC_DCHECK(event_output_ && event_output_->IsActive());
  if (!event_output_->Write(output_string)) {
    LOG(LS_ERROR) << "Failed to write RTC event to output.";
    // The first failure closes the output.
    RTC_DCHECK(!event_output_->IsActive());
    StopOutput();  // Clean-up.
    return;
  }
  written_bytes_ += output_string.size();
}

}  // namespace

#endif  // ENABLE_RTC_EVENT_LOG

// RtcEventLog member functions.
std::unique_ptr<RtcEventLog> RtcEventLog::Create(EncodingType encoding_type) {
#ifdef ENABLE_RTC_EVENT_LOG
  // TODO(eladalon): Known issue - there's a race over |rtc_event_log_count|.
  constexpr int kMaxLogCount = 5;
  int count = 1 + std::atomic_fetch_add(&rtc_event_log_count, 1);
  if (count > kMaxLogCount) {
    LOG(LS_WARNING) << "Denied creation of additional WebRTC event logs. "
                    << count - 1 << " logs open already.";
    std::atomic_fetch_sub(&rtc_event_log_count, 1);
    return CreateNull();
  }
  auto encoder = CreateEncoder(encoding_type);
  return rtc::MakeUnique<RtcEventLogImpl>(std::move(encoder));
#else
  return CreateNull();
#endif  // ENABLE_RTC_EVENT_LOG
}

std::unique_ptr<RtcEventLog> RtcEventLog::CreateNull() {
  return std::unique_ptr<RtcEventLog>(new RtcEventLogNullImpl());
}

}  // namespace webrtc
<commit_msg>Prevent unbounded memory consumption through RtcEventLogImpl::config_history_<commit_after>/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "logging/rtc_event_log/rtc_event_log.h"

#include <atomic>
#include <deque>
#include <functional>
#include <limits>
#include <memory>
#include <utility>
#include <vector>

#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h"
#include "logging/rtc_event_log/events/rtc_event_logging_started.h"
#include "logging/rtc_event_log/events/rtc_event_logging_stopped.h"
#include "logging/rtc_event_log/output/rtc_event_log_output_file.h"
#include "rtc_base/checks.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/event.h"
#include "rtc_base/logging.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/safe_conversions.h"
#include "rtc_base/sequenced_task_checker.h"
#include "rtc_base/task_queue.h"
#include "rtc_base/thread_annotations.h"

namespace webrtc {

#ifdef ENABLE_RTC_EVENT_LOG

namespace {
constexpr size_t kMaxEventsInHistory = 10000;
// The config-history is supposed to be unbounded, but needs to have some bound
// to prevent an attack via unreasonable memory use.
constexpr size_t kMaxEventsInConfigHistory = 1000;

// Observe a limit on the number of concurrent logs, so as not to run into
// OS-imposed limits on open files and/or threads/task-queues.
// TODO(eladalon): Known issue - there's a race over |rtc_event_log_count|.
std::atomic<int> rtc_event_log_count(0);

// TODO(eladalon): This class exists because C++11 doesn't allow transferring a
// unique_ptr to a lambda (a copy constructor is required). We should get
// rid of this when we move to C++14.
template <typename T>
class ResourceOwningTask final : public rtc::QueuedTask {
 public:
  ResourceOwningTask(std::unique_ptr<T> resource,
                     std::function<void(std::unique_ptr<T>)> handler)
      : resource_(std::move(resource)), handler_(handler) {}

  bool Run() override {
    handler_(std::move(resource_));
    return true;
  }

 private:
  std::unique_ptr<T> resource_;
  std::function<void(std::unique_ptr<T>)> handler_;
};

std::unique_ptr<RtcEventLogEncoder> CreateEncoder(
    RtcEventLog::EncodingType type) {
  switch (type) {
    case RtcEventLog::EncodingType::Legacy:
      return rtc::MakeUnique<RtcEventLogEncoderLegacy>();
    default:
      LOG(LS_ERROR) << "Unknown RtcEventLog encoder type (" << int(type) << ")";
      RTC_NOTREACHED();
      return std::unique_ptr<RtcEventLogEncoder>(nullptr);
  }
}

class RtcEventLogImpl final : public RtcEventLog {
 public:
  explicit RtcEventLogImpl(std::unique_ptr<RtcEventLogEncoder> event_encoder);
  ~RtcEventLogImpl() override;

  // TODO(eladalon): We should change these name to reflect that what we're
  // actually starting/stopping is the output of the log, not the log itself.
  bool StartLogging(std::unique_ptr<RtcEventLogOutput> output) override;
  void StopLogging() override;

  void Log(std::unique_ptr<RtcEvent> event) override;

 private:
  // Appends an event to the output protobuf string, returning true on success.
  // Fails and returns false in case the limit on output size prevents the
  // event from being added; in this case, the output string is left unchanged.
  // The event is encoded before being appended.
  // We could have avoided this, because the output repeats the check, but this
  // way, we minimize the number of lock acquisitions, task switches, etc.,
  // that might be associated with each call to RtcEventLogOutput::Write().
  bool AppendEventToString(const RtcEvent& event,
                           std::string* output_string) RTC_WARN_UNUSED_RESULT;

  void LogToMemory(std::unique_ptr<RtcEvent> event) RTC_RUN_ON(&task_queue_);

  void LogEventsFromMemoryToOutput() RTC_RUN_ON(&task_queue_);
  void LogToOutput(std::unique_ptr<RtcEvent> event) RTC_RUN_ON(&task_queue_);
  void StopOutput() RTC_RUN_ON(&task_queue_);

  void WriteToOutput(const std::string& output_string) RTC_RUN_ON(&task_queue_);

  void StopLoggingInternal() RTC_RUN_ON(&task_queue_);

  // Make sure that the event log is "managed" - created/destroyed, as well
  // as started/stopped - from the same thread/task-queue.
  rtc::SequencedTaskChecker owner_sequence_checker_;

  // History containing all past configuration events.
  std::deque<std::unique_ptr<RtcEvent>> config_history_
      RTC_ACCESS_ON(task_queue_);

  // History containing the most recent (non-configuration) events (~10s).
  std::deque<std::unique_ptr<RtcEvent>> history_ RTC_ACCESS_ON(task_queue_);

  size_t max_size_bytes_ RTC_ACCESS_ON(task_queue_);
  size_t written_bytes_ RTC_ACCESS_ON(task_queue_);

  std::unique_ptr<RtcEventLogEncoder> event_encoder_ RTC_ACCESS_ON(task_queue_);
  std::unique_ptr<RtcEventLogOutput> event_output_ RTC_ACCESS_ON(task_queue_);

  // Keep this last to ensure it destructs first, or else tasks living on the
  // queue might access other members after they've been torn down.
  rtc::TaskQueue task_queue_;

  RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogImpl);
};

RtcEventLogImpl::RtcEventLogImpl(
    std::unique_ptr<RtcEventLogEncoder> event_encoder)
    : max_size_bytes_(std::numeric_limits<decltype(max_size_bytes_)>::max()),
      written_bytes_(0),
      event_encoder_(std::move(event_encoder)),
      task_queue_("rtc_event_log") {}

RtcEventLogImpl::~RtcEventLogImpl() {
  RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);

  // If we're logging to the output, this will stop that. Blocking function.
  StopLogging();

  int count = std::atomic_fetch_sub(&rtc_event_log_count, 1) - 1;
  RTC_DCHECK_GE(count, 0);
}

bool RtcEventLogImpl::StartLogging(std::unique_ptr<RtcEventLogOutput> output) {
  RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);

  if (!output->IsActive()) {
    return false;
  }

  LOG(LS_INFO) << "Starting WebRTC event log.";

  // |start_event| captured by value. This is done here because we want the
  // timestamp to reflect when StartLogging() was called; not the queueing
  // delay of the TaskQueue.
  // This is a bit inefficient - especially since we copy again to get it
  // to comply with LogToOutput()'s signature - but it's a small problem.
  RtcEventLoggingStarted start_event;

  auto start = [this, start_event](std::unique_ptr<RtcEventLogOutput> output) {
    RTC_DCHECK_RUN_ON(&task_queue_);
    RTC_DCHECK(output->IsActive());
    event_output_ = std::move(output);
    LogToOutput(rtc::MakeUnique<RtcEventLoggingStarted>(start_event));
    LogEventsFromMemoryToOutput();
  };

  task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<RtcEventLogOutput>>(
      std::move(output), start));

  return true;
}

void RtcEventLogImpl::StopLogging() {
  RTC_DCHECK_CALLED_SEQUENTIALLY(&owner_sequence_checker_);

  LOG(LS_INFO) << "Stopping WebRTC event log.";

  rtc::Event output_stopped(true, false);

  task_queue_.PostTask([this, &output_stopped]() {
    RTC_DCHECK_RUN_ON(&task_queue_);
    StopLoggingInternal();
    output_stopped.Set();
  });

  output_stopped.Wait(rtc::Event::kForever);

  LOG(LS_INFO) << "WebRTC event log successfully stopped.";
}

void RtcEventLogImpl::Log(std::unique_ptr<RtcEvent> event) {
  RTC_DCHECK(event);

  auto event_handler = [this](std::unique_ptr<RtcEvent> unencoded_event) {
    RTC_DCHECK_RUN_ON(&task_queue_);
    if (event_output_) {
      LogToOutput(std::move(unencoded_event));
    } else {
      LogToMemory(std::move(unencoded_event));
    }
  };

  task_queue_.PostTask(rtc::MakeUnique<ResourceOwningTask<RtcEvent>>(
      std::move(event), event_handler));
}

bool RtcEventLogImpl::AppendEventToString(const RtcEvent& event,
                                          std::string* output_string) {
  RTC_DCHECK_RUN_ON(&task_queue_);

  std::string encoded_event = event_encoder_->Encode(event);

  bool appended;
  size_t potential_new_size =
      written_bytes_ + output_string->size() + encoded_event.length();
  if (potential_new_size <= max_size_bytes_) {
    // TODO(eladalon): This is inefficient; fix this in a separate CL.
    *output_string += encoded_event;
    appended = true;
  } else {
    appended = false;
  }

  return appended;
}

void RtcEventLogImpl::LogToMemory(std::unique_ptr<RtcEvent> event) {
  RTC_DCHECK(!event_output_);

  std::deque<std::unique_ptr<RtcEvent>>& container =
      event->IsConfigEvent() ? config_history_ : history_;
  const size_t container_max_size =
      event->IsConfigEvent() ? kMaxEventsInHistory : kMaxEventsInConfigHistory;

  if (container.size() >= container_max_size) {
    container.pop_front();
  }
  container.push_back(std::move(event));
}

void RtcEventLogImpl::LogEventsFromMemoryToOutput() {
  RTC_DCHECK(event_output_ && event_output_->IsActive());

  std::string output_string;

  // Serialize the config information for all old streams, including streams
  // which were already logged to previous outputs.
  bool appended = true;
  for (auto& event : config_history_) {
    if (!AppendEventToString(*event, &output_string)) {
      appended = false;
      break;
    }
  }

  // Serialize the events in the event queue.
  while (appended && !history_.empty()) {
    appended = AppendEventToString(*history_.front(), &output_string);
    if (appended) {
      // Known issue - if writing to the output fails, these events will have
      // been lost. If we try to open a new output, these events will be missing
      // from it.
      history_.pop_front();
    }
  }

  WriteToOutput(output_string);

  if (!appended) {
    // Successful partial write to the output. Some events could not be written;
    // the output should be closed, to avoid gaps.
    StopOutput();
  }
}

void RtcEventLogImpl::LogToOutput(std::unique_ptr<RtcEvent> event) {
  RTC_DCHECK(event_output_ && event_output_->IsActive());

  std::string output_string;

  bool appended = AppendEventToString(*event, &output_string);

  if (event->IsConfigEvent()) {
    // Config events need to be kept in memory too, so that they may be
    // rewritten into future outputs, too.
    config_history_.push_back(std::move(event));
  }

  if (!appended) {
    if (!event->IsConfigEvent()) {
      // This event will not fit into the output; push it into |history_|
      // instead, so that it might be logged into the next output (if any).
      history_.push_back(std::move(event));
    }
    StopOutput();
    return;
  }

  WriteToOutput(output_string);
}

void RtcEventLogImpl::StopOutput() {
  max_size_bytes_ = std::numeric_limits<decltype(max_size_bytes_)>::max();
  written_bytes_ = 0;
  event_output_.reset();
}

void RtcEventLogImpl::StopLoggingInternal() {
  if (event_output_) {
    RTC_DCHECK(event_output_->IsActive());
    event_output_->Write(
        event_encoder_->Encode(*rtc::MakeUnique<RtcEventLoggingStopped>()));
  }
  StopOutput();
}

void RtcEventLogImpl::WriteToOutput(const std::string& output_string) {
  RTC_DCHECK(event_output_ && event_output_->IsActive());
  if (!event_output_->Write(output_string)) {
    LOG(LS_ERROR) << "Failed to write RTC event to output.";
    // The first failure closes the output.
    RTC_DCHECK(!event_output_->IsActive());
    StopOutput();  // Clean-up.
    return;
  }
  written_bytes_ += output_string.size();
}

}  // namespace

#endif  // ENABLE_RTC_EVENT_LOG

// RtcEventLog member functions.
std::unique_ptr<RtcEventLog> RtcEventLog::Create(EncodingType encoding_type) {
#ifdef ENABLE_RTC_EVENT_LOG
  // TODO(eladalon): Known issue - there's a race over |rtc_event_log_count|.
  constexpr int kMaxLogCount = 5;
  int count = 1 + std::atomic_fetch_add(&rtc_event_log_count, 1);
  if (count > kMaxLogCount) {
    LOG(LS_WARNING) << "Denied creation of additional WebRTC event logs. "
                    << count - 1 << " logs open already.";
    std::atomic_fetch_sub(&rtc_event_log_count, 1);
    return CreateNull();
  }
  auto encoder = CreateEncoder(encoding_type);
  return rtc::MakeUnique<RtcEventLogImpl>(std::move(encoder));
#else
  return CreateNull();
#endif  // ENABLE_RTC_EVENT_LOG
}

std::unique_ptr<RtcEventLog> RtcEventLog::CreateNull() {
  return std::unique_ptr<RtcEventLog>(new RtcEventLogNullImpl());
}

}  // namespace webrtc
<|endoftext|>


<commit_before>//==- UninitializedValues.cpp - Find Unintialized Values --------*- C++ --*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Ted Kremenek and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements Uninitialized Values analysis for source-level CFGs.
//
//===----------------------------------------------------------------------===//

#include "clang/Analysis/UninitializedValues.h"
#include "clang/Analysis/CFGStmtVisitor.h"
#include "clang/Analysis/LocalCheckers.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/ASTContext.h"
#include "DataflowSolver.h"

#include "llvm/ADT/SmallPtrSet.h"

using namespace clang;

//===----------------------------------------------------------------------===//
// Dataflow initialization logic.
//===----------------------------------------------------------------------===//

namespace {

class RegisterDeclsAndExprs : public CFGStmtVisitor<RegisterDeclsAndExprs> {
  UninitializedValues::AnalysisDataTy& AD;
public:
  RegisterDeclsAndExprs(UninitializedValues::AnalysisDataTy& ad) :  AD(ad) {}

  void VisitBlockVarDecl(BlockVarDecl* VD) {
    if (AD.VMap.find(VD) == AD.VMap.end())
      AD.VMap[VD] = AD.NumDecls++;
  }

  void VisitDeclChain(ScopedDecl* D) {
    for (; D != NULL; D = D->getNextDeclarator())
      if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(D))
        VisitBlockVarDecl(VD);
  }

  void BlockStmt_VisitExpr(Expr* E) {
    if (AD.EMap.find(E) == AD.EMap.end())
      AD.EMap[E] = AD.NumBlockExprs++;

    Visit(E);
  }

  void VisitDeclRefExpr(DeclRefExpr* DR) { VisitDeclChain(DR->getDecl()); }
  void VisitDeclStmt(DeclStmt* S) { VisitDeclChain(S->getDecl()); }
  void VisitStmt(Stmt* S) { VisitChildren(S); }
  void operator()(Stmt* S) { BlockStmt_Visit(S); }
};

} // end anonymous namespace

void UninitializedValues::InitializeValues(const CFG& cfg) {
  RegisterDeclsAndExprs R(this->getAnalysisData());
  cfg.VisitBlockStmts(R);
}

//===----------------------------------------------------------------------===//
// Transfer functions.
//===----------------------------------------------------------------------===//

namespace {

class TransferFuncs : public CFGStmtVisitor<TransferFuncs,bool> {
  UninitializedValues::ValTy V;
  UninitializedValues::AnalysisDataTy& AD;
  bool InitWithAssigns;
public:
  TransferFuncs(UninitializedValues::AnalysisDataTy& ad,
                bool init_with_assigns=true) :
    AD(ad), InitWithAssigns(init_with_assigns) {
    V.resetValues(AD);
  }

  UninitializedValues::ValTy& getVal() { return V; }

  bool VisitDeclRefExpr(DeclRefExpr* DR);
  bool VisitBinaryOperator(BinaryOperator* B);
  bool VisitUnaryOperator(UnaryOperator* U);
  bool VisitStmt(Stmt* S);
  bool VisitCallExpr(CallExpr* C);
  bool BlockStmt_VisitExpr(Expr* E);
  bool VisitDeclStmt(DeclStmt* D);

  BlockVarDecl* FindBlockVarDecl(Stmt* S);

  static inline bool Initialized() { return true; }
  static inline bool Uninitialized() { return false; }
};

bool TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) {
  if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(DR->getDecl())) {
    if (AD.Observer) AD.Observer->ObserveDeclRefExpr(V,AD,DR,VD);

    return V.getBitRef(VD,AD);
  }
  else return Initialized();
}

BlockVarDecl* TransferFuncs::FindBlockVarDecl(Stmt *S) {
  for (;;) {
    if (ParenExpr* P = dyn_cast<ParenExpr>(S)) {
      S = P->getSubExpr();
      continue;
    }
    else if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(S))
      if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(DR->getDecl()))
        return VD;

    return NULL;
  }
}

bool TransferFuncs::VisitBinaryOperator(BinaryOperator* B) {

  if (CFG::hasImplicitControlFlow(B))
    return V.getBitRef(B,AD);

  if (B->isAssignmentOp())
    // Get the Decl for the LHS (if any).
    if (BlockVarDecl* VD = FindBlockVarDecl(B->getLHS()))
      if(InitWithAssigns) {
        // Pseudo-hack to prevent cascade of warnings.  If the RHS uses
        // an uninitialized value, then we are already going to flag a warning
        // for the RHS, or for the root "source" of the unintialized values.
        // Thus, propogating uninitialized doesn't make sense, since we are
        // just adding extra messages that don't
        // contribute to diagnosing the bug.  In InitWithAssigns mode
        // we unconditionally set the assigned variable to Initialized to
        // prevent Uninitialized propogation.
        return V.getBitRef(VD,AD) = Initialized();
      }
      else return V.getBitRef(VD,AD) = Visit(B->getRHS());

  return VisitStmt(B);
}

bool TransferFuncs::VisitDeclStmt(DeclStmt* S) {
  bool x = Initialized();

  for (ScopedDecl* D = S->getDecl(); D != NULL; D = D->getNextDeclarator())
    if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(D))
      if (Stmt* I = VD->getInit()) {
        x = V.getBitRef(cast<Expr>(I),AD);
        V.getBitRef(VD,AD) = x;
      }

  return x;
}

bool TransferFuncs::VisitCallExpr(CallExpr* C) {
  VisitStmt(C);
  return Initialized();
}

bool TransferFuncs::VisitUnaryOperator(UnaryOperator* U) {
  switch (U->getOpcode()) {
    case UnaryOperator::AddrOf:
      // For "&x", treat "x" as now being initialized.
      if (BlockVarDecl* VD = FindBlockVarDecl(U->getSubExpr()))
        V.getBitRef(VD,AD) = Initialized();
      else
        return Visit(U->getSubExpr());

    default:
      return Visit(U->getSubExpr());
  }
}

bool TransferFuncs::VisitStmt(Stmt* S) {
  bool x = Initialized();

  // We don't stop at the first subexpression that is Uninitialized because
  // evaluating some subexpressions may result in propogating "Uninitialized"
  // or "Initialized" to variables referenced in the other subexpressions.
  for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I)
    if (Visit(*I) == Uninitialized()) x = Uninitialized();

  return x;
}

bool TransferFuncs::BlockStmt_VisitExpr(Expr* E) {
  assert (AD.isTracked(E));
  return V.getBitRef(E,AD) = Visit(E);
}

} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Merge operator.
//
//  In our transfer functions we take the approach that any
//  combination of unintialized values, e.g. Unitialized + ___ = Unitialized.
//
//  Merges take the opposite approach.
//
//  In the merge of dataflow values we prefer unsoundness, and
//  prefer false negatives to false positives.  At merges, if a value for a
//  tracked Decl is EVER initialized in any of the predecessors we treat it as
//  initialized at the confluence point.
//===----------------------------------------------------------------------===//

namespace {
struct Merge {
  void operator()(UninitializedValues::ValTy& Dst,
                  UninitializedValues::ValTy& Src) {
    assert (Src.sizesEqual(Dst) && "BV sizes do not match.");
    Dst.DeclBV |= Src.DeclBV;
    Dst.ExprBV |= Src.ExprBV;
  }
};
} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Unitialized values checker.   Scan an AST and flag variable uses
//===----------------------------------------------------------------------===//

UninitializedValues_ValueTypes::ObserverTy::~ObserverTy() {}

namespace {
class UninitializedValuesChecker : public UninitializedValues::ObserverTy {
  ASTContext &Ctx;
  Diagnostic &Diags;
  llvm::SmallPtrSet<BlockVarDecl*,10> AlreadyWarned;

public:
  UninitializedValuesChecker(ASTContext &ctx, Diagnostic &diags)
    : Ctx(ctx), Diags(diags) {}

  virtual void ObserveDeclRefExpr(UninitializedValues::ValTy& V,
                                  UninitializedValues::AnalysisDataTy& AD,
                                  DeclRefExpr* DR, BlockVarDecl* VD) {

    assert ( AD.isTracked(VD) && "Unknown VarDecl.");

    if (V.getBitRef(VD,AD) == TransferFuncs::Uninitialized())
      if (AlreadyWarned.insert(VD))
        Diags.Report(DR->getSourceRange().Begin(), diag::warn_uninit_val);
  }
};
} // end anonymous namespace

namespace clang {
void CheckUninitializedValues(CFG& cfg, ASTContext &Ctx, Diagnostic &Diags) {

  typedef DataflowSolver<UninitializedValues,TransferFuncs,Merge> Solver;

  // Compute the unitialized values information.
  UninitializedValues U;
  Solver S(U);
  S.runOnCFG(cfg);

  // Scan for DeclRefExprs that use uninitialized values.
  UninitializedValuesChecker Observer(Ctx,Diags);
  U.getAnalysisData().Observer = &Observer;
  S.runOnAllBlocks(cfg);
}
} // end namespace clang
<commit_msg>Minor performance fix in transfer function logic. Previously we called VisitStmt, but VisitChildren is more direct and creates less boilerplate logic.<commit_after>//==- UninitializedValues.cpp - Find Unintialized Values --------*- C++ --*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Ted Kremenek and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements Uninitialized Values analysis for source-level CFGs.
//
//===----------------------------------------------------------------------===//

#include "clang/Analysis/UninitializedValues.h"
#include "clang/Analysis/CFGStmtVisitor.h"
#include "clang/Analysis/LocalCheckers.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/ASTContext.h"
#include "DataflowSolver.h"

#include "llvm/ADT/SmallPtrSet.h"

using namespace clang;

//===----------------------------------------------------------------------===//
// Dataflow initialization logic.
//===----------------------------------------------------------------------===//

namespace {

class RegisterDeclsAndExprs : public CFGStmtVisitor<RegisterDeclsAndExprs> {
  UninitializedValues::AnalysisDataTy& AD;
public:
  RegisterDeclsAndExprs(UninitializedValues::AnalysisDataTy& ad) :  AD(ad) {}

  void VisitBlockVarDecl(BlockVarDecl* VD) {
    if (AD.VMap.find(VD) == AD.VMap.end())
      AD.VMap[VD] = AD.NumDecls++;
  }

  void VisitDeclChain(ScopedDecl* D) {
    for (; D != NULL; D = D->getNextDeclarator())
      if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(D))
        VisitBlockVarDecl(VD);
  }

  void BlockStmt_VisitExpr(Expr* E) {
    if (AD.EMap.find(E) == AD.EMap.end())
      AD.EMap[E] = AD.NumBlockExprs++;

    Visit(E);
  }

  void VisitDeclRefExpr(DeclRefExpr* DR) { VisitDeclChain(DR->getDecl()); }
  void VisitDeclStmt(DeclStmt* S) { VisitDeclChain(S->getDecl()); }
  void VisitStmt(Stmt* S) { VisitChildren(S); }
  void operator()(Stmt* S) { BlockStmt_Visit(S); }
};

} // end anonymous namespace

void UninitializedValues::InitializeValues(const CFG& cfg) {
  RegisterDeclsAndExprs R(this->getAnalysisData());
  cfg.VisitBlockStmts(R);
}

//===----------------------------------------------------------------------===//
// Transfer functions.
//===----------------------------------------------------------------------===//

namespace {

class TransferFuncs : public CFGStmtVisitor<TransferFuncs,bool> {
  UninitializedValues::ValTy V;
  UninitializedValues::AnalysisDataTy& AD;
  bool InitWithAssigns;
public:
  TransferFuncs(UninitializedValues::AnalysisDataTy& ad,
                bool init_with_assigns=true) :
    AD(ad), InitWithAssigns(init_with_assigns) {
    V.resetValues(AD);
  }

  UninitializedValues::ValTy& getVal() { return V; }

  bool VisitDeclRefExpr(DeclRefExpr* DR);
  bool VisitBinaryOperator(BinaryOperator* B);
  bool VisitUnaryOperator(UnaryOperator* U);
  bool VisitStmt(Stmt* S);
  bool VisitCallExpr(CallExpr* C);
  bool BlockStmt_VisitExpr(Expr* E);
  bool VisitDeclStmt(DeclStmt* D);

  BlockVarDecl* FindBlockVarDecl(Stmt* S);

  static inline bool Initialized() { return true; }
  static inline bool Uninitialized() { return false; }
};

bool TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) {
  if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(DR->getDecl())) {
    if (AD.Observer) AD.Observer->ObserveDeclRefExpr(V,AD,DR,VD);

    return V.getBitRef(VD,AD);
  }
  else return Initialized();
}

BlockVarDecl* TransferFuncs::FindBlockVarDecl(Stmt *S) {
  for (;;) {
    if (ParenExpr* P = dyn_cast<ParenExpr>(S)) {
      S = P->getSubExpr();
      continue;
    }
    else if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(S))
      if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(DR->getDecl()))
        return VD;

    return NULL;
  }
}

bool TransferFuncs::VisitBinaryOperator(BinaryOperator* B) {

  if (CFG::hasImplicitControlFlow(B))
    return V.getBitRef(B,AD);

  if (B->isAssignmentOp())
    // Get the Decl for the LHS (if any).
    if (BlockVarDecl* VD = FindBlockVarDecl(B->getLHS()))
      if(InitWithAssigns) {
        // Pseudo-hack to prevent cascade of warnings.  If the RHS uses
        // an uninitialized value, then we are already going to flag a warning
        // for the RHS, or for the root "source" of the unintialized values.
        // Thus, propogating uninitialized doesn't make sense, since we are
        // just adding extra messages that don't
        // contribute to diagnosing the bug.  In InitWithAssigns mode
        // we unconditionally set the assigned variable to Initialized to
        // prevent Uninitialized propogation.
        return V.getBitRef(VD,AD) = Initialized();
      }
      else return V.getBitRef(VD,AD) = Visit(B->getRHS());

  return VisitStmt(B);
}

bool TransferFuncs::VisitDeclStmt(DeclStmt* S) {
  bool x = Initialized();

  for (ScopedDecl* D = S->getDecl(); D != NULL; D = D->getNextDeclarator())
    if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(D))
      if (Stmt* I = VD->getInit()) {
        x = V.getBitRef(cast<Expr>(I),AD);
        V.getBitRef(VD,AD) = x;
      }

  return x;
}

bool TransferFuncs::VisitCallExpr(CallExpr* C) {
  VisitChildren(C);
  return Initialized();
}

bool TransferFuncs::VisitUnaryOperator(UnaryOperator* U) {
  switch (U->getOpcode()) {
    case UnaryOperator::AddrOf:
      // For "&x", treat "x" as now being initialized.
      if (BlockVarDecl* VD = FindBlockVarDecl(U->getSubExpr()))
        V.getBitRef(VD,AD) = Initialized();
      else
        return Visit(U->getSubExpr());

    default:
      return Visit(U->getSubExpr());
  }
}

bool TransferFuncs::VisitStmt(Stmt* S) {
  bool x = Initialized();

  // We don't stop at the first subexpression that is Uninitialized because
  // evaluating some subexpressions may result in propogating "Uninitialized"
  // or "Initialized" to variables referenced in the other subexpressions.
  for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I)
    if (Visit(*I) == Uninitialized()) x = Uninitialized();

  return x;
}

bool TransferFuncs::BlockStmt_VisitExpr(Expr* E) {
  assert (AD.isTracked(E));
  return V.getBitRef(E,AD) = Visit(E);
}

} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Merge operator.
//
//  In our transfer functions we take the approach that any
//  combination of unintialized values, e.g. Unitialized + ___ = Unitialized.
//
//  Merges take the opposite approach.
//
//  In the merge of dataflow values we prefer unsoundness, and
//  prefer false negatives to false positives.  At merges, if a value for a
//  tracked Decl is EVER initialized in any of the predecessors we treat it as
//  initialized at the confluence point.
//===----------------------------------------------------------------------===//

namespace {
struct Merge {
  void operator()(UninitializedValues::ValTy& Dst,
                  UninitializedValues::ValTy& Src) {
    assert (Src.sizesEqual(Dst) && "BV sizes do not match.");
    Dst.DeclBV |= Src.DeclBV;
    Dst.ExprBV |= Src.ExprBV;
  }
};
} // end anonymous namespace

//===----------------------------------------------------------------------===//
// Unitialized values checker.   Scan an AST and flag variable uses
//===----------------------------------------------------------------------===//

UninitializedValues_ValueTypes::ObserverTy::~ObserverTy() {}

namespace {
class UninitializedValuesChecker : public UninitializedValues::ObserverTy {
  ASTContext &Ctx;
  Diagnostic &Diags;
  llvm::SmallPtrSet<BlockVarDecl*,10> AlreadyWarned;

public:
  UninitializedValuesChecker(ASTContext &ctx, Diagnostic &diags)
    : Ctx(ctx), Diags(diags) {}

  virtual void ObserveDeclRefExpr(UninitializedValues::ValTy& V,
                                  UninitializedValues::AnalysisDataTy& AD,
                                  DeclRefExpr* DR, BlockVarDecl* VD) {

    assert ( AD.isTracked(VD) && "Unknown VarDecl.");

    if (V.getBitRef(VD,AD) == TransferFuncs::Uninitialized())
      if (AlreadyWarned.insert(VD))
        Diags.Report(DR->getSourceRange().Begin(), diag::warn_uninit_val);
  }
};
} // end anonymous namespace

namespace clang {
void CheckUninitializedValues(CFG& cfg, ASTContext &Ctx, Diagnostic &Diags) {

  typedef DataflowSolver<UninitializedValues,TransferFuncs,Merge> Solver;

  // Compute the unitialized values information.
  UninitializedValues U;
  Solver S(U);
  S.runOnCFG(cfg);

  // Scan for DeclRefExprs that use uninitialized values.
  UninitializedValuesChecker Observer(Ctx,Diags);
  U.getAnalysisData().Observer = &Observer;
  S.runOnAllBlocks(cfg);
}
} // end namespace clang
<|endoftext|>


<commit_before>#include <iostream>
#include "BenchTimer.h"
#include <Eigen/Dense>
#include <map>
#include <string>
using namespace Eigen;

std::map<std::string,Array<float,1,4> > results;

template<typename Scalar,int Size>
void bench(int id, int size = Size)
{
  typedef Matrix<Scalar,Size,Size> Mat;
  Mat A(size,size);
  A.setRandom();
  A = A*A.adjoint();
  BenchTimer t_llt, t_ldlt, t_lu, t_fplu, t_qr, t_cpqr, t_cod, t_fpqr, t_jsvd, t_bdcsvd;

  int tries = 3;
  int rep = 1000/size;
  if(rep==0) rep = 1;
//   rep = rep*rep;

  LLT<Mat> llt(A);
  LDLT<Mat> ldlt(A);
  PartialPivLU<Mat> lu(A);
  FullPivLU<Mat> fplu(A);
  HouseholderQR<Mat> qr(A);
  ColPivHouseholderQR<Mat> cpqr(A);
  CompleteOrthogonalDecomposition<Mat> cod(A);
  FullPivHouseholderQR<Mat> fpqr(A);
  JacobiSVD<Mat> jsvd(A.rows(),A.cols());
  BDCSVD<Mat> bdcsvd(A.rows(),A.cols());

  BENCH(t_llt, tries, rep, llt.compute(A));
  BENCH(t_ldlt, tries, rep, ldlt.compute(A));
  BENCH(t_lu, tries, rep, lu.compute(A));
  BENCH(t_fplu, tries, rep, fplu.compute(A));
  BENCH(t_qr, tries, rep, qr.compute(A));
  BENCH(t_cpqr, tries, rep, cpqr.compute(A));
  BENCH(t_cod, tries, rep, cod.compute(A));
  BENCH(t_fpqr, tries, rep, fpqr.compute(A));
  if(size<500) // JacobiSVD is really too slow for too large matrices
    BENCH(t_jsvd, tries, rep, jsvd.compute(A,ComputeFullU|ComputeFullV));
  BENCH(t_bdcsvd, tries, rep, bdcsvd.compute(A,ComputeFullU|ComputeFullV));

  results["LLT"][id] = t_llt.best();
  results["LDLT"][id] = t_ldlt.best();
  results["PartialPivLU"][id] = t_lu.best();
  results["FullPivLU"][id] = t_fplu.best();
  results["HouseholderQR"][id] = t_qr.best();
  results["ColPivHouseholderQR"][id] = t_cpqr.best();
  results["CompleteOrthogonalDecomposition"][id] = t_cod.best();
  results["FullPivHouseholderQR"][id] = t_fpqr.best();
  results["JacobiSVD"][id] = size<500 ? t_jsvd.best() : 0;
  results["BDCSVD"][id] = t_bdcsvd.best();
}

int main()
{
  const int small = 8;
  const int medium = 100;
  const int large = 1000;
  const int xl = 4000;

  bench<float,small>(0);
  bench<float,Dynamic>(1,medium);
  bench<float,Dynamic>(2,large);
  bench<float,Dynamic>(3,xl);

  IOFormat fmt(3, 0, " \t", "\n", "", "");

  std::cout << "solver/size                           " << small << "\t" << medium << "\t" << large << "\t" << xl << "\n";
  std::cout << "LLT                             (ms)  " << (results["LLT"]/1000.).format(fmt) << "\n";
  std::cout << "LDLT                             (%)  " << (results["LDLT"]/results["LLT"]).format(fmt) << "\n";
  std::cout << "PartialPivLU                     (%)  " << (results["PartialPivLU"]/results["LLT"]).format(fmt) << "\n";
  std::cout << "FullPivLU                        (%)  " << (results["FullPivLU"]/results["LLT"]).format(fmt) << "\n";
  std::cout << "HouseholderQR                    (%)  " << (results["HouseholderQR"]/results["LLT"]).format(fmt) << "\n";
  std::cout << "ColPivHouseholderQR              (%)  " << (results["ColPivHouseholderQR"]/results["LLT"]).format(fmt) << "\n";
  std::cout << "CompleteOrthogonalDecomposition  (%)  " << (results["CompleteOrthogonalDecomposition"]/results["LLT"]).format(fmt) << "\n";
  std::cout << "FullPivHouseholderQR             (%)  " << (results["FullPivHouseholderQR"]/results["LLT"]).format(fmt) << "\n";
  std::cout << "JacobiSVD                        (%)  " << (results["JacobiSVD"]/results["LLT"]).format(fmt) << "\n";
  std::cout << "BDCSVD                           (%)  " << (results["BDCSVD"]/results["LLT"]).format(fmt) << "\n";
}
<commit_msg>Update benchmark for dense solver to stress least-squares pb, and to output a HTML table<commit_after>#include <iostream>
#include "BenchTimer.h"
#include <Eigen/Dense>
#include <map>
#include <vector>
#include <string>
#include <sstream>
using namespace Eigen;

std::map<std::string,Array<float,1,8,DontAlign|RowMajor> > results;
std::vector<std::string> labels;
std::vector<Array2i> sizes;

template<typename Solver,typename MatrixType>
EIGEN_DONT_INLINE
void compute_norm_equation(Solver &solver, const MatrixType &A) {
  if(A.rows()!=A.cols())
    solver.compute(A.transpose()*A);
  else
    solver.compute(A);
}

template<typename Solver,typename MatrixType>
EIGEN_DONT_INLINE
void compute(Solver &solver, const MatrixType &A) {
  solver.compute(A);
}

template<typename Scalar,int Size>
void bench(int id, int rows, int size = Size)
{
  typedef Matrix<Scalar,Dynamic,Size> Mat;
  typedef Matrix<Scalar,Dynamic,Dynamic> MatDyn;
  typedef Matrix<Scalar,Size,Size> MatSquare;
  Mat A(rows,size);
  A.setRandom();
  if(rows==size)
    A = A*A.adjoint();
  BenchTimer t_llt, t_ldlt, t_lu, t_fplu, t_qr, t_cpqr, t_cod, t_fpqr, t_jsvd, t_bdcsvd;

  int svd_opt = ComputeThinU|ComputeThinV;

  int tries = 5;
  int rep = 1000/size;
  if(rep==0) rep = 1;
//   rep = rep*rep;

  LLT<MatSquare> llt(size);
  LDLT<MatSquare> ldlt(size);
  PartialPivLU<MatSquare> lu(size);
  FullPivLU<MatSquare> fplu(size,size);
  HouseholderQR<Mat> qr(A.rows(),A.cols());
  ColPivHouseholderQR<Mat> cpqr(A.rows(),A.cols());
  CompleteOrthogonalDecomposition<Mat> cod(A.rows(),A.cols());
  FullPivHouseholderQR<Mat> fpqr(A.rows(),A.cols());
  JacobiSVD<MatDyn> jsvd(A.rows(),A.cols());
  BDCSVD<MatDyn> bdcsvd(A.rows(),A.cols());

  BENCH(t_llt, tries, rep, compute_norm_equation(llt,A));
  BENCH(t_ldlt, tries, rep, compute_norm_equation(ldlt,A));
  BENCH(t_lu, tries, rep, compute_norm_equation(lu,A));
  if(size<=1000)
    BENCH(t_fplu, tries, rep, compute_norm_equation(fplu,A));
  BENCH(t_qr, tries, rep, compute(qr,A));
  BENCH(t_cpqr, tries, rep, compute(cpqr,A));
  BENCH(t_cod, tries, rep, compute(cod,A));
  if(size*rows<=10000000)
    BENCH(t_fpqr, tries, rep, compute(fpqr,A));
  if(size<500) // JacobiSVD is really too slow for too large matrices
    BENCH(t_jsvd, tries, rep, jsvd.compute(A,svd_opt));
//   if(size*rows<=20000000)
    BENCH(t_bdcsvd, tries, rep, bdcsvd.compute(A,svd_opt));

  results["LLT"][id] = t_llt.best();
  results["LDLT"][id] = t_ldlt.best();
  results["PartialPivLU"][id] = t_lu.best();
  results["FullPivLU"][id] = t_fplu.best();
  results["HouseholderQR"][id] = t_qr.best();
  results["ColPivHouseholderQR"][id] = t_cpqr.best();
  results["CompleteOrthogonalDecomposition"][id] = t_cod.best();
  results["FullPivHouseholderQR"][id] = t_fpqr.best();
  results["JacobiSVD"][id] = t_jsvd.best();
  results["BDCSVD"][id] = t_bdcsvd.best();
}

int main()
{
  labels.push_back("LLT");
  labels.push_back("LDLT");
  labels.push_back("PartialPivLU");
  labels.push_back("FullPivLU");
  labels.push_back("HouseholderQR");
  labels.push_back("ColPivHouseholderQR");
  labels.push_back("CompleteOrthogonalDecomposition");
  labels.push_back("FullPivHouseholderQR");
  labels.push_back("JacobiSVD");
  labels.push_back("BDCSVD");

  for(int i=0; i<labels.size(); ++i)
    results[labels[i]].fill(-1);

  const int small = 8;
  sizes.push_back(Array2i(small,small));
  sizes.push_back(Array2i(100,100));
  sizes.push_back(Array2i(1000,1000));
  sizes.push_back(Array2i(4000,4000));
  sizes.push_back(Array2i(10000,small));
  sizes.push_back(Array2i(10000,100));
  sizes.push_back(Array2i(10000,1000));
  sizes.push_back(Array2i(10000,4000));

  using namespace std;

  for(int k=0; k<sizes.size(); ++k)
  {
    cout << sizes[k](0) << "x" << sizes[k](1) << "...\n";
    bench<float,Dynamic>(k,sizes[k](0),sizes[k](1));
  }

  cout.width(32);
  cout << "solver/size";
  cout << "  ";
  for(int k=0; k<sizes.size(); ++k)
  {
    std::stringstream ss;
    ss << sizes[k](0) << "x" << sizes[k](1);
    cout.width(10); cout << ss.str(); cout << " ";
  }
  cout << endl;

  for(int i=0; i<labels.size(); ++i)
  {
    cout.width(32); cout << labels[i]; cout << "  ";
    ArrayXf r = (results[labels[i]]*100000.f).floor()/100.f;
    for(int k=0; k<sizes.size(); ++k)
    {
      cout.width(10);
      if(r(k)>=1e6)  cout << "-";
      else           cout << r(k);
      cout << " ";
    }
    cout << endl;
  }

  // HTML output
  cout << "
" << endl;
  cout << "
solver/size	" << endl;
  for(int k=0; k<sizes.size(); ++k)
    cout << "  " << sizes[k](0) << "x" << sizes[k](1) << "	";
  cout << "
" << endl;
  for(int i=0; i<labels.size(); ++i)
  {
    cout << "<tr";
    if(i%2==1) cout << " class=\"alt\"";
    cout << ">" << labels[i] << "	";
    ArrayXf r = (results[labels[i]]*100000.f).floor()/100.f;
    for(int k=0; k<sizes.size(); ++k)
    {
      if(r(k)>=1e6) cout << "-	";
      else
      {
        cout << "" << r(k);
        if(i>0)
          cout << " (x" << numext::round(10.f*results[labels[i]](k)/results["LLT"](k))/10.f << ")";
        if(i<4 && sizes[k](0)!=sizes[k](1))
          cout << " *";
        cout << "	";
      }
    }
    cout << "
" << endl;
  }
  cout << "
" << endl;

//   cout << "LLT                             (ms)  " << (results["LLT"]*1000.).format(fmt) << "\n";
//   cout << "LDLT                             (%)  " << (results["LDLT"]/results["LLT"]).format(fmt) << "\n";
//   cout << "PartialPivLU                     (%)  " << (results["PartialPivLU"]/results["LLT"]).format(fmt) << "\n";
//   cout << "FullPivLU                        (%)  " << (results["FullPivLU"]/results["LLT"]).format(fmt) << "\n";
//   cout << "HouseholderQR                    (%)  " << (results["HouseholderQR"]/results["LLT"]).format(fmt) << "\n";
//   cout << "ColPivHouseholderQR              (%)  " << (results["ColPivHouseholderQR"]/results["LLT"]).format(fmt) << "\n";
//   cout << "CompleteOrthogonalDecomposition  (%)  " << (results["CompleteOrthogonalDecomposition"]/results["LLT"]).format(fmt) << "\n";
//   cout << "FullPivHouseholderQR             (%)  " << (results["FullPivHouseholderQR"]/results["LLT"]).format(fmt) << "\n";
//   cout << "JacobiSVD                        (%)  " << (results["JacobiSVD"]/results["LLT"]).format(fmt) << "\n";
//   cout << "BDCSVD                           (%)  " << (results["BDCSVD"]/results["LLT"]).format(fmt) << "\n";
}
<|endoftext|>


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

#include "media/audio/audio_output_controller.h"

#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/time.h"

using base::Time;
using base::WaitableEvent;

namespace media {

// Signal a pause in low-latency mode.
const int AudioOutputController::kPauseMark = -1;

// Polling-related constants.
const int AudioOutputController::kPollNumAttempts = 3;
const int AudioOutputController::kPollPauseInMilliseconds = 3;

AudioOutputController::AudioOutputController(EventHandler* handler,
                                             SyncReader* sync_reader)
    : handler_(handler),
      stream_(NULL),
      volume_(1.0),
      state_(kEmpty),
      sync_reader_(sync_reader),
      message_loop_(NULL),
      number_polling_attempts_left_(0),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) {
}

AudioOutputController::~AudioOutputController() {
  DCHECK_EQ(kClosed, state_);
  DCHECK(message_loop_);

  if (!message_loop_.get() || message_loop_->BelongsToCurrentThread()) {
    DoStopCloseAndClearStream(NULL);
  } else {
    // http://crbug.com/120973
    base::ThreadRestrictions::ScopedAllowWait allow_wait;
    WaitableEvent completion(true /* manual reset */,
                             false /* initial state */);
    message_loop_->PostTask(FROM_HERE,
        base::Bind(&AudioOutputController::DoStopCloseAndClearStream,
                   base::Unretained(this),
                   &completion));
    completion.Wait();
  }
}

// static
scoped_refptr<AudioOutputController> AudioOutputController::Create(
    AudioManager* audio_manager,
    EventHandler* event_handler,
    const AudioParameters& params,
    SyncReader* sync_reader) {
  DCHECK(audio_manager);
  DCHECK(sync_reader);

  if (!params.IsValid() || !audio_manager)
    return NULL;

  // Starts the audio controller thread.
  scoped_refptr<AudioOutputController> controller(new AudioOutputController(
      event_handler, sync_reader));

  controller->message_loop_ = audio_manager->GetMessageLoop();
  controller->message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoCreate, controller,
      base::Unretained(audio_manager), params));
  return controller;
}

void AudioOutputController::Play() {
  DCHECK(message_loop_);
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoPlay, base::Unretained(this)));
}

void AudioOutputController::Pause() {
  DCHECK(message_loop_);
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoPause, base::Unretained(this)));
}

void AudioOutputController::Flush() {
  DCHECK(message_loop_);
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoFlush, base::Unretained(this)));
}

void AudioOutputController::Close(const base::Closure& closed_task) {
  DCHECK(!closed_task.is_null());
  DCHECK(message_loop_);
  message_loop_->PostTaskAndReply(FROM_HERE, base::Bind(
      &AudioOutputController::DoClose, base::Unretained(this)), closed_task);
}

void AudioOutputController::SetVolume(double volume) {
  DCHECK(message_loop_);
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoSetVolume, base::Unretained(this), volume));
}

void AudioOutputController::DoCreate(AudioManager* audio_manager,
                                     const AudioParameters& params) {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // Close() can be called before DoCreate() is executed.
  if (state_ == kClosed)
    return;
  DCHECK_EQ(kEmpty, state_);

  DoStopCloseAndClearStream(NULL);
  stream_ = audio_manager->MakeAudioOutputStreamProxy(params);
  if (!stream_) {
    // TODO(hclam): Define error types.
    handler_->OnError(this, 0);
    return;
  }

  if (!stream_->Open()) {
    DoStopCloseAndClearStream(NULL);

    // TODO(hclam): Define error types.
    handler_->OnError(this, 0);
    return;
  }

  // We have successfully opened the stream. Set the initial volume.
  stream_->SetVolume(volume_);

  // Finally set the state to kCreated.
  state_ = kCreated;

  // And then report we have been created.
  handler_->OnCreated(this);
}

void AudioOutputController::DoPlay() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // We can start from created or paused state.
  if (state_ != kCreated && state_ != kPaused)
    return;

  state_ = kStarting;

  // Ask for first packet.
  sync_reader_->UpdatePendingBytes(0);

  // Cannot start stream immediately, should give renderer some time
  // to deliver data.
  // TODO(vrk): The polling here and in WaitTillDataReady() is pretty clunky.
  // Refine the API such that polling is no longer needed. (crbug.com/112196)
  number_polling_attempts_left_ = kPollNumAttempts;
  message_loop_->PostDelayedTask(
      FROM_HERE,
      base::Bind(&AudioOutputController::PollAndStartIfDataReady,
      weak_this_.GetWeakPtr()),
      base::TimeDelta::FromMilliseconds(kPollPauseInMilliseconds));
}

void AudioOutputController::PollAndStartIfDataReady() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // Being paranoid: do nothing if state unexpectedly changed.
  if ((state_ != kStarting) && (state_ != kPausedWhenStarting))
    return;

  bool pausing = (state_ == kPausedWhenStarting);
  // If we are ready to start the stream, start it.
  // Of course we may have to stop it immediately...
  if (--number_polling_attempts_left_ == 0 ||
      pausing ||
      sync_reader_->DataReady()) {
    StartStream();
    if (pausing) {
      DoPause();
    }
  } else {
    message_loop_->PostDelayedTask(
        FROM_HERE,
        base::Bind(&AudioOutputController::PollAndStartIfDataReady,
        weak_this_.GetWeakPtr()),
        base::TimeDelta::FromMilliseconds(kPollPauseInMilliseconds));
  }
}

void AudioOutputController::StartStream() {
  DCHECK(message_loop_->BelongsToCurrentThread());
  state_ = kPlaying;

  // We start the AudioOutputStream lazily.
  stream_->Start(this);

  // Tell the event handler that we are now playing.
  handler_->OnPlaying(this);
}

void AudioOutputController::DoPause() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  if (stream_)
    stream_->Stop();

  switch (state_) {
    case kStarting:
      // We were asked to pause while starting. There is delayed task that will
      // try starting playback, and there is no way to remove that task from the
      // queue. If we stop now that task will be executed anyway.
      // Delay pausing, let delayed task to do pause after it start playback.
      state_ = kPausedWhenStarting;
      break;
    case kPlaying:
      state_ = kPaused;

      // Then we stop the audio device. This is not the perfect solution
      // because it discards all the internal buffer in the audio device.
      // TODO(hclam): Actually pause the audio device.
      stream_->Stop();

      // Send a special pause mark to the low-latency audio thread.
      sync_reader_->UpdatePendingBytes(kPauseMark);

      handler_->OnPaused(this);
      break;
    default:
      return;
  }
}

void AudioOutputController::DoFlush() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // TODO(hclam): Actually flush the audio device.
}

void AudioOutputController::DoClose() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  if (state_ != kClosed) {
    DoStopCloseAndClearStream(NULL);
    sync_reader_->Close();
    state_ = kClosed;
  }
}

void AudioOutputController::DoSetVolume(double volume) {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // Saves the volume to a member first. We may not be able to set the volume
  // right away but when the stream is created we'll set the volume.
  volume_ = volume;

  switch (state_) {
    case kCreated:
    case kStarting:
    case kPausedWhenStarting:
    case kPlaying:
    case kPaused:
      stream_->SetVolume(volume_);
      break;
    default:
      return;
  }
}

void AudioOutputController::DoReportError(int code) {
  DCHECK(message_loop_->BelongsToCurrentThread());
  if (state_ != kClosed)
    handler_->OnError(this, code);
}

uint32 AudioOutputController::OnMoreData(uint8* dest,
                                         uint32 max_size,
                                         AudioBuffersState buffers_state) {
  TRACE_EVENT0("audio", "AudioOutputController::OnMoreData");

  {
    // Check state and do nothing if we are not playing.
    // We are on the hardware audio thread, so lock is needed.
    base::AutoLock auto_lock(lock_);
    if (state_ != kPlaying) {
      return 0;
    }
  }
  uint32 size = sync_reader_->Read(dest, max_size);
  sync_reader_->UpdatePendingBytes(buffers_state.total_bytes() + size);
  return size;
}

void AudioOutputController::WaitTillDataReady() {
  if (!sync_reader_->DataReady()) {
    // In the different place we use different mechanism to poll, get max
    // polling delay from constants used there.
    const base::TimeDelta kMaxPollingDelay = base::TimeDelta::FromMilliseconds(
        kPollNumAttempts * kPollPauseInMilliseconds);
    Time start_time = Time::Now();
    do {
      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
    } while (!sync_reader_->DataReady() &&
             Time::Now() - start_time < kMaxPollingDelay);
  }
}

void AudioOutputController::OnError(AudioOutputStream* stream, int code) {
  // Handle error on the audio controller thread.
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoReportError, base::Unretained(this), code));
}

void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent *done) {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // Allow calling unconditionally and bail if we don't have a stream_ to close.
  if (stream_ != NULL) {
    stream_->Stop();
    stream_->Close();
    stream_ = NULL;
    weak_this_.InvalidateWeakPtrs();
  }

  // Should be last in the method, do not touch "this" from here on.
  if (done != NULL)
    done->Signal();
}

}  // namespace media
<commit_msg>Fix race condition caused by r114084.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/audio_output_controller.h"

#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/time.h"

using base::Time;
using base::WaitableEvent;

namespace media {

// Signal a pause in low-latency mode.
const int AudioOutputController::kPauseMark = -1;

// Polling-related constants.
const int AudioOutputController::kPollNumAttempts = 3;
const int AudioOutputController::kPollPauseInMilliseconds = 3;

AudioOutputController::AudioOutputController(EventHandler* handler,
                                             SyncReader* sync_reader)
    : handler_(handler),
      stream_(NULL),
      volume_(1.0),
      state_(kEmpty),
      sync_reader_(sync_reader),
      message_loop_(NULL),
      number_polling_attempts_left_(0),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)) {
}

AudioOutputController::~AudioOutputController() {
  DCHECK_EQ(kClosed, state_);
  DCHECK(message_loop_);

  if (!message_loop_.get() || message_loop_->BelongsToCurrentThread()) {
    DoStopCloseAndClearStream(NULL);
  } else {
    // http://crbug.com/120973
    base::ThreadRestrictions::ScopedAllowWait allow_wait;
    WaitableEvent completion(true /* manual reset */,
                             false /* initial state */);
    message_loop_->PostTask(FROM_HERE,
        base::Bind(&AudioOutputController::DoStopCloseAndClearStream,
                   base::Unretained(this),
                   &completion));
    completion.Wait();
  }
}

// static
scoped_refptr<AudioOutputController> AudioOutputController::Create(
    AudioManager* audio_manager,
    EventHandler* event_handler,
    const AudioParameters& params,
    SyncReader* sync_reader) {
  DCHECK(audio_manager);
  DCHECK(sync_reader);

  if (!params.IsValid() || !audio_manager)
    return NULL;

  // Starts the audio controller thread.
  scoped_refptr<AudioOutputController> controller(new AudioOutputController(
      event_handler, sync_reader));

  controller->message_loop_ = audio_manager->GetMessageLoop();
  controller->message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoCreate, controller,
      base::Unretained(audio_manager), params));
  return controller;
}

void AudioOutputController::Play() {
  DCHECK(message_loop_);
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoPlay, this));
}

void AudioOutputController::Pause() {
  DCHECK(message_loop_);
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoPause, this));
}

void AudioOutputController::Flush() {
  DCHECK(message_loop_);
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoFlush, this));
}

void AudioOutputController::Close(const base::Closure& closed_task) {
  DCHECK(!closed_task.is_null());
  DCHECK(message_loop_);
  message_loop_->PostTaskAndReply(FROM_HERE, base::Bind(
      &AudioOutputController::DoClose, this), closed_task);
}

void AudioOutputController::SetVolume(double volume) {
  DCHECK(message_loop_);
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoSetVolume, this, volume));
}

void AudioOutputController::DoCreate(AudioManager* audio_manager,
                                     const AudioParameters& params) {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // Close() can be called before DoCreate() is executed.
  if (state_ == kClosed)
    return;
  DCHECK_EQ(kEmpty, state_);

  DoStopCloseAndClearStream(NULL);
  stream_ = audio_manager->MakeAudioOutputStreamProxy(params);
  if (!stream_) {
    // TODO(hclam): Define error types.
    handler_->OnError(this, 0);
    return;
  }

  if (!stream_->Open()) {
    DoStopCloseAndClearStream(NULL);

    // TODO(hclam): Define error types.
    handler_->OnError(this, 0);
    return;
  }

  // We have successfully opened the stream. Set the initial volume.
  stream_->SetVolume(volume_);

  // Finally set the state to kCreated.
  state_ = kCreated;

  // And then report we have been created.
  handler_->OnCreated(this);
}

void AudioOutputController::DoPlay() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // We can start from created or paused state.
  if (state_ != kCreated && state_ != kPaused)
    return;

  state_ = kStarting;

  // Ask for first packet.
  sync_reader_->UpdatePendingBytes(0);

  // Cannot start stream immediately, should give renderer some time
  // to deliver data.
  // TODO(vrk): The polling here and in WaitTillDataReady() is pretty clunky.
  // Refine the API such that polling is no longer needed. (crbug.com/112196)
  number_polling_attempts_left_ = kPollNumAttempts;
  message_loop_->PostDelayedTask(
      FROM_HERE,
      base::Bind(&AudioOutputController::PollAndStartIfDataReady,
      weak_this_.GetWeakPtr()),
      base::TimeDelta::FromMilliseconds(kPollPauseInMilliseconds));
}

void AudioOutputController::PollAndStartIfDataReady() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // Being paranoid: do nothing if state unexpectedly changed.
  if ((state_ != kStarting) && (state_ != kPausedWhenStarting))
    return;

  bool pausing = (state_ == kPausedWhenStarting);
  // If we are ready to start the stream, start it.
  // Of course we may have to stop it immediately...
  if (--number_polling_attempts_left_ == 0 ||
      pausing ||
      sync_reader_->DataReady()) {
    StartStream();
    if (pausing) {
      DoPause();
    }
  } else {
    message_loop_->PostDelayedTask(
        FROM_HERE,
        base::Bind(&AudioOutputController::PollAndStartIfDataReady,
        weak_this_.GetWeakPtr()),
        base::TimeDelta::FromMilliseconds(kPollPauseInMilliseconds));
  }
}

void AudioOutputController::StartStream() {
  DCHECK(message_loop_->BelongsToCurrentThread());
  state_ = kPlaying;

  // We start the AudioOutputStream lazily.
  stream_->Start(this);

  // Tell the event handler that we are now playing.
  handler_->OnPlaying(this);
}

void AudioOutputController::DoPause() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  if (stream_)
    stream_->Stop();

  switch (state_) {
    case kStarting:
      // We were asked to pause while starting. There is delayed task that will
      // try starting playback, and there is no way to remove that task from the
      // queue. If we stop now that task will be executed anyway.
      // Delay pausing, let delayed task to do pause after it start playback.
      state_ = kPausedWhenStarting;
      break;
    case kPlaying:
      state_ = kPaused;

      // Then we stop the audio device. This is not the perfect solution
      // because it discards all the internal buffer in the audio device.
      // TODO(hclam): Actually pause the audio device.
      stream_->Stop();

      // Send a special pause mark to the low-latency audio thread.
      sync_reader_->UpdatePendingBytes(kPauseMark);

      handler_->OnPaused(this);
      break;
    default:
      return;
  }
}

void AudioOutputController::DoFlush() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // TODO(hclam): Actually flush the audio device.
}

void AudioOutputController::DoClose() {
  DCHECK(message_loop_->BelongsToCurrentThread());

  if (state_ != kClosed) {
    DoStopCloseAndClearStream(NULL);
    sync_reader_->Close();
    state_ = kClosed;
  }
}

void AudioOutputController::DoSetVolume(double volume) {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // Saves the volume to a member first. We may not be able to set the volume
  // right away but when the stream is created we'll set the volume.
  volume_ = volume;

  switch (state_) {
    case kCreated:
    case kStarting:
    case kPausedWhenStarting:
    case kPlaying:
    case kPaused:
      stream_->SetVolume(volume_);
      break;
    default:
      return;
  }
}

void AudioOutputController::DoReportError(int code) {
  DCHECK(message_loop_->BelongsToCurrentThread());
  if (state_ != kClosed)
    handler_->OnError(this, code);
}

uint32 AudioOutputController::OnMoreData(uint8* dest,
                                         uint32 max_size,
                                         AudioBuffersState buffers_state) {
  TRACE_EVENT0("audio", "AudioOutputController::OnMoreData");

  {
    // Check state and do nothing if we are not playing.
    // We are on the hardware audio thread, so lock is needed.
    base::AutoLock auto_lock(lock_);
    if (state_ != kPlaying) {
      return 0;
    }
  }
  uint32 size = sync_reader_->Read(dest, max_size);
  sync_reader_->UpdatePendingBytes(buffers_state.total_bytes() + size);
  return size;
}

void AudioOutputController::WaitTillDataReady() {
  if (!sync_reader_->DataReady()) {
    // In the different place we use different mechanism to poll, get max
    // polling delay from constants used there.
    const base::TimeDelta kMaxPollingDelay = base::TimeDelta::FromMilliseconds(
        kPollNumAttempts * kPollPauseInMilliseconds);
    Time start_time = Time::Now();
    do {
      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
    } while (!sync_reader_->DataReady() &&
             Time::Now() - start_time < kMaxPollingDelay);
  }
}

void AudioOutputController::OnError(AudioOutputStream* stream, int code) {
  // Handle error on the audio controller thread.
  message_loop_->PostTask(FROM_HERE, base::Bind(
      &AudioOutputController::DoReportError, this, code));
}

void AudioOutputController::DoStopCloseAndClearStream(WaitableEvent *done) {
  DCHECK(message_loop_->BelongsToCurrentThread());

  // Allow calling unconditionally and bail if we don't have a stream_ to close.
  if (stream_ != NULL) {
    stream_->Stop();
    stream_->Close();
    stream_ = NULL;
    weak_this_.InvalidateWeakPtrs();
  }

  // Should be last in the method, do not touch "this" from here on.
  if (done != NULL)
    done->Signal();
}

}  // namespace media
<|endoftext|>


<commit_before>#include <stdio.h>
#include <stdlib.h>
#include "traffic.hpp"
#include "../search/greedy.hpp"

int main(int argc, const char *argv[]) {

	if(argc < 4) {
		fprintf(stderr, "usage: %s width height num_obs seed\n", argv[0]);
		return 0;
	}

	int width = strtol(argv[1], NULL, 10);
	int height = strtol(argv[2], NULL, 10);
	int num_obs = strtol(argv[3], NULL, 10);
	int seed = strtol(argv[4], NULL, 10);

	srand(seed);

	while(true) {
		GridMap map(width, height);

		for(int i = 0; i < num_obs; i++) {
			map.obstacles.emplace_back( (rand() % (width-1)) + 1, //x
									(rand() % (height-1)) + 1, //y
									(rand() % 3) - 1,		//dx
									(rand() % 3) - 1);		//dy
		}

		Traffic traffic(&map, 0, 0, width-1, height-1);
		Greedy<Traffic, true> greedy(0, NULL);
		Traffic::State start = traffic.initialstate();
		greedy.search(traffic, start);
		bool solvable = (greedy.res.ops.size() > 0);
		if(solvable) {
			traffic.output(stdout);
			break;
		}
		else {
			fprintf(stderr, "NOT SOLVABLE\n");
		}
	}

}
<commit_msg>traffic: limit the speedy search for generating instances.<commit_after>#include <stdio.h>
#include <stdlib.h>
#include "traffic.hpp"
#include "../search/greedy.hpp"

int main(int argc, const char *argv[]) {

	if(argc < 4) {
		fprintf(stderr, "usage: %s width height num_obs seed\n", argv[0]);
		return 0;
	}

	int width = strtol(argv[1], NULL, 10);
	int height = strtol(argv[2], NULL, 10);
	int num_obs = strtol(argv[3], NULL, 10);
	int seed = strtol(argv[4], NULL, 10);

	srand(seed);

	while(true) {
		GridMap map(width, height);

		for(int i = 0; i < num_obs; i++) {
			map.obstacles.emplace_back( (rand() % (width-1)) + 1, //x
									(rand() % (height-1)) + 1, //y
									(rand() % 3) - 1,		//dx
									(rand() % 3) - 1);		//dy
		}

		Traffic traffic(&map, 0, 0, width-1, height-1);
		Greedy<Traffic, true> greedy(0, NULL);

		const char *lim[4];
		lim[0] = "-mem";
		lim[1] = "6G";
		lim[2] = "-walltime";
		lim[3] = "300";
		greedy.lim = Limit(sizeof(lim)/sizeof(lim[0]), lim);

		Traffic::State start = traffic.initialstate();
		greedy.search(traffic, start);
		bool solvable = (greedy.res.ops.size() > 0);
		if(solvable) {
			traffic.output(stdout);
			break;
		}
		else {
			fprintf(stderr, "NOT SOLVABLE\n");
		}
	}

}
<|endoftext|>


<commit_before>#include "unit_tests/suite/lumix_unit_tests.h"

#include "core/log.h"
#include "core/mt/lock_free_fixed_queue.h"
#include "core/mt/task.h"
#include "core/MT/thread.h"
#include "core/mt/transaction.h"
#include "core/queue.h"
#include "core/array.h"

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <cstdio>

//#define ASSERT_HANDLE_FAIL

namespace Lumix
{
	namespace UnitTest
	{
		Manager* Manager::s_instance = NULL;

		static const int32_t C_MAX_TRANS = 16;

		struct UnitTestPair
		{
			const char* name;
			const char* parameters;
			Manager::unitTestFunc func;
		};

		struct FailInfo
		{
			const char* m_file_name;
			uint32_t m_line;
		};

		typedef MT::Transaction<UnitTestPair> AsynTest;
		typedef MT::LockFreeFixedQueue<AsynTest, C_MAX_TRANS> TransQueue;
		typedef Queue<AsynTest*, C_MAX_TRANS> InProgressQueue;

		class WorkerTask : public MT::Task
		{
		public:
			WorkerTask(TransQueue* tests_todo, IAllocator& allocator)
				: MT::Task(allocator)
				, m_tests_todo(tests_todo)
			{
			}

			virtual int task() override
			{
				while(!m_tests_todo->isAborted())
				{
					AsynTest* test = m_tests_todo->pop(true);
					if(NULL == test)
						break;

					UnitTestPair& ut = test->data;

					g_log_info.log("unit") << "-------------------------";
					g_log_info.log("unit") << ut.name;
					g_log_info.log("unit") << "-------------------------";
					ut.func(ut.parameters);
					g_log_info.log("unit") << "-------------------------";

					test->setCompleted();
				}

				return 0;
			}

		private:
			TransQueue* m_tests_todo;
		};

		typedef Array<UnitTestPair> UnitTestTable;
		typedef Array<FailInfo> FailedTestTable;

		struct ManagerImpl
		{
		public:
			void registerFunction(const char* name, Manager::unitTestFunc func, const char* params)
			{
				UnitTestPair& pair = m_unit_tests.pushEmpty();
				pair.name = name;
				pair.parameters = params;
				pair.func = func;
			}

			void dumpTests() const
			{
				for(int i = 0, c = m_unit_tests.size(); i < c; ++i)
				{
					g_log_info.log("unit") << m_unit_tests[i].name;
				}

				g_log_info.log("unit") << "";
				g_log_info.log("unit") << "Running tests ...";
				g_log_info.log("unit") << "";
			}

			void runTests(const char* filter_tests)
			{
				spawnWorkerTask();
				int i = 0, c = m_unit_tests.size();
				while(i < c || m_in_progress.size() != 0 || !m_trans_queue.isEmpty())
				{
					if(m_in_progress.size() > 0)
					{
						AsynTest* test = m_in_progress.front();
						if(test->isCompleted())
						{
							m_in_progress.pop();
							m_trans_queue.dealoc(test);
						}
					}

					if(i < c)
					{
						UnitTestPair& pair = m_unit_tests[i];
						if (shouldTest(string(pair.name, m_allocator), string(filter_tests, m_allocator)))
						{
							AsynTest* test = m_trans_queue.alloc(false);
							if (test)
							{
								test->data.name = pair.name;
								test->data.func = pair.func;
								test->data.parameters = pair.parameters;
								i++;
								m_trans_queue.push(test, true);
								m_in_progress.push(test);
							}
						}
						else
						{
							++i;
							Lumix::MT::yield();
						}
					}

					// fatal error occured. We need to respawn task.
					if (m_task.isFinished())
					{
						// test failed, remove it from the queue and spawn new thread
						AsynTest* test = m_in_progress.front();
						m_in_progress.pop();
							m_trans_queue.dealoc(test);

						m_task.destroy();
						spawnWorkerTask();
					}

					Lumix::MT::yield();
				}

				m_trans_queue.abort();
			}

			void dumpResults() const
			{

				if (m_fails > 0)
				{
					g_log_info.log("unit") << "----------Fails----------";
					for (int i = 0; i < m_failed_tests.size(); i++)
					{
						g_log_info.log("unit") << m_failed_tests[i].m_file_name << "(" << m_failed_tests[i].m_line << ")";
					}
				}

				FILE* fout = fopen("tests.xml", "w");

				fprintf(fout, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>"
					"<test-results  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"nunit_schema_2.5.xsd\" name=\"Lumix\" total=\"%d\" errors=\"0\" failures=\"%d\" not-run=\"0\" inconclusive=\"0\" ignored=\"0\" skipped=\"0\" invalid=\"0\">"
					"<culture-info current-culture=\"\" current-uiculture=\"\" />"
					, m_failed_tests.size(), m_failed_tests.size());

				for (int i = 0; i < m_failed_tests.size(); i++)
				{
					fprintf(fout, "<test-suite type=\"Powershell\" name=\"%s\" executed=\"True\" result=\"Failure\" success=\"False\" time=\"0\" asserts=\"0\"> <results>"
						"<test-case name=\"line %d\" executed=\"True\" result=\"Failure\" success=\"False\" time=\"0.1443834\" asserts=\"0\"> 	<failure> 		<message>error message</message> 	<stack-trace></stack-trace> 	</failure> </test-case></results> </test-suite> 	", m_failed_tests[i].m_file_name, m_failed_tests[i].m_line);
				}

				fprintf(fout, "</test-results>");
				fclose(fout);

				g_log_info.log("unit") << "--------- Results ---------";
				g_log_info.log("unit") << "Fails:     " << m_fails;
				g_log_info.log("unit") << "---------------------------";
			}

			void handleFail(const char* file_name, uint32_t line)
			{
				FailInfo& fi = m_failed_tests.pushEmpty();
				fi.m_file_name = file_name;
				fi.m_line = line;
				m_fails++;

#if ASSERT_HANDLE_FAIL
				ASSERT(false);
#endif //ASSERT_HANDLE_FAIL

				m_task.exit(10);
			}

			void spawnWorkerTask()
			{
				ASSERT(!m_task.isRunning());
				m_task.create("TestWorkerTask");
				m_task.run();
			}

			ManagerImpl(IAllocator& allocator)
				: m_fails(0)
				, m_task(&m_trans_queue, allocator)
				, m_in_progress(allocator)
				, m_trans_queue(allocator)
				, m_allocator(allocator)
				, m_unit_tests(allocator)
				, m_failed_tests(allocator)
			{
			}

			~ManagerImpl()
			{
				m_task.destroy();
			}

			IAllocator& getAllocator() { return m_allocator; }

		private:

			bool shouldTest(const string& name, const string& filter)
			{
				if (filter.length() > 1)
				{
					if (filter[0] == '*')
					{
						string filter_part = filter.substr(1, filter.length() - 1);

						return name.length() > filter_part.length()
							&& (name.substr(name.length() - filter_part.length(), filter_part.length()) == filter_part);
					}
					else if (filter[filter.length() - 1] == '*')
					{
						string filter_part = filter.substr(0, filter.length() - 1);

						return name.length() > filter_part.length()
							&& (name.substr(0, filter_part.length()) == filter_part);
					}
					else
					{
						return name == filter;
					}
				}
				else if (filter == "*")
				{
					return true;
				}

				return false;
			}

			IAllocator& m_allocator;
			uint32_t m_fails;

			UnitTestTable	m_unit_tests;
			FailedTestTable m_failed_tests;
			TransQueue		m_trans_queue;
			InProgressQueue m_in_progress;

			WorkerTask m_task;
		};

		void Manager::registerFunction(const char* name, Manager::unitTestFunc func, const char* params)
		{
			m_impl->registerFunction(name, func, params);
		}

		void Manager::dumpTests() const
		{
			m_impl->dumpTests();
		}

		/// there are four supported modes:
		// "*" -> runs all tests
		// "*test_name" -> runs all tests ending with test_names
		// test_name* -> runs all tests beggining with test_name
		// test_name -> runs all tests matching test_name
		void Manager::runTests(const char *filter_tests)
		{
			m_impl->runTests(filter_tests);
		}

		void Manager::dumpResults() const
		{
			m_impl->dumpResults();
		}

		void Manager::handleFail(const char* file_name, uint32_t line)
		{
			m_impl->handleFail(file_name, line);
		}

		Manager::Manager(IAllocator& allocator)
		{
			m_impl = allocator.newObject<ManagerImpl>(allocator);
		}

		Manager::~Manager()
		{
			m_impl->getAllocator().deleteObject(m_impl);
		}
	} //~UnitTest
} //~UnitTest<commit_msg>asserts if any unit test fails - closes #139<commit_after>#include "unit_tests/suite/lumix_unit_tests.h"

#include "core/log.h"
#include "core/mt/lock_free_fixed_queue.h"
#include "core/mt/task.h"
#include "core/MT/thread.h"
#include "core/mt/transaction.h"
#include "core/queue.h"
#include "core/array.h"

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <cstdio>

//#define ASSERT_HANDLE_FAIL

namespace Lumix
{
	namespace UnitTest
	{
		Manager* Manager::s_instance = NULL;

		static const int32_t C_MAX_TRANS = 16;

		struct UnitTestPair
		{
			const char* name;
			const char* parameters;
			Manager::unitTestFunc func;
		};

		struct FailInfo
		{
			const char* m_file_name;
			uint32_t m_line;
		};

		typedef MT::Transaction<UnitTestPair> AsynTest;
		typedef MT::LockFreeFixedQueue<AsynTest, C_MAX_TRANS> TransQueue;
		typedef Queue<AsynTest*, C_MAX_TRANS> InProgressQueue;

		class WorkerTask : public MT::Task
		{
		public:
			WorkerTask(TransQueue* tests_todo, IAllocator& allocator)
				: MT::Task(allocator)
				, m_tests_todo(tests_todo)
			{
			}

			virtual int task() override
			{
				while(!m_tests_todo->isAborted())
				{
					AsynTest* test = m_tests_todo->pop(true);
					if(NULL == test)
						break;

					UnitTestPair& ut = test->data;

					g_log_info.log("unit") << "-------------------------";
					g_log_info.log("unit") << ut.name;
					g_log_info.log("unit") << "-------------------------";
					ut.func(ut.parameters);
					g_log_info.log("unit") << "-------------------------";

					test->setCompleted();
				}

				return 0;
			}

		private:
			TransQueue* m_tests_todo;
		};

		typedef Array<UnitTestPair> UnitTestTable;
		typedef Array<FailInfo> FailedTestTable;

		struct ManagerImpl
		{
		public:
			void registerFunction(const char* name, Manager::unitTestFunc func, const char* params)
			{
				UnitTestPair& pair = m_unit_tests.pushEmpty();
				pair.name = name;
				pair.parameters = params;
				pair.func = func;
			}

			void dumpTests() const
			{
				for(int i = 0, c = m_unit_tests.size(); i < c; ++i)
				{
					g_log_info.log("unit") << m_unit_tests[i].name;
				}

				g_log_info.log("unit") << "";
				g_log_info.log("unit") << "Running tests ...";
				g_log_info.log("unit") << "";
			}

			void runTests(const char* filter_tests)
			{
				spawnWorkerTask();
				int i = 0, c = m_unit_tests.size();
				while(i < c || m_in_progress.size() != 0 || !m_trans_queue.isEmpty())
				{
					if(m_in_progress.size() > 0)
					{
						AsynTest* test = m_in_progress.front();
						if(test->isCompleted())
						{
							m_in_progress.pop();
							m_trans_queue.dealoc(test);
						}
					}

					if(i < c)
					{
						UnitTestPair& pair = m_unit_tests[i];
						if (shouldTest(string(pair.name, m_allocator), string(filter_tests, m_allocator)))
						{
							AsynTest* test = m_trans_queue.alloc(false);
							if (test)
							{
								test->data.name = pair.name;
								test->data.func = pair.func;
								test->data.parameters = pair.parameters;
								i++;
								m_trans_queue.push(test, true);
								m_in_progress.push(test);
							}
						}
						else
						{
							++i;
							Lumix::MT::yield();
						}
					}

					// fatal error occured. We need to respawn task.
					if (m_task.isFinished())
					{
						// test failed, remove it from the queue and spawn new thread
						AsynTest* test = m_in_progress.front();
						m_in_progress.pop();
							m_trans_queue.dealoc(test);

						m_task.destroy();
						spawnWorkerTask();
					}

					Lumix::MT::yield();
				}

				m_trans_queue.abort();
			}

			void dumpResults() const
			{

				if (m_fails > 0)
				{
					ASSERT(false);

					g_log_info.log("unit") << "----------Fails----------";
					for (int i = 0; i < m_failed_tests.size(); i++)
					{
						g_log_info.log("unit") << m_failed_tests[i].m_file_name << "(" << m_failed_tests[i].m_line << ")";
					}
				}

				FILE* fout = fopen("tests.xml", "w");

				fprintf(fout, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>"
					"<test-results  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"nunit_schema_2.5.xsd\" name=\"Lumix\" total=\"%d\" errors=\"0\" failures=\"%d\" not-run=\"0\" inconclusive=\"0\" ignored=\"0\" skipped=\"0\" invalid=\"0\">"
					"<culture-info current-culture=\"\" current-uiculture=\"\" />"
					, m_failed_tests.size(), m_failed_tests.size());

				for (int i = 0; i < m_failed_tests.size(); i++)
				{
					fprintf(fout, "<test-suite type=\"Powershell\" name=\"%s\" executed=\"True\" result=\"Failure\" success=\"False\" time=\"0\" asserts=\"0\"> <results>"
						"<test-case name=\"line %d\" executed=\"True\" result=\"Failure\" success=\"False\" time=\"0.1443834\" asserts=\"0\"> 	<failure> 		<message>error message</message> 	<stack-trace></stack-trace> 	</failure> </test-case></results> </test-suite> 	", m_failed_tests[i].m_file_name, m_failed_tests[i].m_line);
				}

				fprintf(fout, "</test-results>");
				fclose(fout);

				g_log_info.log("unit") << "--------- Results ---------";
				g_log_info.log("unit") << "Fails:     " << m_fails;
				g_log_info.log("unit") << "---------------------------";
			}

			void handleFail(const char* file_name, uint32_t line)
			{
				FailInfo& fi = m_failed_tests.pushEmpty();
				fi.m_file_name = file_name;
				fi.m_line = line;
				m_fails++;

#if ASSERT_HANDLE_FAIL
				ASSERT(false);
#endif //ASSERT_HANDLE_FAIL

				m_task.exit(10);
			}

			void spawnWorkerTask()
			{
				ASSERT(!m_task.isRunning());
				m_task.create("TestWorkerTask");
				m_task.run();
			}

			ManagerImpl(IAllocator& allocator)
				: m_fails(0)
				, m_task(&m_trans_queue, allocator)
				, m_in_progress(allocator)
				, m_trans_queue(allocator)
				, m_allocator(allocator)
				, m_unit_tests(allocator)
				, m_failed_tests(allocator)
			{
			}

			~ManagerImpl()
			{
				m_task.destroy();
			}

			IAllocator& getAllocator() { return m_allocator; }

		private:

			bool shouldTest(const string& name, const string& filter)
			{
				if (filter.length() > 1)
				{
					if (filter[0] == '*')
					{
						string filter_part = filter.substr(1, filter.length() - 1);

						return name.length() > filter_part.length()
							&& (name.substr(name.length() - filter_part.length(), filter_part.length()) == filter_part);
					}
					else if (filter[filter.length() - 1] == '*')
					{
						string filter_part = filter.substr(0, filter.length() - 1);

						return name.length() > filter_part.length()
							&& (name.substr(0, filter_part.length()) == filter_part);
					}
					else
					{
						return name == filter;
					}
				}
				else if (filter == "*")
				{
					return true;
				}

				return false;
			}

			IAllocator& m_allocator;
			uint32_t m_fails;

			UnitTestTable	m_unit_tests;
			FailedTestTable m_failed_tests;
			TransQueue		m_trans_queue;
			InProgressQueue m_in_progress;

			WorkerTask m_task;
		};

		void Manager::registerFunction(const char* name, Manager::unitTestFunc func, const char* params)
		{
			m_impl->registerFunction(name, func, params);
		}

		void Manager::dumpTests() const
		{
			m_impl->dumpTests();
		}

		/// there are four supported modes:
		// "*" -> runs all tests
		// "*test_name" -> runs all tests ending with test_names
		// test_name* -> runs all tests beggining with test_name
		// test_name -> runs all tests matching test_name
		void Manager::runTests(const char *filter_tests)
		{
			m_impl->runTests(filter_tests);
		}

		void Manager::dumpResults() const
		{
			m_impl->dumpResults();
		}

		void Manager::handleFail(const char* file_name, uint32_t line)
		{
			m_impl->handleFail(file_name, line);
		}

		Manager::Manager(IAllocator& allocator)
		{
			m_impl = allocator.newObject<ManagerImpl>(allocator);
		}

		Manager::~Manager()
		{
			m_impl->getAllocator().deleteObject(m_impl);
		}
	} //~UnitTest
} //~UnitTest<|endoftext|>


<commit_before>#include "jni_util.h"

extern "C" {

void helloworld(JNIEnv *env, jobject clazz, jstring jni_str) {
  const char *str_utf8 = env->GetStringUTFChars(jni_str, nullptr);
  LOGI("%s", str_utf8);
  env->ReleaseStringUTFChars(jni_str, str_utf8);
}

}

JNINativeMethod g_helloworld_methods[] = {
    {"say_hello", "(Ljava/lang/String;)V", (void *)helloworld}
};
extern "C" const int g_helloworld_methods_num = sizeof(g_helloworld_methods) / sizeof(g_helloworld_methods[0]);
<commit_msg>JNIEXPORT AND JNICALL<commit_after>#include "jni_util.h"

extern "C" {
JNIEXPORT void JNICALL helloworld(JNIEnv *env, jobject clazz, jstring jni_str);
}

JNINativeMethod g_helloworld_methods[] = {
    {"say_hello", "(Ljava/lang/String;)V", (void *)helloworld}
};
extern "C" const int g_helloworld_methods_num = sizeof(g_helloworld_methods) / sizeof(g_helloworld_methods[0]);

void helloworld(JNIEnv *env, jobject clazz, jstring jni_str) {
  const char *str_utf8 = env->GetStringUTFChars(jni_str, nullptr);
  LOGI("%s", str_utf8);
  env->ReleaseStringUTFChars(jni_str, str_utf8);
}
<|endoftext|>


<commit_before>/*
 * Instance.hpp -- reference counting and secure destruction
 *
 * Copyright (c) 2007-2009, Frank Mertens
 *
 * See ../LICENSE for the license.
 */
#ifndef PONA_INSTANCE_HPP
#define PONA_INSTANCE_HPP

#include "Exception.hpp"

namespace pona
{

PONA_EXCEPTION(ReferenceException, Exception);

class Instance;

class BackRef
{
public:
	BackRef(void** instance): instance_(instance) {}
	BackRef* pred_;
	BackRef* succ_;
	void** instance_;
};

/** \brief reference counting and secure destruction
  *
  * Base class for all classes T, which instances can be referred to by Ref<T>.
  * Enforces a consistent allocation schema by suppressing two things:
  *   - combination of static allocation and dynamic destruction
  *   - manual detruction by delete operator
  * In both cases an exception of type ReferenceException is thrown.
  */
class Instance
{
public:
	Instance(): refCount_(0), backRefList_(0) {}

	virtual ~Instance()
	{
		if (refCount_ > 0)
			PONA_THROW(ReferenceException, "Deleting object, which is still in use");
		BackRef* ref = backRefList_;
		while (ref) {
			*ref->instance_ = 0;
			ref = ref->succ_;
		}
	}

	inline int refCount() const { return refCount_; }
	inline void incRefCount() { refCount_ += (refCount_ >= 0); }
	inline void decRefCount() {
		refCount_ -= (refCount_ >= 0);
		if (refCount_ == 0)
				delete this;
	}

	inline void liberate() { refCount_ = -1; }

	virtual void acquire() {}
	virtual void release() {}

	inline void addBackRef(BackRef* ref)
	{
		acquire();
		ref->succ_ = backRefList_;
		ref->pred_ = 0;
		if (backRefList_)
			backRefList_->pred_ = ref;
		backRefList_ = ref;
		release();
	}

	inline void delBackRef(BackRef* ref)
	{
		acquire();
		if (ref->pred_)
			ref->pred_->succ_ = ref->succ_;
		if (ref->succ_)
			ref->succ_->pred_ = ref->pred_;
		if (ref == backRefList_)
			backRefList_ = backRefList_->succ_;
		release();
	}

private:
	int refCount_;
	BackRef* backRefList_;
};

} // namespace pona

#endif // PONA_INSTANCE_HPP
<commit_msg>Minimal, but important: Making the two final simultanous dereferencing of shared objects thread-safe. (Affects only real-time/preemptively scheduled threads.)<commit_after>/*
 * Instance.hpp -- reference counting and secure destruction
 *
 * Copyright (c) 2007-2009, Frank Mertens
 *
 * See ../LICENSE for the license.
 */
#ifndef PONA_INSTANCE_HPP
#define PONA_INSTANCE_HPP

#include "Exception.hpp"

namespace pona
{

PONA_EXCEPTION(ReferenceException, Exception);

class Instance;

class BackRef
{
public:
	BackRef(void** instance): instance_(instance) {}
	BackRef* pred_;
	BackRef* succ_;
	void** instance_;
};

/** \brief reference counting and secure destruction
  *
  * Base class for all classes T, which instances can be referred to by Ref<T>.
  * Enforces a consistent allocation schema by suppressing two things:
  *   - combination of static allocation and dynamic destruction
  *   - manual detruction by delete operator
  * In both cases an exception of type ReferenceException is thrown.
  */
class Instance
{
public:
	Instance(): refCount_(0), backRefList_(0) {}

	virtual ~Instance()
	{
		if (refCount_ > 0)
			PONA_THROW(ReferenceException, "Deleting object, which is still in use");
		BackRef* ref = backRefList_;
		while (ref) {
			*ref->instance_ = 0;
			ref = ref->succ_;
		}
	}

	inline int refCount() const { return refCount_; }
	inline void incRefCount() { refCount_ += (refCount_ >= 0); }
	inline void decRefCount() {
		acquire();
		refCount_ -= (refCount_ >= 0);
		if (refCount_ == 0) {
			release();
			delete this;
		}
		else
			release();
	}

	inline void liberate() { refCount_ = -1; }

	virtual void acquire() {}
	virtual void release() {}

	inline void addBackRef(BackRef* ref)
	{
		acquire();
		ref->succ_ = backRefList_;
		ref->pred_ = 0;
		if (backRefList_)
			backRefList_->pred_ = ref;
		backRefList_ = ref;
		release();
	}

	inline void delBackRef(BackRef* ref)
	{
		acquire();
		if (ref->pred_)
			ref->pred_->succ_ = ref->succ_;
		if (ref->succ_)
			ref->succ_->pred_ = ref->pred_;
		if (ref == backRefList_)
			backRefList_ = backRefList_->succ_;
		release();
	}

private:
	int refCount_;
	BackRef* backRefList_;
};

} // namespace pona

#endif // PONA_INSTANCE_HPP
<|endoftext|>