<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "projectmanager.h"

// appleseed.shared headers.
#include "application/application.h"

// Qt headers.
#include <QtConcurrentRun>

// boost headers.
#include "boost/filesystem/path.hpp"

using namespace appleseed::shared;
using namespace boost;
using namespace foundation;
using namespace renderer;
using namespace std;

namespace appleseed {
namespace studio {

//
// ProjectManager class implementation.
//

ProjectManager::ProjectManager()
  : m_dirty_flag(false)
{
    connect(
        &m_async_io_future_watcher, SIGNAL(finished()),
        this, SLOT(slot_load_project_async_complete()));
}

void ProjectManager::create_project()
{
    const bool result = load_builtin_project("default");
    assert(result);
}

void ProjectManager::load_project(const string& filepath)
{
    m_async_io_filepath = QString::fromStdString(filepath);

    m_async_io_future_watcher.setFuture(
        QtConcurrent::run(this, &ProjectManager::do_load_project, filepath));
}

bool ProjectManager::load_builtin_project(const string& name)
{
    ProjectFileReader reader;
    m_project = reader.load_builtin(name.c_str());

    m_dirty_flag = false;

    return true;
}

bool ProjectManager::save_project()
{
    return do_save_project_as(m_project->get_path());
}

bool ProjectManager::save_project_as(const string& filepath)
{
    return do_save_project_as(filepath, ProjectFileWriter::OmitSearchPaths);
}

bool ProjectManager::do_save_project_as(const string& filepath,
                                        ProjectFileWriter::Options options)
{
    assert(m_project.get());

    if (!ProjectFileWriter::write(m_project.ref(),
                                  filepath.c_str(),
                                  options))
        return false;

    m_project->set_path(filepath.c_str());

    m_dirty_flag = false;

    return true;
}

void ProjectManager::close_project()
{
    m_project.reset();
    m_dirty_flag = false;
}

Project* ProjectManager::get_project() const
{
    return m_project.get();
}

bool ProjectManager::is_project_open() const
{
    return m_project.get() != 0;
}

string ProjectManager::get_project_display_name() const
{
    assert(m_project.get());

    if (m_project->has_path())
    {
        const filesystem::path filepath(m_project->get_path());
        return filepath.filename().string();
    }
    else
    {
        return "Untitled";
    }
}

void ProjectManager::clear_project_dirty_flag()
{
    m_dirty_flag = false;
}

void ProjectManager::set_project_dirty_flag()
{
    m_dirty_flag = true;
}

bool ProjectManager::is_project_dirty() const
{
    return m_dirty_flag;
}

void ProjectManager::slot_load_project_async_complete()
{
    // Can't use qobject_cast<>: https://bugreports.qt-project.org/browse/QTBUG-10727.
    const bool successful =
        static_cast<QFutureWatcher<bool>*>(sender())->future().result();

    emit signal_load_project_async_complete(m_async_io_filepath, successful);
}

string ProjectManager::get_project_schema_filepath()
{
    const filesystem::path schema_path =
          filesystem::path(Application::get_root_path())
        / "schemas"
        / "project.xsd";

    return schema_path.string();
}

bool ProjectManager::do_load_project(const string& filepath)
{
    const string schema_filepath = get_project_schema_filepath();

    ProjectFileReader reader;
    auto_release_ptr<Project> loaded_project(
        reader.read(filepath.c_str(), schema_filepath.c_str()));

    if (loaded_project.get() == 0)
        return false;

    m_project = loaded_project;
    m_dirty_flag = false;

    return true;
}

}   // namespace studio
}   // namespace appleseed
<commit_msg>removed extra whitespace<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "projectmanager.h"

// appleseed.shared headers.
#include "application/application.h"

// Qt headers.
#include <QtConcurrentRun>

// boost headers.
#include "boost/filesystem/path.hpp"

using namespace appleseed::shared;
using namespace boost;
using namespace foundation;
using namespace renderer;
using namespace std;

namespace appleseed {
namespace studio {

//
// ProjectManager class implementation.
//

ProjectManager::ProjectManager()
  : m_dirty_flag(false)
{
    connect(
        &m_async_io_future_watcher, SIGNAL(finished()),
        this, SLOT(slot_load_project_async_complete()));
}

void ProjectManager::create_project()
{
    const bool result = load_builtin_project("default");
    assert(result);
}

void ProjectManager::load_project(const string& filepath)
{
    m_async_io_filepath = QString::fromStdString(filepath);

    m_async_io_future_watcher.setFuture(
        QtConcurrent::run(this, &ProjectManager::do_load_project, filepath));
}

bool ProjectManager::load_builtin_project(const string& name)
{
    ProjectFileReader reader;
    m_project = reader.load_builtin(name.c_str());

    m_dirty_flag = false;

    return true;
}

bool ProjectManager::save_project()
{
    return do_save_project_as(m_project->get_path());
}

bool ProjectManager::save_project_as(const string& filepath)
{
    return do_save_project_as(filepath, ProjectFileWriter::OmitSearchPaths);
}

bool ProjectManager::do_save_project_as(const string& filepath,
                                        ProjectFileWriter::Options options)
{
    assert(m_project.get());

    if (!ProjectFileWriter::write(m_project.ref(),
                                  filepath.c_str(),
                                  options))
        return false;

    m_project->set_path(filepath.c_str());

    m_dirty_flag = false;

    return true;
}

void ProjectManager::close_project()
{
    m_project.reset();
    m_dirty_flag = false;
}

Project* ProjectManager::get_project() const
{
    return m_project.get();
}

bool ProjectManager::is_project_open() const
{
    return m_project.get() != 0;
}

string ProjectManager::get_project_display_name() const
{
    assert(m_project.get());

    if (m_project->has_path())
    {
        const filesystem::path filepath(m_project->get_path());
        return filepath.filename().string();
    }
    else
    {
        return "Untitled";
    }
}

void ProjectManager::clear_project_dirty_flag()
{
    m_dirty_flag = false;
}

void ProjectManager::set_project_dirty_flag()
{
    m_dirty_flag = true;
}

bool ProjectManager::is_project_dirty() const
{
    return m_dirty_flag;
}

void ProjectManager::slot_load_project_async_complete()
{
    // Can't use qobject_cast<>: https://bugreports.qt-project.org/browse/QTBUG-10727.
    const bool successful =
        static_cast<QFutureWatcher<bool>*>(sender())->future().result();

    emit signal_load_project_async_complete(m_async_io_filepath, successful);
}

string ProjectManager::get_project_schema_filepath()
{
    const filesystem::path schema_path =
          filesystem::path(Application::get_root_path())
        / "schemas"
        / "project.xsd";

    return schema_path.string();
}

bool ProjectManager::do_load_project(const string& filepath)
{
    const string schema_filepath = get_project_schema_filepath();

    ProjectFileReader reader;
    auto_release_ptr<Project> loaded_project(
        reader.read(filepath.c_str(), schema_filepath.c_str()));

    if (loaded_project.get() == 0)
        return false;

    m_project = loaded_project;
    m_dirty_flag = false;

    return true;
}

}   // namespace studio
}   // namespace appleseed
<|endoftext|>


<commit_before>/** Copyright (C) 2016, 2017 European Spallation Source ERIC */

#include <cassert>
#include <common/Detector.h>
#include <common/EFUArgs.h>
#include <common/Trace.h>
#include <efu/Launcher.h>
#include <iostream>
#include <thread>
#include <map>

void Launcher::launchThreads(std::shared_ptr<Detector> &detector) {
  auto startThreadsWithoutAffinity = [&detector]() {
    XTRACE(MAIN, ALW, "Launching threads without core affinity.\n");
    for (auto &ThreadInfo : detector->GetThreadInfo()) {
      XTRACE(MAIN, ALW, "Creating new thread (id: %s)\n", ThreadInfo.name.c_str());
      ThreadInfo.thread = std::thread(ThreadInfo.func);
    }
  };

  auto setThreadCoreAffinity = [](std::thread __attribute__((unused)) &thread, std::uint16_t __attribute__((unused)) core) {
#ifdef __linux__
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core, &cpuset);
    XTRACE(MAIN, ALW, "Setting thread affinity to core %d\n", core);
    GLOG_INF("Setting thread affinity to core " + std::to_string(core));

      int __attribute__((unused))s = pthread_setaffinity_np(thread.native_handle(), sizeof(cpu_set_t), &cpuset);
      assert(s == 0);
    }
#else
#pragma message("setaffinity only implemented for Linux")
    GLOG_WAR("setaffinity only implemented for Linux");
#endif
  };

  std::map<std::string,std::uint16_t> AffinityMap;
  for (auto &Affinity : ThreadCoreAffinity) {
    AffinityMap[Affinity.Name] = Affinity.Core;
  }
  if (0 == ThreadCoreAffinity.size()) {
    startThreadsWithoutAffinity();
  } else if (1 == ThreadCoreAffinity.size() and ThreadCoreAffinity[0].Name == "implicit_affinity") {
    XTRACE(MAIN, ALW, "Launching threads with implicit core affinity.\n");
    int CoreCounter = ThreadCoreAffinity[0].Core;
    for (auto &ThreadInfo : detector->GetThreadInfo()) {
      XTRACE(MAIN, ALW, "Creating new thread (id: %s)\n", ThreadInfo.name.c_str());
      ThreadInfo.thread = std::thread(ThreadInfo.func);
      setThreadCoreAffinity(ThreadInfo.thread, CoreCounter++);
    }
  } else {
    XTRACE(MAIN, ALW, "Launching threads with explicit core affinity.\n");
    for (auto &ThreadInfo : detector->GetThreadInfo()) {
      XTRACE(MAIN, ALW, "Creating new thread (id: %s)\n", ThreadInfo.name.c_str());
      ThreadInfo.thread = std::thread(ThreadInfo.func);
      if (1 == AffinityMap.count(ThreadInfo.name)) {
        setThreadCoreAffinity(ThreadInfo.thread, AffinityMap[ThreadInfo.name]);
      } else {
        XTRACE(MAIN, ALW, "No thread core affinity information available for thread with id: %s\n", ThreadInfo.name.c_str());
      }
    }
  }
}
<commit_msg>Minor typo.<commit_after>/** Copyright (C) 2016, 2017 European Spallation Source ERIC */

#include <cassert>
#include <common/Detector.h>
#include <common/EFUArgs.h>
#include <common/Trace.h>
#include <efu/Launcher.h>
#include <iostream>
#include <thread>
#include <map>

void Launcher::launchThreads(std::shared_ptr<Detector> &detector) {
  auto startThreadsWithoutAffinity = [&detector]() {
    XTRACE(MAIN, ALW, "Launching threads without core affinity.\n");
    for (auto &ThreadInfo : detector->GetThreadInfo()) {
      XTRACE(MAIN, ALW, "Creating new thread (id: %s)\n", ThreadInfo.name.c_str());
      ThreadInfo.thread = std::thread(ThreadInfo.func);
    }
  };

  auto setThreadCoreAffinity = [](std::thread __attribute__((unused)) &thread, std::uint16_t __attribute__((unused)) core) {
#ifdef __linux__
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core, &cpuset);
    XTRACE(MAIN, ALW, "Setting thread affinity to core %d\n", core);
    GLOG_INF("Setting thread affinity to core " + std::to_string(core));

      int __attribute__((unused))s = pthread_setaffinity_np(thread.native_handle(), sizeof(cpu_set_t), &cpuset);
      assert(s == 0);
#else
#pragma message("setaffinity only implemented for Linux")
    GLOG_WAR("setaffinity only implemented for Linux");
#endif
  };

  std::map<std::string,std::uint16_t> AffinityMap;
  for (auto &Affinity : ThreadCoreAffinity) {
    AffinityMap[Affinity.Name] = Affinity.Core;
  }
  if (0 == ThreadCoreAffinity.size()) {
    startThreadsWithoutAffinity();
  } else if (1 == ThreadCoreAffinity.size() and ThreadCoreAffinity[0].Name == "implicit_affinity") {
    XTRACE(MAIN, ALW, "Launching threads with implicit core affinity.\n");
    int CoreCounter = ThreadCoreAffinity[0].Core;
    for (auto &ThreadInfo : detector->GetThreadInfo()) {
      XTRACE(MAIN, ALW, "Creating new thread (id: %s)\n", ThreadInfo.name.c_str());
      ThreadInfo.thread = std::thread(ThreadInfo.func);
      setThreadCoreAffinity(ThreadInfo.thread, CoreCounter++);
    }
  } else {
    XTRACE(MAIN, ALW, "Launching threads with explicit core affinity.\n");
    for (auto &ThreadInfo : detector->GetThreadInfo()) {
      XTRACE(MAIN, ALW, "Creating new thread (id: %s)\n", ThreadInfo.name.c_str());
      ThreadInfo.thread = std::thread(ThreadInfo.func);
      if (1 == AffinityMap.count(ThreadInfo.name)) {
        setThreadCoreAffinity(ThreadInfo.thread, AffinityMap[ThreadInfo.name]);
      } else {
        XTRACE(MAIN, ALW, "No thread core affinity information available for thread with id: %s\n", ThreadInfo.name.c_str());
      }
    }
  }
}
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "masterrenderer.h"

// appleseed.renderer headers.
#include "renderer/kernel/lighting/drt/drt.h"
#include "renderer/kernel/lighting/ilightingengine.h"
#include "renderer/kernel/lighting/lightsampler.h"
#include "renderer/kernel/lighting/pathtracing/pathtracing.h"
#include "renderer/kernel/rendering/debug/blanktilerenderer.h"
#include "renderer/kernel/rendering/debug/debugtilerenderer.h"
#include "renderer/kernel/rendering/generic/genericframerenderer.h"
#include "renderer/kernel/rendering/generic/genericsamplegenerator.h"
#include "renderer/kernel/rendering/generic/genericsamplerenderer.h"
#include "renderer/kernel/rendering/generic/generictilerenderer.h"
#include "renderer/kernel/rendering/lighttracing/lighttracingsamplegenerator.h"
#include "renderer/kernel/rendering/progressive/progressiveframerenderer.h"
#include "renderer/kernel/rendering/iframerenderer.h"
#include "renderer/kernel/rendering/isamplegenerator.h"
#include "renderer/kernel/rendering/isamplerenderer.h"
#include "renderer/kernel/rendering/itilecallback.h"
#include "renderer/kernel/rendering/itilerenderer.h"
#include "renderer/kernel/shading/shadingcontext.h"
#include "renderer/kernel/shading/shadingengine.h"
#include "renderer/modeling/frame/frame.h"
#include "renderer/modeling/input/inputbinder.h"
#include "renderer/modeling/project/project.h"
#include "renderer/modeling/scene/scene.h"

// appleseed.foundation headers.
#include "foundation/core/exceptions/exception.h"
#include "foundation/core/exceptions/stringexception.h"

// Standard headers.
#include <exception>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// MasterRenderer class implementation.
//

MasterRenderer::MasterRenderer(
    Project&                project,
    const ParamArray&       params,
    IRendererController*    renderer_controller,
    ITileCallbackFactory*   tile_callback_factory)
  : m_project(project)
  , m_params(params)
  , m_renderer_controller(renderer_controller)
  , m_tile_callback_factory(tile_callback_factory)
{
}

ParamArray& MasterRenderer::get_parameters()
{
    return m_params;
}

const ParamArray& MasterRenderer::get_parameters() const
{
    return m_params;
}

void MasterRenderer::render()
{
    try
    {
        do_render();
    }
    catch (const StringException& e)
    {
        m_renderer_controller->on_rendering_abort();
        RENDERER_LOG_ERROR("rendering failed (%s: %s)", e.what(), e.string());
    }
    catch (const Exception& e)
    {
        m_renderer_controller->on_rendering_abort();
        RENDERER_LOG_ERROR("rendering failed (%s)", e.what());
    }
    catch (const bad_alloc&)
    {
        m_renderer_controller->on_rendering_abort();
        RENDERER_LOG_ERROR("rendering failed (ran out of memory)");
    }
    catch (...)
    {
        m_renderer_controller->on_rendering_abort();
        RENDERER_LOG_ERROR("rendering failed (unknown exception)");
    }
}

void MasterRenderer::do_render()
{
    while (true)
    {
        m_renderer_controller->on_rendering_begin();

        const IRendererController::Status status = initialize_and_render_frame_sequence();

        switch (status)
        {
          case IRendererController::TerminateRendering:
            m_renderer_controller->on_rendering_success();
            return;

          case IRendererController::AbortRendering:
            m_renderer_controller->on_rendering_abort();
            return;

          case IRendererController::ReinitializeRendering:
            break;

          assert_otherwise;
        }
    }
}

IRendererController::Status MasterRenderer::initialize_and_render_frame_sequence()
{
    assert(m_project.get_scene());
    assert(m_project.get_frame());

    if (!bind_scene_entities_inputs())
        return IRendererController::AbortRendering;

    m_project.create_aov_images();
    m_project.update_trace_context();

    const Scene& scene = *m_project.get_scene();
    Frame& frame = *m_project.get_frame();

    // Create the light sampler.
    LightSampler light_sampler(scene);

    // Create the shading engine.
    ShadingEngine shading_engine(m_params.child("shading_engine"));

    //
    // Create a lighting engine factory.
    //

    auto_ptr<ILightingEngineFactory> lighting_engine_factory;

    const string lighting_engine_param =
        m_params.get_required<string>("lighting_engine", "pt");

    if (lighting_engine_param == "drt")
    {
        lighting_engine_factory.reset(
            new DRTLightingEngineFactory(
                light_sampler,
                m_params.child("drt")));
    }
    else if (lighting_engine_param == "pt")
    {
        lighting_engine_factory.reset(
            new PTLightingEngineFactory(
                light_sampler,
                m_params.child("pt")));
    }

    //
    // Create a sample renderer factory.
    //

    auto_ptr<ISampleRendererFactory> sample_renderer_factory;

    const string sample_renderer_param =
        m_params.get_required<string>("sample_renderer", "generic");

    if (sample_renderer_param == "generic")
    {
        sample_renderer_factory.reset(
            new GenericSampleRendererFactory(
                scene,
                frame,
                m_project.get_trace_context(),
                lighting_engine_factory.get(),
                shading_engine,
                m_params.child("generic_sample_renderer")));
    }

    //
    // Create a tile renderer factory.
    //

    auto_ptr<ITileRendererFactory> tile_renderer_factory;

    const string tile_renderer_param =
        m_params.get_required<string>("tile_renderer", "generic");

    if (tile_renderer_param == "generic")
    {
        tile_renderer_factory.reset(
            new GenericTileRendererFactory(
                frame,
                sample_renderer_factory.get(),
                m_params.child("generic_tile_renderer")));
    }
    else if (tile_renderer_param == "blank")
    {
        tile_renderer_factory.reset(new BlankTileRendererFactory());
    }
    else if (tile_renderer_param == "debug")
    {
        tile_renderer_factory.reset(new DebugTileRendererFactory());
    }

    //
    // Create a sample generator factory.
    //

    auto_ptr<ISampleGeneratorFactory> sample_generator_factory;

    const string sample_generator_param =
        m_params.get_optional<string>("sample_generator", "generic");

    if (sample_generator_param == "generic")
    {
        sample_generator_factory.reset(
            new GenericSampleGeneratorFactory(
                frame,
                sample_renderer_factory.get()));
    }
    else if (sample_generator_param == "lighttracing")
    {
        sample_generator_factory.reset(
            new LightTracingSampleGeneratorFactory(
                scene,
                frame,
                m_project.get_trace_context(),
                light_sampler,
                m_params.child("lighttracing_sample_generator")));
    }

    //
    // Create a frame renderer.
    //

    auto_release_ptr<IFrameRenderer> frame_renderer;

    const string frame_renderer_param =
        m_params.get_required<string>("frame_renderer", "generic");

    if (frame_renderer_param == "generic")
    {
        frame_renderer.reset(
            GenericFrameRendererFactory::create(
                frame,
                tile_renderer_factory.get(),
                m_tile_callback_factory,
                m_params.child("generic_frame_renderer")));
    }
    else if (frame_renderer_param == "progressive")
    {
        frame_renderer.reset(
            ProgressiveFrameRendererFactory::create(
                m_project,
                sample_generator_factory.get(),
                m_tile_callback_factory,
                m_params.child("progressive_frame_renderer")));
    }

    // Execute the main rendering loop.
    return render_frame_sequence(frame_renderer.get());
}

IRendererController::Status MasterRenderer::render_frame_sequence(IFrameRenderer* frame_renderer)
{
    while (true)
    {
        assert(!frame_renderer->is_rendering());

        m_renderer_controller->on_frame_begin();
        m_project.get_scene()->on_frame_begin(m_project);

        const IRendererController::Status status = render_frame(frame_renderer);
        assert(!frame_renderer->is_rendering());

        m_project.get_scene()->on_frame_end(m_project);
        m_renderer_controller->on_frame_end();

        switch (status)
        {
          case IRendererController::TerminateRendering:
          case IRendererController::AbortRendering:
          case IRendererController::ReinitializeRendering:
            return status;

          case IRendererController::RestartRendering:
            break;

          assert_otherwise;
        }
    }
}

IRendererController::Status MasterRenderer::render_frame(IFrameRenderer* frame_renderer)
{
    frame_renderer->start_rendering();

    while (frame_renderer->is_rendering())
    {
        const IRendererController::Status status = m_renderer_controller->on_progress();

        switch (status)
        {
          case IRendererController::ContinueRendering:
            break;

          case IRendererController::TerminateRendering:
          case IRendererController::AbortRendering:
          case IRendererController::ReinitializeRendering:
            frame_renderer->terminate_rendering();
            return status;

          case IRendererController::RestartRendering:
            frame_renderer->stop_rendering();
            return status;

          assert_otherwise;
        }
    }

    return IRendererController::TerminateRendering;
}

bool MasterRenderer::bind_scene_entities_inputs() const
{
    InputBinder input_binder;
    input_binder.bind(*m_project.get_scene());
    return input_binder.get_error_count() == 0;
}

}   // namespace renderer
<commit_msg>abort rendering with an error message if a rendering component cannot be created.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "masterrenderer.h"

// appleseed.renderer headers.
#include "renderer/kernel/lighting/drt/drt.h"
#include "renderer/kernel/lighting/ilightingengine.h"
#include "renderer/kernel/lighting/lightsampler.h"
#include "renderer/kernel/lighting/pathtracing/pathtracing.h"
#include "renderer/kernel/rendering/debug/blanktilerenderer.h"
#include "renderer/kernel/rendering/debug/debugtilerenderer.h"
#include "renderer/kernel/rendering/generic/genericframerenderer.h"
#include "renderer/kernel/rendering/generic/genericsamplegenerator.h"
#include "renderer/kernel/rendering/generic/genericsamplerenderer.h"
#include "renderer/kernel/rendering/generic/generictilerenderer.h"
#include "renderer/kernel/rendering/lighttracing/lighttracingsamplegenerator.h"
#include "renderer/kernel/rendering/progressive/progressiveframerenderer.h"
#include "renderer/kernel/rendering/iframerenderer.h"
#include "renderer/kernel/rendering/isamplegenerator.h"
#include "renderer/kernel/rendering/isamplerenderer.h"
#include "renderer/kernel/rendering/itilecallback.h"
#include "renderer/kernel/rendering/itilerenderer.h"
#include "renderer/kernel/shading/shadingcontext.h"
#include "renderer/kernel/shading/shadingengine.h"
#include "renderer/modeling/frame/frame.h"
#include "renderer/modeling/input/inputbinder.h"
#include "renderer/modeling/project/project.h"
#include "renderer/modeling/scene/scene.h"

// appleseed.foundation headers.
#include "foundation/core/exceptions/exception.h"
#include "foundation/core/exceptions/stringexception.h"

// Standard headers.
#include <exception>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// MasterRenderer class implementation.
//

MasterRenderer::MasterRenderer(
    Project&                project,
    const ParamArray&       params,
    IRendererController*    renderer_controller,
    ITileCallbackFactory*   tile_callback_factory)
  : m_project(project)
  , m_params(params)
  , m_renderer_controller(renderer_controller)
  , m_tile_callback_factory(tile_callback_factory)
{
}

ParamArray& MasterRenderer::get_parameters()
{
    return m_params;
}

const ParamArray& MasterRenderer::get_parameters() const
{
    return m_params;
}

void MasterRenderer::render()
{
    try
    {
        do_render();
    }
    catch (const StringException& e)
    {
        m_renderer_controller->on_rendering_abort();
        RENDERER_LOG_ERROR("rendering failed (%s: %s)", e.what(), e.string());
    }
    catch (const Exception& e)
    {
        m_renderer_controller->on_rendering_abort();
        RENDERER_LOG_ERROR("rendering failed (%s)", e.what());
    }
    catch (const bad_alloc&)
    {
        m_renderer_controller->on_rendering_abort();
        RENDERER_LOG_ERROR("rendering failed (ran out of memory)");
    }
    catch (...)
    {
        m_renderer_controller->on_rendering_abort();
        RENDERER_LOG_ERROR("rendering failed (unknown exception)");
    }
}

void MasterRenderer::do_render()
{
    while (true)
    {
        m_renderer_controller->on_rendering_begin();

        const IRendererController::Status status = initialize_and_render_frame_sequence();

        switch (status)
        {
          case IRendererController::TerminateRendering:
            m_renderer_controller->on_rendering_success();
            return;

          case IRendererController::AbortRendering:
            m_renderer_controller->on_rendering_abort();
            return;

          case IRendererController::ReinitializeRendering:
            break;

          assert_otherwise;
        }
    }
}

IRendererController::Status MasterRenderer::initialize_and_render_frame_sequence()
{
    assert(m_project.get_scene());
    assert(m_project.get_frame());

    if (!bind_scene_entities_inputs())
        return IRendererController::AbortRendering;

    m_project.create_aov_images();
    m_project.update_trace_context();

    const Scene& scene = *m_project.get_scene();
    Frame& frame = *m_project.get_frame();

    // Create the light sampler.
    LightSampler light_sampler(scene);

    // Create the shading engine.
    ShadingEngine shading_engine(m_params.child("shading_engine"));

    //
    // Create a lighting engine factory.
    //

    auto_ptr<ILightingEngineFactory> lighting_engine_factory;

    const string lighting_engine_param =
        m_params.get_required<string>("lighting_engine", "pt");

    if (lighting_engine_param == "drt")
    {
        lighting_engine_factory.reset(
            new DRTLightingEngineFactory(
                light_sampler,
                m_params.child("drt")));
    }
    else if (lighting_engine_param == "pt")
    {
        lighting_engine_factory.reset(
            new PTLightingEngineFactory(
                light_sampler,
                m_params.child("pt")));
    }
    else
    {
        RENDERER_LOG_ERROR(
            "invalid value for \"lighting_engine\" parameter: \"%s\"",
            lighting_engine_param.c_str());
        return IRendererController::AbortRendering;
    }

    //
    // Create a sample renderer factory.
    //

    auto_ptr<ISampleRendererFactory> sample_renderer_factory;

    const string sample_renderer_param =
        m_params.get_required<string>("sample_renderer", "generic");

    if (sample_renderer_param == "generic")
    {
        sample_renderer_factory.reset(
            new GenericSampleRendererFactory(
                scene,
                frame,
                m_project.get_trace_context(),
                lighting_engine_factory.get(),
                shading_engine,
                m_params.child("generic_sample_renderer")));
    }
    else
    {
        RENDERER_LOG_ERROR(
            "invalid value for \"sample_renderer\" parameter: \"%s\"",
            sample_renderer_param.c_str());
        return IRendererController::AbortRendering;
    }

    //
    // Create a tile renderer factory.
    //

    auto_ptr<ITileRendererFactory> tile_renderer_factory;

    const string tile_renderer_param =
        m_params.get_required<string>("tile_renderer", "generic");

    if (tile_renderer_param == "generic")
    {
        tile_renderer_factory.reset(
            new GenericTileRendererFactory(
                frame,
                sample_renderer_factory.get(),
                m_params.child("generic_tile_renderer")));
    }
    else if (tile_renderer_param == "blank")
    {
        tile_renderer_factory.reset(new BlankTileRendererFactory());
    }
    else if (tile_renderer_param == "debug")
    {
        tile_renderer_factory.reset(new DebugTileRendererFactory());
    }
    else
    {
        RENDERER_LOG_ERROR(
            "invalid value for \"tile_renderer\" parameter: \"%s\"",
            tile_renderer_param.c_str());
        return IRendererController::AbortRendering;
    }

    //
    // Create a sample generator factory.
    //

    auto_ptr<ISampleGeneratorFactory> sample_generator_factory;

    const string sample_generator_param =
        m_params.get_optional<string>("sample_generator", "generic");

    if (sample_generator_param == "generic")
    {
        sample_generator_factory.reset(
            new GenericSampleGeneratorFactory(
                frame,
                sample_renderer_factory.get()));
    }
    else if (sample_generator_param == "lighttracing")
    {
        sample_generator_factory.reset(
            new LightTracingSampleGeneratorFactory(
                scene,
                frame,
                m_project.get_trace_context(),
                light_sampler,
                m_params.child("lighttracing_sample_generator")));
    }
    else
    {
        RENDERER_LOG_ERROR(
            "invalid value for \"sample_generator\" parameter: \"%s\"",
            sample_generator_param.c_str());
        return IRendererController::AbortRendering;
    }

    //
    // Create a frame renderer.
    //

    auto_release_ptr<IFrameRenderer> frame_renderer;

    const string frame_renderer_param =
        m_params.get_required<string>("frame_renderer", "generic");

    if (frame_renderer_param == "generic")
    {
        frame_renderer.reset(
            GenericFrameRendererFactory::create(
                frame,
                tile_renderer_factory.get(),
                m_tile_callback_factory,
                m_params.child("generic_frame_renderer")));
    }
    else if (frame_renderer_param == "progressive")
    {
        frame_renderer.reset(
            ProgressiveFrameRendererFactory::create(
                m_project,
                sample_generator_factory.get(),
                m_tile_callback_factory,
                m_params.child("progressive_frame_renderer")));
    }
    else
    {
        RENDERER_LOG_ERROR(
            "invalid value for \"frame_renderer\" parameter: \"%s\"",
            frame_renderer_param.c_str());
        return IRendererController::AbortRendering;
    }

    // Execute the main rendering loop.
    return render_frame_sequence(frame_renderer.get());
}

IRendererController::Status MasterRenderer::render_frame_sequence(IFrameRenderer* frame_renderer)
{
    while (true)
    {
        assert(!frame_renderer->is_rendering());

        m_renderer_controller->on_frame_begin();
        m_project.get_scene()->on_frame_begin(m_project);

        const IRendererController::Status status = render_frame(frame_renderer);
        assert(!frame_renderer->is_rendering());

        m_project.get_scene()->on_frame_end(m_project);
        m_renderer_controller->on_frame_end();

        switch (status)
        {
          case IRendererController::TerminateRendering:
          case IRendererController::AbortRendering:
          case IRendererController::ReinitializeRendering:
            return status;

          case IRendererController::RestartRendering:
            break;

          assert_otherwise;
        }
    }
}

IRendererController::Status MasterRenderer::render_frame(IFrameRenderer* frame_renderer)
{
    frame_renderer->start_rendering();

    while (frame_renderer->is_rendering())
    {
        const IRendererController::Status status = m_renderer_controller->on_progress();

        switch (status)
        {
          case IRendererController::ContinueRendering:
            break;

          case IRendererController::TerminateRendering:
          case IRendererController::AbortRendering:
          case IRendererController::ReinitializeRendering:
            frame_renderer->terminate_rendering();
            return status;

          case IRendererController::RestartRendering:
            frame_renderer->stop_rendering();
            return status;

          assert_otherwise;
        }
    }

    return IRendererController::TerminateRendering;
}

bool MasterRenderer::bind_scene_entities_inputs() const
{
    InputBinder input_binder;
    input_binder.bind(*m_project.get_scene());
    return input_binder.get_error_count() == 0;
}

}   // namespace renderer
<|endoftext|>


<commit_before>#include "tests.h"
#include <cstdlib>
#include <chrono>
#include <memory>
#include <algorithm>
#include <unordered_set>
#ifdef _WIN32
    #define WIN32_LEAN_AND_MEAN
    #include <Windows.h>
    #include <conio.h> // _kbhit
#else
    #include <unistd.h>
    #include <termios.h>
#endif

namespace rpp
{
    int test::asserts_failed;

    // there are initialization order issues with this global variable, so wrap it to guarantee initialization order
    static vector<test*>& all_tests() noexcept
    {
        static vector<test*> tests;
        return tests;
    }

    test::test(strview name, bool autorun) : name(name), auto_run(autorun)
    {
        all_tests().push_back(this);
    }

    test::~test()
    {
        if (!all_tests().empty())
        {
            auto it = find(all_tests().begin(), all_tests().end(), this);
            if (it != all_tests().end())
                all_tests().erase(it);
        }
    }

    void test::consolef(ConsoleColor color, const char* fmt, ...)
    {
        va_list ap;
        va_start(ap, fmt);
    #if _WIN32
        static HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
        static const int colormap[] = {
            FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // Default
            FOREGROUND_GREEN, // dark green
            FOREGROUND_RED | FOREGROUND_GREEN, // dark yellow
            FOREGROUND_RED, // dark red
        };
        if (color != Default) SetConsoleTextAttribute(console, colormap[color]);
        if (color == Red) vfprintf(stderr, fmt, ap);
        else              vfprintf(stdout, fmt, ap);
        if (color != Default) SetConsoleTextAttribute(console, colormap[Default]);
    #else // @todo Proper Linux & OSX implementations
        if (color == Red) vfprintf(stderr, fmt, ap);
        else              vfprintf(stdout, fmt, ap);
    #endif
    }

    void test::assert_failed(const char* file, int line, const char* fmt, ...)
    {
        const char* filename = file + int(strview{ file }.rfindany("\\/") - file) + 1;

        char message[8192];
        va_list ap; va_start(ap, fmt);
        vsnprintf(message, 8192, fmt, ap);

        ++asserts_failed;
        consolef(Red, "FAILURE %12s:%d    %s\n", filename, line, message);
    }

    void test::run_test(strview methodFilter)
    {
        char title[256];
        int len = methodFilter
            ? snprintf(title, sizeof(title), "--------  running '%s.%.*s'  --------", name.str, methodFilter.len, methodFilter.str)
            : snprintf(title, sizeof(title), "--------  running '%s'  --------", name.str);

        consolef(Yellow, "%s\n", title);
        run();

        auto funcs = test_funcs.data();
        auto count = test_funcs.size();
        if (methodFilter)
        {
            for (auto i = 0u; i < count; ++i) {
                if (funcs[i].name.find(methodFilter)) {
                    (funcs[i].lambda.*funcs[i].func)();
                }
            }
        }
        else
        {
            for (auto i = 0u; i < count; ++i) {
                (funcs[i].lambda.*funcs[i].func)();
            }
        }
        consolef(Yellow, "%s\n\n", (char*)memset(title, '-', len)); // "-------------"
    }

    void test::sleep(int millis)
    {
        #ifdef _WIN32
            Sleep(millis);
        #else
            usleep(millis * 1000);
        #endif
    }

#ifndef _WIN32 // Linux:
    static int _kbhit()
    {
        termios oldSettings; tcgetattr(STDIN_FILENO, &oldSettings);
        termios newSettings = oldSettings;
        newSettings.c_cc[VTIME] = 0;
        newSettings.c_cc[VMIN] = 0;
        newSettings.c_iflag &= ~(IXOFF);
        newSettings.c_lflag &= ~(ECHO | ICANON);
        tcsetattr(STDIN_FILENO, TCSANOW, &newSettings);

        char keycodes[16];
        int count = (int)::read(fileno(stdin), (void*)keycodes, 16);

        tcsetattr(STDIN_FILENO, TCSANOW, &oldSettings);

        return count == 0 ? 0 : keycodes[0];
    }
#endif

    static void pause(int millis = -1/*forever*/)
    {
        printf("\nPress any key to continue...");

        using namespace chrono;
        auto start = system_clock::now();
        while (!_kbhit())
        {
            if (millis != -1)
            {
                auto elapsed = duration_cast<milliseconds>(system_clock::now() - start);
                if (elapsed.count() >= millis)
                    break;
            }
            test::sleep(50);
        }
    }

    //TestImpl(test_test)
    //{
    //	Implement(test_test)
    //	{
    //		Assert(1 == 1);
    //	}
    //}
    //Instance;

    int test::run_tests(const char* testNamePattern)
    {
        char empty[1] = "";
        char name[1024]; strncpy(name, testNamePattern, 1024);
        char* argv[2] = { empty, name };
        return run_tests(2, argv);
    }

    int test::run_tests()
    {
        char empty[1] = "";
        char* argv[1] = { empty };
        return run_tests(1, argv);
    }

    static void move_console_window()
    {
        // move console window to the other monitor to make test debugging more seamless
        // if debugger is attached with Visual Studio
    #if _WIN32 && _MSC_VER
        int numMonitors = 0;
        if (IsDebuggerPresent() && (numMonitors = GetSystemMetrics(SM_CMONITORS)) > 1)
        {
            vector<HMONITOR> mon;
            EnumDisplayMonitors(0, 0, [](HMONITOR monitor, HDC, RECT*, LPARAM data) {
                ((vector<HMONITOR>*)data)->push_back(monitor); return 1; }, (LPARAM)&mon);

            RECT consoleRect; GetWindowRect(GetConsoleWindow(), &consoleRect);
            HMONITOR consoleMon = MonitorFromRect(&consoleRect, MONITOR_DEFAULTTONEAREST);
            HMONITOR otherMon   = consoleMon != mon[0] ? mon[0] : mon[1];

            MONITORINFO consoleMI = { sizeof(MONITORINFO) };
            MONITORINFO otherMI   = { sizeof(MONITORINFO) };
            GetMonitorInfo(consoleMon, &consoleMI);
            GetMonitorInfo(otherMon, &otherMI);

            int x = consoleMI.rcMonitor.left > otherMI.rcMonitor.left // moveLeft ?
                ? otherMI.rcMonitor.right - (consoleRect.left - consoleMI.rcMonitor.left) - (consoleRect.right-consoleRect.left)
                : otherMI.rcMonitor.left  + (consoleRect.left - consoleMI.rcMonitor.left);
            int y = otherMI.rcMonitor.top + (consoleRect.top - consoleMI.rcMonitor.top);
            SetWindowPos(GetConsoleWindow(), 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
        }
    #endif
    }

    int test::run_tests(int argc, char* argv[])
    {
        move_console_window();

        for (test* t : all_tests()) { // set the defaults
            if (!t->auto_run) t->test_enabled = false;
        }

        int numTest = 0;
        if (argc > 1)
        {
            // if arg is provided, we assume they are:
            // test_testname or testname or -test_testname or -testname
            // OR to run a specific test:  testname.specifictest
            unordered_set<strview> enabled, disabled;

            for (int iarg = 1; iarg < argc; ++iarg)
            {
                rpp::strview arg = argv[iarg];
                rpp::strview testName = arg.next('.');
                rpp::strview specific = arg.next('.');

                const bool enableTest = testName[0] != '-';
                if (!enableTest) testName.chomp_first();

                const bool exactMatch = testName.starts_with("test_");
                if (exactMatch) consolef(Yellow, "Filtering exact tests '%s'\n\n", argv[iarg]);
                else            consolef(Yellow, "Filtering substr tests '%s'\n\n", argv[iarg]);

                for (test* t : all_tests())
                {
                    if (( exactMatch && t->name == testName) ||
                        (!exactMatch && t->name.find(testName)))
                    {
                        t->test_specific = specific;
                        if (enableTest) enabled.insert(t->name);
                        else            disabled.insert(t->name);
                        break;
                    }
                }
            }

            if (disabled.size())
            {
                for (test* t : all_tests()) {
                    if (t->auto_run) { // only consider disabling auto_run tests
                        t->test_enabled = disabled.find(t->name) == disabled.end();
                        if (!t->test_enabled)
                            consolef(ConsoleColor::Red, "Disabled test %s", t->name.to_cstr());
                    }
                }
            }
            else if (enabled.size())
            {
                for (test* t : all_tests()) { // enable whatever was requested
                    t->test_enabled = enabled.find(t->name) != enabled.end();
                    if (t->test_enabled)
                        consolef(ConsoleColor::Green, "Enabled test %s", t->name.to_cstr());
                }
            }
        }
        else
        {
            consolef(Green, "Running all auto-run tests\n");
        }

        // run all the marked tests
        for (test* t : all_tests()) {
            if (t->test_enabled) {
                t->run_test();
                ++numTest;
            }
        }

        if (test::asserts_failed)
        {
            consolef(Red, "\nWARNING: %d assertions failed!\n", test::asserts_failed);
            pause();
            return -1;
        }

        if (numTest > 0)
            consolef(Green, "\nSUCCESS: All test runs passed!\n");
        else
            consolef(Yellow, "\nNOTE: No tests were run! (out of %d)\n", (int)all_tests().size());
        pause(5000);
        return 0;
    }

} // namespace rpp

#if RPP_TESTS_DEFINE_MAIN
int main(int argc, char* argv[])
{
    return rpp::test::run_tests(argc, argv);
}
#endif
<commit_msg>Added missing newlines in rpp/tests<commit_after>#include "tests.h"
#include <cstdlib>
#include <chrono>
#include <memory>
#include <algorithm>
#include <unordered_set>
#ifdef _WIN32
    #define WIN32_LEAN_AND_MEAN
    #include <Windows.h>
    #include <conio.h> // _kbhit
#else
    #include <unistd.h>
    #include <termios.h>
#endif

namespace rpp
{
    int test::asserts_failed;

    // there are initialization order issues with this global variable, so wrap it to guarantee initialization order
    static vector<test*>& all_tests() noexcept
    {
        static vector<test*> tests;
        return tests;
    }

    test::test(strview name, bool autorun) : name(name), auto_run(autorun)
    {
        all_tests().push_back(this);
    }

    test::~test()
    {
        if (!all_tests().empty())
        {
            auto it = find(all_tests().begin(), all_tests().end(), this);
            if (it != all_tests().end())
                all_tests().erase(it);
        }
    }

    void test::consolef(ConsoleColor color, const char* fmt, ...)
    {
        va_list ap;
        va_start(ap, fmt);
    #if _WIN32
        static HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
        static const int colormap[] = {
            FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // Default
            FOREGROUND_GREEN, // dark green
            FOREGROUND_RED | FOREGROUND_GREEN, // dark yellow
            FOREGROUND_RED, // dark red
        };
        if (color != Default) SetConsoleTextAttribute(console, colormap[color]);
        if (color == Red) vfprintf(stderr, fmt, ap);
        else              vfprintf(stdout, fmt, ap);
        if (color != Default) SetConsoleTextAttribute(console, colormap[Default]);
    #else // @todo Proper Linux & OSX implementations
        if (color == Red) vfprintf(stderr, fmt, ap);
        else              vfprintf(stdout, fmt, ap);
    #endif
    }

    void test::assert_failed(const char* file, int line, const char* fmt, ...)
    {
        const char* filename = file + int(strview{ file }.rfindany("\\/") - file) + 1;

        char message[8192];
        va_list ap; va_start(ap, fmt);
        vsnprintf(message, 8192, fmt, ap);

        ++asserts_failed;
        consolef(Red, "FAILURE %12s:%d    %s\n", filename, line, message);
    }

    void test::run_test(strview methodFilter)
    {
        char title[256];
        int len = methodFilter
            ? snprintf(title, sizeof(title), "--------  running '%s.%.*s'  --------", name.str, methodFilter.len, methodFilter.str)
            : snprintf(title, sizeof(title), "--------  running '%s'  --------", name.str);

        consolef(Yellow, "%s\n", title);
        run();

        auto funcs = test_funcs.data();
        auto count = test_funcs.size();
        if (methodFilter)
        {
            for (auto i = 0u; i < count; ++i) {
                if (funcs[i].name.find(methodFilter)) {
                    (funcs[i].lambda.*funcs[i].func)();
                }
            }
        }
        else
        {
            for (auto i = 0u; i < count; ++i) {
                (funcs[i].lambda.*funcs[i].func)();
            }
        }
        consolef(Yellow, "%s\n\n", (char*)memset(title, '-', len)); // "-------------"
    }

    void test::sleep(int millis)
    {
        #ifdef _WIN32
            Sleep(millis);
        #else
            usleep(millis * 1000);
        #endif
    }

#ifndef _WIN32 // Linux:
    static int _kbhit()
    {
        termios oldSettings; tcgetattr(STDIN_FILENO, &oldSettings);
        termios newSettings = oldSettings;
        newSettings.c_cc[VTIME] = 0;
        newSettings.c_cc[VMIN] = 0;
        newSettings.c_iflag &= ~(IXOFF);
        newSettings.c_lflag &= ~(ECHO | ICANON);
        tcsetattr(STDIN_FILENO, TCSANOW, &newSettings);

        char keycodes[16];
        int count = (int)::read(fileno(stdin), (void*)keycodes, 16);

        tcsetattr(STDIN_FILENO, TCSANOW, &oldSettings);

        return count == 0 ? 0 : keycodes[0];
    }
#endif

    static void pause(int millis = -1/*forever*/)
    {
        printf("\nPress any key to continue...");

        using namespace chrono;
        auto start = system_clock::now();
        while (!_kbhit())
        {
            if (millis != -1)
            {
                auto elapsed = duration_cast<milliseconds>(system_clock::now() - start);
                if (elapsed.count() >= millis)
                    break;
            }
            test::sleep(50);
        }
    }

    //TestImpl(test_test)
    //{
    //	Implement(test_test)
    //	{
    //		Assert(1 == 1);
    //	}
    //}
    //Instance;

    int test::run_tests(const char* testNamePattern)
    {
        char empty[1] = "";
        char name[1024]; strncpy(name, testNamePattern, 1024);
        char* argv[2] = { empty, name };
        return run_tests(2, argv);
    }

    int test::run_tests()
    {
        char empty[1] = "";
        char* argv[1] = { empty };
        return run_tests(1, argv);
    }

    static void move_console_window()
    {
        // move console window to the other monitor to make test debugging more seamless
        // if debugger is attached with Visual Studio
    #if _WIN32 && _MSC_VER
        int numMonitors = 0;
        if (IsDebuggerPresent() && (numMonitors = GetSystemMetrics(SM_CMONITORS)) > 1)
        {
            vector<HMONITOR> mon;
            EnumDisplayMonitors(0, 0, [](HMONITOR monitor, HDC, RECT*, LPARAM data) {
                ((vector<HMONITOR>*)data)->push_back(monitor); return 1; }, (LPARAM)&mon);

            RECT consoleRect; GetWindowRect(GetConsoleWindow(), &consoleRect);
            HMONITOR consoleMon = MonitorFromRect(&consoleRect, MONITOR_DEFAULTTONEAREST);
            HMONITOR otherMon   = consoleMon != mon[0] ? mon[0] : mon[1];

            MONITORINFO consoleMI = { sizeof(MONITORINFO) };
            MONITORINFO otherMI   = { sizeof(MONITORINFO) };
            GetMonitorInfo(consoleMon, &consoleMI);
            GetMonitorInfo(otherMon, &otherMI);

            int x = consoleMI.rcMonitor.left > otherMI.rcMonitor.left // moveLeft ?
                ? otherMI.rcMonitor.right - (consoleRect.left - consoleMI.rcMonitor.left) - (consoleRect.right-consoleRect.left)
                : otherMI.rcMonitor.left  + (consoleRect.left - consoleMI.rcMonitor.left);
            int y = otherMI.rcMonitor.top + (consoleRect.top - consoleMI.rcMonitor.top);
            SetWindowPos(GetConsoleWindow(), 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
        }
    #endif
    }

    int test::run_tests(int argc, char* argv[])
    {
        move_console_window();

        for (test* t : all_tests()) { // set the defaults
            if (!t->auto_run) t->test_enabled = false;
        }

        int numTest = 0;
        if (argc > 1)
        {
            // if arg is provided, we assume they are:
            // test_testname or testname or -test_testname or -testname
            // OR to run a specific test:  testname.specifictest
            unordered_set<strview> enabled, disabled;

            for (int iarg = 1; iarg < argc; ++iarg)
            {
                rpp::strview arg = argv[iarg];
                rpp::strview testName = arg.next('.');
                rpp::strview specific = arg.next('.');

                const bool enableTest = testName[0] != '-';
                if (!enableTest) testName.chomp_first();

                const bool exactMatch = testName.starts_with("test_");
                if (exactMatch) consolef(Yellow, "Filtering exact tests '%s'\n\n", argv[iarg]);
                else            consolef(Yellow, "Filtering substr tests '%s'\n\n", argv[iarg]);

                for (test* t : all_tests())
                {
                    if (( exactMatch && t->name == testName) ||
                        (!exactMatch && t->name.find(testName)))
                    {
                        t->test_specific = specific;
                        if (enableTest) enabled.insert(t->name);
                        else            disabled.insert(t->name);
                        break;
                    }
                }
            }

            if (disabled.size())
            {
                for (test* t : all_tests()) {
                    if (t->auto_run) { // only consider disabling auto_run tests
                        t->test_enabled = disabled.find(t->name) == disabled.end();
                        if (!t->test_enabled)
                            consolef(ConsoleColor::Red, "Disabled test %s\n", t->name.to_cstr());
                    }
                }
            }
            else if (enabled.size())
            {
                for (test* t : all_tests()) { // enable whatever was requested
                    t->test_enabled = enabled.find(t->name) != enabled.end();
                    if (t->test_enabled)
                        consolef(ConsoleColor::Green, "Enabled test %s\n", t->name.to_cstr());
                }
            }
        }
        else
        {
            consolef(Green, "Running all auto-run tests\n");
        }

        // run all the marked tests
        for (test* t : all_tests()) {
            if (t->test_enabled) {
                t->run_test();
                ++numTest;
            }
        }

        if (test::asserts_failed)
        {
            consolef(Red, "\nWARNING: %d assertions failed!\n", test::asserts_failed);
            pause();
            return -1;
        }

        if (numTest > 0)
            consolef(Green, "\nSUCCESS: All test runs passed!\n");
        else
            consolef(Yellow, "\nNOTE: No tests were run! (out of %d)\n", (int)all_tests().size());
        pause(5000);
        return 0;
    }

} // namespace rpp

#if RPP_TESTS_DEFINE_MAIN
int main(int argc, char* argv[])
{
    return rpp::test::run_tests(argc, argv);
}
#endif
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// transaction_manager.cpp
//
// Identification: src/backend/concurrency/optimistic_transaction_manager.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "optimistic_transaction_manager.h"

#include "backend/common/platform.h"
#include "backend/logging/log_manager.h"
#include "backend/logging/records/transaction_record.h"
#include "backend/concurrency/transaction.h"
#include "backend/catalog/manager.h"
#include "backend/common/exception.h"
#include "backend/common/logger.h"
#include "backend/storage/data_table.h"
#include "backend/storage/tile_group.h"
#include "backend/storage/tile_group_header.h"

namespace peloton {
namespace concurrency {

OptimisticTransactionManager &OptimisticTransactionManager::GetInstance() {
  static OptimisticTransactionManager txn_manager;
  return txn_manager;
}

// Visibility check
bool OptimisticTransactionManager::IsVisible(const txn_id_t &tuple_txn_id,
                                             const cid_t &tuple_begin_cid,
                                             const cid_t &tuple_end_cid) {
  if (tuple_txn_id == INVALID_TXN_ID) {
    // the tuple is not available.
    return false;
  }
  bool own = (current_txn->GetTransactionId() == tuple_txn_id);

  // there are exactly two versions that can be owned by a transaction.
  if (own == true) {
    if (tuple_begin_cid == MAX_CID && tuple_end_cid != INVALID_CID) {
      assert(tuple_end_cid == MAX_CID);
      // the only version that is visible is the newly inserted one.
      return true;
    } else {
      // the older version is not visible.
      return false;
    }
  } else {
    bool activated = (current_txn->GetStartCommitId() >= tuple_begin_cid);
    bool invalidated = (current_txn->GetStartCommitId() >= tuple_end_cid);
    if (tuple_txn_id != INITIAL_TXN_ID) {
      // if the tuple is owned by other transactions.
      if (tuple_begin_cid == MAX_CID) {
        // currently, we do not handle cascading abort. so never read an
        // uncommitted version.
        return false;
      } else {
        // the older version may be visible.
        if (activated && !invalidated) {
          return true;
        } else {
          return false;
        }
      }
    } else {
      // if the tuple is not owned by any transaction.
      if (activated && !invalidated) {
        return true;
      } else {
        return false;
      }
    }
  }
}

bool OptimisticTransactionManager::RecordRead(const oid_t &tile_group_id, const oid_t &tuple_id) {
  current_txn->RecordRead(tile_group_id, tuple_id);
  return true;
}

bool OptimisticTransactionManager::RecordWrite(const oid_t &tile_group_id, const oid_t &tuple_id) {
  current_txn->RecordWrite(tile_group_id, tuple_id);
  return true;
}

bool OptimisticTransactionManager::RecordInsert(const oid_t &tile_group_id, const oid_t &tuple_id) {
  current_txn->RecordInsert(tile_group_id, tuple_id);
  return true;
}

bool OptimisticTransactionManager::RecordDelete(const oid_t &tile_group_id, const oid_t &tuple_id) {
  current_txn->RecordDelete(tile_group_id, tuple_id);
  return true;
}

void OptimisticTransactionManager::CommitTransaction() {
  LOG_INFO("Committing peloton txn : %lu ", current_txn->GetTransactionId());

  auto &manager = catalog::Manager::GetInstance();

  // generate transaction id.
  cid_t end_commit_id = GetNextCommitId();

  // validate read set.
  auto read_tuples = current_txn->GetReadTuples();
  for (auto entry : read_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      if (tile_group_header->GetTransactionId(tuple_slot) ==
          current_txn->GetTransactionId()) {
        // the version is owned by the transaction.
        continue;
      } else {
        if (tile_group_header->GetTransactionId(tuple_slot) == INITIAL_TXN_ID &&
          tile_group_header->GetBeginCommitId(tuple_slot) <= end_commit_id &&
          tile_group_header->GetEndCommitId(tuple_slot) >= end_commit_id) {
          // the version is not locked and still visible.
          continue;
        }
      }
      // otherwise, validation fails. abort transaction.
      AbortTransaction();
      return;
    }
  }
  //////////////////////////////////////////////////////////

  auto written_tuples = current_txn->GetWrittenTuples();
  // install all updates.
  for (auto entry : written_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      // we must guarantee that, at any time point, only one version is visible.
      tile_group_header->SetEndCommitId(tuple_slot, end_commit_id);
      ItemPointer new_version =
          tile_group_header->GetNextItemPointer(tuple_slot);

      auto new_tile_group_header =
          manager.GetTileGroup(new_version.block)->GetHeader();
      new_tile_group_header->SetBeginCommitId(new_version.offset,
                                              end_commit_id);
      new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID);

      COMPILER_MEMORY_FENCE;

      new_tile_group_header->SetTransactionId(new_version.offset,
                                              INITIAL_TXN_ID);
      tile_group_header->UnlockTupleSlot(
          tuple_slot, current_txn->GetTransactionId());

      // Logging
      // {
      //   auto &log_manager = logging::LogManager::GetInstance();
      //   if (log_manager.IsInLoggingMode()) {
      //     auto logger = log_manager.GetBackendLogger();
      //     auto record = logger->GetTupleRecord(
      //       LOGRECORD_TYPE_TUPLE_UPDATE, transaction_->GetTransactionId(),
      //       target_table_->GetOid(), location, old_location, new_tuple);

      //     logger->Log(record);
      //   }
      // }
    }
  }

  // commit insert set.
  auto inserted_tuples = current_txn->GetInsertedTuples();
  for (auto entry : inserted_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    for (auto tuple_slot : entry.second) {
      tile_group->CommitInsertedTuple(
          tuple_slot, current_txn->GetTransactionId(), end_commit_id);
    }
      // Logging
      // {
      //   auto &log_manager = logging::LogManager::GetInstance();
      //   if (log_manager.IsInLoggingMode()) {
      //     auto logger = log_manager.GetBackendLogger();
      //     auto record = logger->GetTupleRecord(
      //       LOGRECORD_TYPE_TUPLE_UPDATE, transaction_->GetTransactionId(),
      //       target_table_->GetOid(), location, old_location, new_tuple);

      //     logger->Log(record);
      //   }
      // }
  }

  // commit delete set.
  auto deleted_tuples = current_txn->GetDeletedTuples();
  for (auto entry : deleted_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      // we must guarantee that, at any time point, only one version is visible.

      tile_group_header->SetEndCommitId(tuple_slot, end_commit_id);
      ItemPointer new_version =
          tile_group_header->GetNextItemPointer(tuple_slot);

      auto new_tile_group_header =
          manager.GetTileGroup(new_version.block)->GetHeader();
      new_tile_group_header->SetBeginCommitId(new_version.offset,
                                              end_commit_id);
      new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID);

      COMPILER_MEMORY_FENCE;

      new_tile_group_header->SetTransactionId(new_version.offset,
                                              INVALID_TXN_ID);
      tile_group_header->UnlockTupleSlot(
          tuple_slot, current_txn->GetTransactionId());

      // Logging
      // {
      //   auto &log_manager = logging::LogManager::GetInstance();
      //   if (log_manager.IsInLoggingMode()) {
      //     auto logger = log_manager.GetBackendLogger();
      //     auto record = logger->GetTupleRecord(
      //       LOGRECORD_TYPE_TUPLE_UPDATE, transaction_->GetTransactionId(),
      //       target_table_->GetOid(), location, old_location, new_tuple);

      //     logger->Log(record);
      //   }
      // }
    }
  }
  delete current_txn;
  current_txn = nullptr;
}

void OptimisticTransactionManager::AbortTransaction() {
  LOG_INFO("Aborting peloton txn : %lu ", current_txn->GetTransactionId());
  auto &manager = catalog::Manager::GetInstance();
  auto written_tuples = current_txn->GetWrittenTuples();

  // recover write set.
  for (auto entry : written_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      tile_group_header->UnlockTupleSlot(
          tuple_slot, current_txn->GetTransactionId());
      tile_group_header->SetEndCommitId(tuple_slot, MAX_CID);
      ItemPointer new_version =
          tile_group_header->GetNextItemPointer(tuple_slot);
      auto new_tile_group_header =
          manager.GetTileGroup(new_version.block)->GetHeader();
      new_tile_group_header->SetTransactionId(new_version.offset,
                                              INVALID_TXN_ID);
      new_tile_group_header->SetBeginCommitId(new_version.offset, MAX_CID);
      new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID);
    }
  }

  // recover delete set.
  auto deleted_tuples = current_txn->GetDeletedTuples();
  for (auto entry : deleted_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      tile_group_header->UnlockTupleSlot(
          tuple_slot, current_txn->GetTransactionId());
      tile_group_header->SetEndCommitId(tuple_slot, MAX_CID);
      ItemPointer new_version =
          tile_group_header->GetNextItemPointer(tuple_slot);
      auto new_tile_group_header =
          manager.GetTileGroup(new_version.block)->GetHeader();
      new_tile_group_header->SetTransactionId(new_version.offset,
                                              INVALID_TXN_ID);
      new_tile_group_header->SetBeginCommitId(new_version.offset, MAX_CID);
      new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID);
    }
  }

  delete current_txn;
  current_txn = nullptr;
}

}  // End storage namespace
}  // End peloton namespace<commit_msg>moved logging to mvcc commit<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// transaction_manager.cpp
//
// Identification: src/backend/concurrency/optimistic_transaction_manager.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "optimistic_transaction_manager.h"

#include "backend/common/platform.h"
#include "backend/logging/log_record.h"
#include "backend/logging/log_manager.h"
#include "backend/logging/records/transaction_record.h"
#include "backend/concurrency/transaction.h"
#include "backend/catalog/manager.h"
#include "backend/common/exception.h"
#include "backend/common/logger.h"
#include "backend/storage/data_table.h"
#include "backend/storage/tile.h"
#include "backend/storage/tile_group.h"
#include "backend/storage/tile_group_header.h"

namespace peloton {
namespace concurrency {

OptimisticTransactionManager &OptimisticTransactionManager::GetInstance() {
  static OptimisticTransactionManager txn_manager;
  return txn_manager;
}

// Visibility check
bool OptimisticTransactionManager::IsVisible(const txn_id_t &tuple_txn_id,
                                             const cid_t &tuple_begin_cid,
                                             const cid_t &tuple_end_cid) {
  if (tuple_txn_id == INVALID_TXN_ID) {
    // the tuple is not available.
    return false;
  }
  bool own = (current_txn->GetTransactionId() == tuple_txn_id);

  // there are exactly two versions that can be owned by a transaction.
  if (own == true) {
    if (tuple_begin_cid == MAX_CID && tuple_end_cid != INVALID_CID) {
      assert(tuple_end_cid == MAX_CID);
      // the only version that is visible is the newly inserted one.
      return true;
    } else {
      // the older version is not visible.
      return false;
    }
  } else {
    bool activated = (current_txn->GetStartCommitId() >= tuple_begin_cid);
    bool invalidated = (current_txn->GetStartCommitId() >= tuple_end_cid);
    if (tuple_txn_id != INITIAL_TXN_ID) {
      // if the tuple is owned by other transactions.
      if (tuple_begin_cid == MAX_CID) {
        // currently, we do not handle cascading abort. so never read an
        // uncommitted version.
        return false;
      } else {
        // the older version may be visible.
        if (activated && !invalidated) {
          return true;
        } else {
          return false;
        }
      }
    } else {
      // if the tuple is not owned by any transaction.
      if (activated && !invalidated) {
        return true;
      } else {
        return false;
      }
    }
  }
}

bool OptimisticTransactionManager::RecordRead(const oid_t &tile_group_id, const oid_t &tuple_id) {
  current_txn->RecordRead(tile_group_id, tuple_id);
  return true;
}

bool OptimisticTransactionManager::RecordWrite(const oid_t &tile_group_id, const oid_t &tuple_id) {
  current_txn->RecordWrite(tile_group_id, tuple_id);
  return true;
}

bool OptimisticTransactionManager::RecordInsert(const oid_t &tile_group_id, const oid_t &tuple_id) {
  current_txn->RecordInsert(tile_group_id, tuple_id);
  return true;
}

bool OptimisticTransactionManager::RecordDelete(const oid_t &tile_group_id, const oid_t &tuple_id) {
  current_txn->RecordDelete(tile_group_id, tuple_id);
  return true;
}

void OptimisticTransactionManager::CommitTransaction() {
  LOG_INFO("Committing peloton txn : %lu ", current_txn->GetTransactionId());

  auto &manager = catalog::Manager::GetInstance();

  // generate transaction id.
  cid_t end_commit_id = GetNextCommitId();

  // validate read set.
  auto read_tuples = current_txn->GetReadTuples();
  for (auto entry : read_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      if (tile_group_header->GetTransactionId(tuple_slot) ==
          current_txn->GetTransactionId()) {
        // the version is owned by the transaction.
        continue;
      } else {
        if (tile_group_header->GetTransactionId(tuple_slot) == INITIAL_TXN_ID &&
          tile_group_header->GetBeginCommitId(tuple_slot) <= end_commit_id &&
          tile_group_header->GetEndCommitId(tuple_slot) >= end_commit_id) {
          // the version is not locked and still visible.
          continue;
        }
      }
      // otherwise, validation fails. abort transaction.
      AbortTransaction();
      return;
    }
  }
  //////////////////////////////////////////////////////////

  auto written_tuples = current_txn->GetWrittenTuples();
  auto &log_manager = logging::LogManager::GetInstance();
  if (log_manager.IsInLoggingMode()) {
	   auto logger = log_manager.GetBackendLogger();
	   auto record = new logging::TransactionRecord(LOGRECORD_TYPE_TRANSACTION_BEGIN, end_commit_id);
	   logger->Log(record);
	 }
  // install all updates.
  for (auto entry : written_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      // we must guarantee that, at any time point, only one version is visible.
      tile_group_header->SetEndCommitId(tuple_slot, end_commit_id);
      ItemPointer new_version =
          tile_group_header->GetNextItemPointer(tuple_slot);

      auto new_tile_group_header =
          manager.GetTileGroup(new_version.block)->GetHeader();
      new_tile_group_header->SetBeginCommitId(new_version.offset,
                                              end_commit_id);
      new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID);

      COMPILER_MEMORY_FENCE;

      new_tile_group_header->SetTransactionId(new_version.offset,
                                              INITIAL_TXN_ID);
      tile_group_header->UnlockTupleSlot(
          tuple_slot, current_txn->GetTransactionId());

		 if (log_manager.IsInLoggingMode()) {
		   auto logger = log_manager.GetBackendLogger();
		   ItemPointer old_version(tile_group_id, tuple_slot);
		   auto new_tuple_tile_group = manager.GetTileGroup(new_version.block);
		   // TODO assumes in row store for now
		   auto new_tuple = new_tuple_tile_group->GetTile(0)->GetTupleLocation(new_version.offset);
		   auto record = logger->GetTupleRecord(
			 LOGRECORD_TYPE_TUPLE_UPDATE, end_commit_id,
			 tile_group->GetTableId(), new_version, old_version, new_tuple);
		   logger->Log(record);
		 }
    }
  }

  // commit insert set.
  auto inserted_tuples = current_txn->GetInsertedTuples();
  for (auto entry : inserted_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    for (auto tuple_slot : entry.second) {
      tile_group->CommitInsertedTuple(
          tuple_slot, current_txn->GetTransactionId(), end_commit_id);
      if (log_manager.IsInLoggingMode()) {
       auto logger = log_manager.GetBackendLogger();
	   auto new_tuple_tile_group = manager.GetTileGroup(tile_group_id);
	   // TODO assumes in row store for now
	   ItemPointer new_version(tile_group_id, tuple_slot);
	   auto new_tuple = new_tuple_tile_group->GetTile(0)->GetTupleLocation(tuple_slot);
	   auto record = logger->GetTupleRecord(
		 LOGRECORD_TYPE_TUPLE_INSERT, end_commit_id,
		 tile_group->GetTableId(), new_version, INVALID_ITEMPOINTER, new_tuple);
	     logger->Log(record);
	 }
    }
  }

  // commit delete set.
  auto deleted_tuples = current_txn->GetDeletedTuples();
  for (auto entry : deleted_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      // we must guarantee that, at any time point, only one version is visible.

      tile_group_header->SetEndCommitId(tuple_slot, end_commit_id);
      ItemPointer new_version =
          tile_group_header->GetNextItemPointer(tuple_slot);

      auto new_tile_group_header =
          manager.GetTileGroup(new_version.block)->GetHeader();
      new_tile_group_header->SetBeginCommitId(new_version.offset,
                                              end_commit_id);
      new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID);

      COMPILER_MEMORY_FENCE;

      new_tile_group_header->SetTransactionId(new_version.offset,
                                              INVALID_TXN_ID);
      tile_group_header->UnlockTupleSlot(
          tuple_slot, current_txn->GetTransactionId());

      if (log_manager.IsInLoggingMode()) {
             auto logger = log_manager.GetBackendLogger();
           ItemPointer removed(tile_group_id, tuple_slot);
      	   auto record = logger->GetTupleRecord(
      		 LOGRECORD_TYPE_TUPLE_DELETE, end_commit_id,
			 tile_group->GetTableId(), INVALID_ITEMPOINTER, removed);
      	     logger->Log(record);
      	 }
    }
  }
  if (log_manager.IsInLoggingMode()) {
  	   auto logger = log_manager.GetBackendLogger();
  	   auto record = new logging::TransactionRecord(LOGRECORD_TYPE_TRANSACTION_COMMIT, end_commit_id);
  	   logger->Log(record);
  	 }
  delete current_txn;
  current_txn = nullptr;
}

void OptimisticTransactionManager::AbortTransaction() {
  LOG_INFO("Aborting peloton txn : %lu ", current_txn->GetTransactionId());
  auto &manager = catalog::Manager::GetInstance();
  auto written_tuples = current_txn->GetWrittenTuples();

  // recover write set.
  for (auto entry : written_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      tile_group_header->UnlockTupleSlot(
          tuple_slot, current_txn->GetTransactionId());
      tile_group_header->SetEndCommitId(tuple_slot, MAX_CID);
      ItemPointer new_version =
          tile_group_header->GetNextItemPointer(tuple_slot);
      auto new_tile_group_header =
          manager.GetTileGroup(new_version.block)->GetHeader();
      new_tile_group_header->SetTransactionId(new_version.offset,
                                              INVALID_TXN_ID);
      new_tile_group_header->SetBeginCommitId(new_version.offset, MAX_CID);
      new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID);
    }
  }

  // recover delete set.
  auto deleted_tuples = current_txn->GetDeletedTuples();
  for (auto entry : deleted_tuples) {
    oid_t tile_group_id = entry.first;
    auto tile_group = manager.GetTileGroup(tile_group_id);
    auto tile_group_header = tile_group->GetHeader();
    for (auto tuple_slot : entry.second) {
      tile_group_header->UnlockTupleSlot(
          tuple_slot, current_txn->GetTransactionId());
      tile_group_header->SetEndCommitId(tuple_slot, MAX_CID);
      ItemPointer new_version =
          tile_group_header->GetNextItemPointer(tuple_slot);
      auto new_tile_group_header =
          manager.GetTileGroup(new_version.block)->GetHeader();
      new_tile_group_header->SetTransactionId(new_version.offset,
                                              INVALID_TXN_ID);
      new_tile_group_header->SetBeginCommitId(new_version.offset, MAX_CID);
      new_tile_group_header->SetEndCommitId(new_version.offset, MAX_CID);
    }
  }

  delete current_txn;
  current_txn = nullptr;
}

}  // End storage namespace
}  // End peloton namespace
<|endoftext|>


<commit_before>/*
 * Copyright 2015 BrewPi / Elco Jacobs
 *
 * This file is part of BrewPi.
 *
 * BrewPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * BrewPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Pid.h"

Pid::Pid(TempSensorBasic * input,
         ActuatorRange * output,
         SetPoint * setPoint)
{
    setConstants(temp_t(0.0), 0, 0);
    p = 0;
    i = 0;
    d = 0;
    inputError           = 0;
    derivative      = 0;
    integral        = 0;
    failedReadCount = 255; // start at 255, so inputFilter is refreshed at first valid read

    setInputSensor(input);
    setOutputActuator(output);
    setSetPoint(setPoint);

    setInputFilter(0);
    // some filtering necessary due to quantization causing steps in the temperature
    setDerivativeFilter(2);
    actuatorIsNegative = false;
    enabled = true;

//    autotune = false;
//    tuning = false;
//    outputLag = 0;
//    maxDerivative = 0.0;
}

Pid::~Pid(){}

void Pid::setConstants(temp_long_t kp,
                       uint16_t ti,
                       uint16_t td)
{
    Kp = kp;
    Ti = ti;
    Td = td;
}

void Pid::update()
{
    temp_t inputVal;
    bool disable = !enabled;

    if( setPoint->read().isDisabledOrInvalid()){
        disable = true;
    }

    inputVal = inputSensor -> read();
    if (inputVal.isDisabledOrInvalid()){
        // Could not read from input sensor
        if (failedReadCount < 255){    // limit
            failedReadCount++;
        }
        if (failedReadCount > 20){
            disable = true; // disable PID if sensor is lost for more than 20 seconds
        }
    }
    else{
        if (failedReadCount > 60){ // filters are stale, re-initialize them
            inputFilter.init(inputVal);
            derivativeFilter.init(temp_precise_t(0.0));
        }
        failedReadCount = 0;
    }

    if ( disable ){
        return;
    }

    inputFilter.add(inputVal);
    temp_t inputErrorPrevious = inputError;
    inputError = inputFilter.readOutput() - setPoint->read();
    if( (inputError - inputErrorPrevious) > temp_t (0.15) ||
            (inputErrorPrevious - inputError)  > temp_t (0.15)){ // more then 2 bits (0.0625 of the input sensor)
        integral = 0; // reset integral when the error changes significantly, most likely due to setpoint changes
    }

    temp_precise_t delta = inputFilter.readOutput() - inputFilter.readPrevOutput();
    derivativeFilter.add(delta);

    derivative = derivativeFilter.readOutput();

    // calculate PID parts.
    p = Kp * -inputError;
    i = (Ti != 0) ? Kp * (integral/Ti) : temp_long_t(0.0);
    d = -Kp * (derivative * Td);

    temp_long_t pidResult = temp_long_t(p) + temp_long_t(i) + temp_long_t(d);

    // Get output to send to actuator. When actuator is a 'cooler', invert the result
    temp_t      output    = (actuatorIsNegative) ? -pidResult : pidResult;

    outputActuator -> setValue(output);

    // get actual value from actuator
    output = outputActuator->getValue();
    // When actuator is a 'cooler', invert the output again
    output = (actuatorIsNegative) ? -output : output;

    // update integral with anti-windup back calculation
    // pidResult - output is zero when actuator is not saturated
    // Anti windup gain is 10.0
    temp_long_t antiWindup = pidResult - temp_long_t(output);
    antiWindup *= 10.0;

    integral = integral - inputError;

    if(Ti == 0){ // 0 has been chosen to indicate that the integrator is disabled. This also prevents divide by zero.
        integral = 0;
    }
    else if(integral.sign() != (integral-antiWindup).sign()){
        // anti-windup would make integral cross zero
        integral = 0;
    }
    else{
        if(integral.sign() == antiWindup.sign()){ // make sure anti-windup is towards zero
            integral -= antiWindup;
        }
    }

/*
    if(autotune){
        tune(output, previousOutput);
    }
*/

}

void Pid::setFiltering(uint8_t b){
    inputFilter.setFiltering(b);
    derivativeFilter.setFiltering(b);
}

uint8_t Pid::getFiltering(){
    return inputFilter.getFiltering();
}

void Pid::setInputFilter(uint8_t b)
{
    inputFilter.setFiltering(b);
}

void Pid::setDerivativeFilter(uint8_t b)
{
    derivativeFilter.setFiltering(b);
}

bool Pid::setInputSensor(TempSensorBasic * s)
{
    inputSensor = s;
    temp_t t = s -> read();

    if (t.isDisabledOrInvalid()){
        return false;    // could not read from sensor
    }

    inputFilter.init(t);
    derivativeFilter.init(0.0);

    return true;
}

bool Pid::setOutputActuator(ActuatorRange * a)
{
    outputActuator = a;

    return true;
}

/*

// Tune the PID with the Ziegler-Nichols Open-Loop Tuning Method or Process Reaction Method
// This determines the dead time and the reaction rate (max derivative) and calculates the PID parameters from that.
void Pid::tune(temp output, temp previousOutput){
    static uint16_t lagTimer = 0;
    static temp tuningStartTemp = inputFilter.readOutput();

    temp min = outputActuator->min();
    temp max = outputActuator->max();
    temp tuningThreshold = (max >> uint8_t(1)) + (min >> uint8_t(1)); // (min + max) / 2

    if(output == outputActuator->max() && previousOutput < tuningThreshold){
        tuning = true; // only start tuning at a big step to the maximum output
    }
    // cancel tuning when the output is under the tuning threshold before maximum derivative is detected
    if(output < tuningThreshold){
        if(lagTimer > 2*(derivativeFilter.getDelay() + inputFilter.getDelay())){
            tuning = false; // only stop tuning if filters have had time to settle
        }
    }

    // TODO: when this happens, check the filter delay and see if the maximum still has to come

    // Detect when at max derivative, the time until this happens is the lag time
    // Together with the maximum derivative, this is used to determine the PID parameters

    if(tuning){ // only for heating now
        // if the derivative of the input starts falling, we have hit an inflection point
        // Also check that the derivative is positive
        if(derivativeFilter.isFalling() && (derivativeFilter.readOutput() > temp_precise(0))){
            maxDerivative = derivativeFilter.readOutput(); // we're at the peak or past it
            uint16_t filterDelay = derivativeFilter.getDelay();
            uint16_t timeToMaxDerivative = (lagTimer <filterDelay) ? 0  : lagTimer - filterDelay;

            // set PID constants to have no overshoot

            temp_long deadTime = temp_long(timeToMaxDerivative) / temp_long(60.0); // derivative and integral are per minute, scale back here
            temp_long riseTime = (inputFilter.readOutput() - tuningStartTemp)  / derivative;
            if(riseTime < temp_long(0)){
                riseTime = 0.0;
            }
            deadTime = (deadTime > riseTime) ? deadTime - riseTime : temp_long(0); // rise time is not part of the dead time, eliminate it

            outputLag =  uint16_t(deadTime * temp_long(60)); // store outputlag in seconds

            temp_long RL = derivative * deadTime;

            if (RL < temp_long(0.25)){ // prevent divide by zero
                Kp = 160.0;
            }
            else{
                Kp = temp_long(100.0*0.4) / RL; // not aggressive. Quarter decay is 1.2 instead of 0.4. We don't want overshoot
            }

            if(deadTime > temp_long(1)){
                Ki = Kp/(deadTime+deadTime);
            }
            else{
                Ki = Kp*temp_long(0.5);
            }
            Kd = Kp*deadTime*temp_long(0.33);

            tuning = false; // tuning ready
        }
        else{
            if(lagTimer < UINT16_MAX){
                lagTimer++;
            }
        }
    }
    else{
        lagTimer= 0;
        tuningStartTemp = inputFilter.readOutput();
    }
}

*/
<commit_msg>changes to integrator anti-windup so it is symmetric and also works with a fluctuating fridge as setpoint actuator<commit_after>/*
 * Copyright 2015 BrewPi / Elco Jacobs
 *
 * This file is part of BrewPi.
 *
 * BrewPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * BrewPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with BrewPi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Pid.h"

Pid::Pid(TempSensorBasic * input,
         ActuatorRange * output,
         SetPoint * setPoint)
{
    setConstants(temp_t(0.0), 0, 0);
    p = 0;
    i = 0;
    d = 0;
    inputError           = 0;
    derivative      = 0;
    integral        = 0;
    failedReadCount = 255; // start at 255, so inputFilter is refreshed at first valid read

    setInputSensor(input);
    setOutputActuator(output);
    setSetPoint(setPoint);

    setInputFilter(0);
    // some filtering necessary due to quantization causing steps in the temperature
    setDerivativeFilter(2);
    actuatorIsNegative = false;
    enabled = true;

//    autotune = false;
//    tuning = false;
//    outputLag = 0;
//    maxDerivative = 0.0;
}

Pid::~Pid(){}

void Pid::setConstants(temp_long_t kp,
                       uint16_t ti,
                       uint16_t td)
{
    Kp = kp;
    Ti = ti;
    Td = td;
}

void Pid::update()
{
    temp_t inputVal;
    bool disable = !enabled;

    if( setPoint->read().isDisabledOrInvalid()){
        disable = true;
    }

    inputVal = inputSensor -> read();
    if (inputVal.isDisabledOrInvalid()){
        // Could not read from input sensor
        if (failedReadCount < 255){    // limit
            failedReadCount++;
        }
        if (failedReadCount > 20){
            disable = true; // disable PID if sensor is lost for more than 20 seconds
        }
    }
    else{
        if (failedReadCount > 60){ // filters are stale, re-initialize them
            inputFilter.init(inputVal);
            derivativeFilter.init(temp_precise_t(0.0));
        }
        failedReadCount = 0;
    }

    if ( disable ){
        return;
    }

    inputFilter.add(inputVal);
    temp_t inputErrorPrevious = inputError;
    inputError = inputFilter.readOutput() - setPoint->read();
    if( (inputError - inputErrorPrevious) > temp_t (0.15) ||
            (inputErrorPrevious - inputError)  > temp_t (0.15)){ // more then 2 bits (0.0625 of the input sensor)
        integral = 0; // reset integral when the error changes significantly, most likely due to setpoint changes
    }

    temp_precise_t delta = inputFilter.readOutput() - inputFilter.readPrevOutput();
    derivativeFilter.add(delta);

    derivative = derivativeFilter.readOutput();

    // calculate PID parts.
    p = Kp * -inputError;
    i = (Ti != 0) ? Kp * (integral/Ti) : temp_long_t(0.0);
    d = -Kp * (derivative * Td);

    temp_long_t pidResult = temp_long_t(p) + temp_long_t(i) + temp_long_t(d);

    // Get output to send to actuator. When actuator is a 'cooler', invert the result
    temp_t      output    = (actuatorIsNegative) ? -pidResult : pidResult;

    outputActuator -> setValue(output);

    // get actual value from actuator
    output = outputActuator->getValue();
    // When actuator is a 'cooler', invert the output again
    output = (actuatorIsNegative) ? -output : output;

    // update integral with anti-windup back calculation
    // pidResult - output is zero when actuator is not saturated
    // Anti windup gain is 10.0
    temp_long_t antiWindup = pidResult - temp_long_t(output);
    temp_long_t antiWindupGain = 10.0;
    antiWindup *= antiWindupGain;

    integral = integral - inputError;

    if(Ti == 0){ // 0 has been chosen to indicate that the integrator is disabled. This also prevents divide by zero.
        integral = 0;
    }
    else if(integral.sign() != (integral-antiWindup).sign()){
        // anti-windup would make integral cross zero
        integral = 0;
    }
    else{
        integral -= antiWindup/Ti;
    }

/*
    if(autotune){
        tune(output, previousOutput);
    }
*/

}

void Pid::setFiltering(uint8_t b){
    inputFilter.setFiltering(b);
    derivativeFilter.setFiltering(b);
}

uint8_t Pid::getFiltering(){
    return inputFilter.getFiltering();
}

void Pid::setInputFilter(uint8_t b)
{
    inputFilter.setFiltering(b);
}

void Pid::setDerivativeFilter(uint8_t b)
{
    derivativeFilter.setFiltering(b);
}

bool Pid::setInputSensor(TempSensorBasic * s)
{
    inputSensor = s;
    temp_t t = s -> read();

    if (t.isDisabledOrInvalid()){
        return false;    // could not read from sensor
    }

    inputFilter.init(t);
    derivativeFilter.init(0.0);

    return true;
}

bool Pid::setOutputActuator(ActuatorRange * a)
{
    outputActuator = a;

    return true;
}

/*

// Tune the PID with the Ziegler-Nichols Open-Loop Tuning Method or Process Reaction Method
// This determines the dead time and the reaction rate (max derivative) and calculates the PID parameters from that.
void Pid::tune(temp output, temp previousOutput){
    static uint16_t lagTimer = 0;
    static temp tuningStartTemp = inputFilter.readOutput();

    temp min = outputActuator->min();
    temp max = outputActuator->max();
    temp tuningThreshold = (max >> uint8_t(1)) + (min >> uint8_t(1)); // (min + max) / 2

    if(output == outputActuator->max() && previousOutput < tuningThreshold){
        tuning = true; // only start tuning at a big step to the maximum output
    }
    // cancel tuning when the output is under the tuning threshold before maximum derivative is detected
    if(output < tuningThreshold){
        if(lagTimer > 2*(derivativeFilter.getDelay() + inputFilter.getDelay())){
            tuning = false; // only stop tuning if filters have had time to settle
        }
    }

    // TODO: when this happens, check the filter delay and see if the maximum still has to come

    // Detect when at max derivative, the time until this happens is the lag time
    // Together with the maximum derivative, this is used to determine the PID parameters

    if(tuning){ // only for heating now
        // if the derivative of the input starts falling, we have hit an inflection point
        // Also check that the derivative is positive
        if(derivativeFilter.isFalling() && (derivativeFilter.readOutput() > temp_precise(0))){
            maxDerivative = derivativeFilter.readOutput(); // we're at the peak or past it
            uint16_t filterDelay = derivativeFilter.getDelay();
            uint16_t timeToMaxDerivative = (lagTimer <filterDelay) ? 0  : lagTimer - filterDelay;

            // set PID constants to have no overshoot

            temp_long deadTime = temp_long(timeToMaxDerivative) / temp_long(60.0); // derivative and integral are per minute, scale back here
            temp_long riseTime = (inputFilter.readOutput() - tuningStartTemp)  / derivative;
            if(riseTime < temp_long(0)){
                riseTime = 0.0;
            }
            deadTime = (deadTime > riseTime) ? deadTime - riseTime : temp_long(0); // rise time is not part of the dead time, eliminate it

            outputLag =  uint16_t(deadTime * temp_long(60)); // store outputlag in seconds

            temp_long RL = derivative * deadTime;

            if (RL < temp_long(0.25)){ // prevent divide by zero
                Kp = 160.0;
            }
            else{
                Kp = temp_long(100.0*0.4) / RL; // not aggressive. Quarter decay is 1.2 instead of 0.4. We don't want overshoot
            }

            if(deadTime > temp_long(1)){
                Ki = Kp/(deadTime+deadTime);
            }
            else{
                Ki = Kp*temp_long(0.5);
            }
            Kd = Kp*deadTime*temp_long(0.33);

            tuning = false; // tuning ready
        }
        else{
            if(lagTimer < UINT16_MAX){
                lagTimer++;
            }
        }
    }
    else{
        lagTimer= 0;
        tuningStartTemp = inputFilter.readOutput();
    }
}

*/
<|endoftext|>


<commit_before><commit_msg>10646 - What is the Card<commit_after><|endoftext|>


<commit_before>#pragma once
#include <cstdint>
#include <nlohmann/json.hpp>
#include <vector>

#include "DatatypeEnum.hpp"
#include "RawBuffer.hpp"
#include "RawDepthCalculatorConfig.hpp"
#include "RawImgFrame.hpp"

namespace dai {

struct DepthCalculatorDataOut {
    DepthCalculatorConfigData config;
    float depth_avg;
    float depth_x;
    float depth_y;
    float depth_z;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DepthCalculatorDataOut, config, depth_avg);

struct RawDepthCalculatorData : public RawBuffer {
    std::vector<DepthCalculatorDataOut> depth;

    void serialize(std::vector<std::uint8_t>& metadata, DatatypeEnum& datatype) override {
        nlohmann::json j = *this;
        metadata = nlohmann::json::to_msgpack(j);
        datatype = DatatypeEnum::DepthCalculatorData;
    };

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(RawDepthCalculatorData, depth);
};

}  // namespace dai<commit_msg>Add binding for depth data out<commit_after>#pragma once
#include <cstdint>
#include <nlohmann/json.hpp>
#include <vector>

#include "DatatypeEnum.hpp"
#include "RawBuffer.hpp"
#include "RawDepthCalculatorConfig.hpp"
#include "RawImgFrame.hpp"

namespace dai {

struct DepthCalculatorDataOut {
    DepthCalculatorConfigData config;
    float depth_avg;
    float depth_x;
    float depth_y;
    float depth_z;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DepthCalculatorDataOut, config, depth_avg, depth_x, depth_y, depth_z);

struct RawDepthCalculatorData : public RawBuffer {
    std::vector<DepthCalculatorDataOut> depth;

    void serialize(std::vector<std::uint8_t>& metadata, DatatypeEnum& datatype) override {
        nlohmann::json j = *this;
        metadata = nlohmann::json::to_msgpack(j);
        datatype = DatatypeEnum::DepthCalculatorData;
    };

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(RawDepthCalculatorData, depth);
};

}  // namespace dai<|endoftext|>