<commit_before>#include "core/tz.hpp"
#include "core/window.hpp"
#include "core/profiling/zone.hpp"
#include "core/containers/grid_view.hpp"
#include "core/time.hpp"
#include "gl/device.hpp"
#include "gl/renderer.hpp"
#include "gl/resource.hpp"
#include "gl/imported_shaders.hpp"
#include "gl/component.hpp"
#include <random>

#include ImportedShaderHeader(tz_dynamic_triangle_demo, vertex)
#include ImportedShaderHeader(tz_dynamic_triangle_demo, fragment)

struct TriangleVertexData
{
	tz::Vec3 position;
	float pad0;
	tz::Vec2 texcoord;
	float pad1[2];
};

TriangleVertexData get_random_triangle(std::default_random_engine& rand)
{
	std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
	return
	{
		.position = {dist(rand), dist(rand), dist(rand)},
		.texcoord = {std::abs(dist(rand)), std::abs(dist(rand))}
	};
}

int main()
{
	tz::initialise
	({
		.name = "tz_dynamic_triangle_demo"
	});
	{
		tz::gl::Device dev;
		using namespace tz::literals;
		tz::gl::ImageResource img = tz::gl::ImageResource::from_memory
		(
			tz::gl::ImageFormat::RGBA32,
			{2u, 2u},
			{
				0b1111'1111_uc,
				0b0000'0000_uc,
				0b0000'0000_uc,
				0b1111'1111_uc,

				0b1111'1111_uc,
				0b1111'1111_uc,
				0b0000'0000_uc,
				0b1111'1111_uc,

				0b0000'0000_uc,
				0b0000'0000_uc,
				0b1111'1111_uc,
				0b1111'1111_uc,

				0b0000'0000_uc,
				0b1111'1111_uc,
				0b0000'0000_uc,
				0b1111'1111_uc
			},
			tz::gl::ResourceAccess::DynamicVariable
		);

		std::size_t triangle_count = 1;
		tz::gl::BufferResource buf = tz::gl::BufferResource::from_many
		({
			TriangleVertexData{.position = {-0.5f, -0.5f, -1.0f}, .texcoord = {0.0f, 0.0f}},
			TriangleVertexData{.position = {0.0f, 0.5f, -1.0f}, .texcoord = {0.5f, 1.0f}},
			TriangleVertexData{.position = {0.5f, -0.5f, -1.0f}, .texcoord = {1.0f, 0.0f}},
		}, tz::gl::ResourceAccess::DynamicVariable);
		tz::gl::BufferResource ibuf = tz::gl::BufferResource::from_many
		({
			0u, 1u, 2u
		}, tz::gl::ResourceAccess::DynamicVariable, {tz::gl::ResourceFlag::IndexBuffer});

		tz::gl::RendererInfo rinfo;
		rinfo.shader().set_shader(tz::gl::ShaderStage::Vertex, ImportedShaderSource(tz_dynamic_triangle_demo, vertex));
		rinfo.shader().set_shader(tz::gl::ShaderStage::Fragment, ImportedShaderSource(tz_dynamic_triangle_demo, fragment));
		tz::gl::ResourceHandle imgh = rinfo.add_resource(img);
		tz::gl::ResourceHandle bufh = rinfo.add_resource(buf);
		tz::gl::ResourceHandle ibufh = rinfo.add_resource(ibuf);

		tz::gl::Renderer renderer = dev.create_renderer(rinfo);
		std::default_random_engine rand;
		tz::Delay fixed_update{50_ms};

		while(!tz::window().is_close_requested())
		{
			TZ_FRAME_BEGIN;
			tz::window().update();
			renderer.render(triangle_count);
			static bool up = true;
			if(fixed_update.done())
			{
				static bool wireframe_mode = false;
				if(tz::window().get_keyboard_state().is_key_down(tz::KeyCode::Q))
				{
					wireframe_mode = !wireframe_mode;
				}

				TZ_PROFZONE("Dynamic Updates", TZ_PROFCOL_GREEN);
				// Add new triangle by resizing the triangle vertex storage buffer to a capacity large enough for an extra triangle. Then we randomise the new triangle data.
				fixed_update.reset();
				triangle_count++;
				auto img_comp = static_cast<tz::gl::ImageComponent*>(renderer.get_component(imgh));
				if(img_comp->get_dimensions()[0] > 49)
				{
					up = false;
				}
				else if(img_comp->get_dimensions()[0] < 2)
				{
					up = true;
				}
				tz::Vec2ui new_dims = img_comp->get_dimensions();
				if(up)
				{
					new_dims += {1u, 1u};
				}
				else
				{
					new_dims -= {1u, 1u};
				}

				const std::size_t vtx_count = 3 * triangle_count;
				renderer.edit(tz::gl::RendererEditBuilder{}
					.buffer({.buffer_handle = bufh, .size = sizeof(TriangleVertexData) * vtx_count})
					.buffer({.buffer_handle = ibufh, .size = sizeof(unsigned int) * vtx_count})
					.image({.image_handle = imgh, .dimensions = new_dims})
					.render_state({.wireframe_mode = wireframe_mode})
					.build());
				// Get the resource data for the new triangle and set it to random values.
				std::span<TriangleVertexData> buf_data = renderer.get_resource(bufh)->data_as<TriangleVertexData>();
				std::span<unsigned int> idx_data = renderer.get_resource(ibufh)->data_as<unsigned int>();
				// We do this by setting the last 3 vertices to a random triangle;
				for(std::size_t i = 0; i < 3; i++)
				{
					buf_data[buf_data.size() - 3 + i] = get_random_triangle(rand);
					idx_data[idx_data.size() - 3 + i] = ((triangle_count - 1) * 3) + i;
				}

				// While we're at it, randomise the second pixel of the image.
				tz::Vec2ui img_dims = img_comp->get_dimensions();
				std::span<std::byte> img_data = renderer.get_resource(imgh)->data();
				std::random_shuffle(img_data.begin(), img_data.end());
				tz::GridView<std::byte, 4> img_view{img_data, img_dims};
				std::span<std::byte> pixel_data = img_view(0, 0);
				std::fill(pixel_data.begin(), pixel_data.end(), std::byte{255});
			}

			TZ_FRAME_END;
		}
	}
	tz::terminate();
}
<commit_msg>* Simplified tz_dynamic_triangle_demo very slightly<commit_after>#include "core/tz.hpp"
#include "core/window.hpp"
#include "core/profiling/zone.hpp"
#include "core/containers/grid_view.hpp"
#include "core/time.hpp"
#include "gl/device.hpp"
#include "gl/renderer.hpp"
#include "gl/resource.hpp"
#include "gl/imported_shaders.hpp"
#include "gl/component.hpp"
#include <random>

#include ImportedShaderHeader(tz_dynamic_triangle_demo, vertex)
#include ImportedShaderHeader(tz_dynamic_triangle_demo, fragment)

struct TriangleVertexData
{
	tz::Vec3 position;
	float pad0;
	tz::Vec2 texcoord;
	float pad1[2];
};

TriangleVertexData get_random_triangle(std::default_random_engine& rand)
{
	std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
	return
	{
		.position = {dist(rand), dist(rand), dist(rand)},
		.texcoord = {std::abs(dist(rand)), std::abs(dist(rand))}
	};
}

int main()
{
	tz::initialise
	({
		.name = "tz_dynamic_triangle_demo"
	});
	{
		tz::gl::Device dev;
		using namespace tz::literals;
		tz::gl::ImageResource img = tz::gl::ImageResource::from_memory
		(
			tz::gl::ImageFormat::RGBA32,
			{2u, 2u},
			{
				0b1111'1111_uc,
				0b0000'0000_uc,
				0b0000'0000_uc,
				0b1111'1111_uc,

				0b1111'1111_uc,
				0b1111'1111_uc,
				0b0000'0000_uc,
				0b1111'1111_uc,

				0b0000'0000_uc,
				0b0000'0000_uc,
				0b1111'1111_uc,
				0b1111'1111_uc,

				0b0000'0000_uc,
				0b1111'1111_uc,
				0b0000'0000_uc,
				0b1111'1111_uc
			},
			tz::gl::ResourceAccess::DynamicVariable
		);

		std::size_t triangle_count = 1;
		tz::gl::BufferResource buf = tz::gl::BufferResource::from_many
		({
			TriangleVertexData{.position = {-0.5f, -0.5f, -1.0f}, .texcoord = {0.0f, 0.0f}},
			TriangleVertexData{.position = {0.0f, 0.5f, -1.0f}, .texcoord = {0.5f, 1.0f}},
			TriangleVertexData{.position = {0.5f, -0.5f, -1.0f}, .texcoord = {1.0f, 0.0f}},
		}, tz::gl::ResourceAccess::DynamicVariable);
		tz::gl::BufferResource ibuf = tz::gl::BufferResource::from_many
		({
			0u, 1u, 2u
		}, tz::gl::ResourceAccess::DynamicVariable, {tz::gl::ResourceFlag::IndexBuffer});

		tz::gl::RendererInfo rinfo;
		rinfo.shader().set_shader(tz::gl::ShaderStage::Vertex, ImportedShaderSource(tz_dynamic_triangle_demo, vertex));
		rinfo.shader().set_shader(tz::gl::ShaderStage::Fragment, ImportedShaderSource(tz_dynamic_triangle_demo, fragment));
		tz::gl::ResourceHandle imgh = rinfo.add_resource(img);
		tz::gl::ResourceHandle bufh = rinfo.add_resource(buf);
		tz::gl::ResourceHandle ibufh = rinfo.add_resource(ibuf);

		tz::gl::Renderer renderer = dev.create_renderer(rinfo);
		std::default_random_engine rand;
		tz::Delay fixed_update{50_ms};

		while(!tz::window().is_close_requested())
		{
			TZ_FRAME_BEGIN;
			tz::window().update();
			renderer.render(triangle_count);
			static bool up = true;
			if(fixed_update.done())
			{
				static bool wireframe_mode = false;
				if(tz::window().get_keyboard_state().is_key_down(tz::KeyCode::Q))
				{
					wireframe_mode = !wireframe_mode;
				}

				TZ_PROFZONE("Dynamic Updates", TZ_PROFCOL_GREEN);
				// Add new triangle by resizing the triangle vertex storage buffer to a capacity large enough for an extra triangle. Then we randomise the new triangle data.
				fixed_update.reset();
				triangle_count++;
				auto img_comp = static_cast<tz::gl::ImageComponent*>(renderer.get_component(imgh));
				if(img_comp->get_dimensions()[0] > 49)
				{
					up = false;
				}
				else if(img_comp->get_dimensions()[0] < 2)
				{
					up = true;
				}
				tz::Vec2ui new_dims = img_comp->get_dimensions();
				if(up)
				{
					new_dims += {1u, 1u};
				}
				else
				{
					new_dims -= {1u, 1u};
				}

				const std::size_t vtx_count = 3 * triangle_count;
				renderer.edit(tz::gl::RendererEditBuilder{}
					.buffer({.buffer_handle = bufh, .size = sizeof(TriangleVertexData) * vtx_count})
					.buffer({.buffer_handle = ibufh, .size = sizeof(unsigned int) * vtx_count})
					.image({.image_handle = imgh, .dimensions = new_dims})
					.render_state({.wireframe_mode = wireframe_mode})
					.build());
				// Get the resource data for the new triangle and set it to random values.
				std::span<TriangleVertexData> buf_data = renderer.get_resource(bufh)->data_as<TriangleVertexData>();
				std::span<unsigned int> idx_data = renderer.get_resource(ibufh)->data_as<unsigned int>();
				// We do this by setting the last 3 vertices to a random triangle;
				for(std::size_t i = 0; i < 3; i++)
				{
					buf_data[buf_data.size() - 3 + i] = get_random_triangle(rand);
					idx_data[idx_data.size() - 3 + i] = ((triangle_count - 1) * 3) + i;
				}

				// Shuffle each byte randomly.
				tz::Vec2ui img_dims = img_comp->get_dimensions();
				std::span<std::byte> img_data = renderer.get_resource(imgh)->data();
				std::random_shuffle(img_data.begin(), img_data.end());
				// But set the first pixel (bottom left) to always be white.
				std::fill(img_data.begin(), img_data.begin() + 4, std::byte{255});
			}

			TZ_FRAME_END;
		}
	}
	tz::terminate();
}
<|endoftext|>


<commit_before>#ifndef __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__
#define __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__

#include <mapnik/datasource.hpp>
#include <mapnik/box2d.hpp>
#include "pbf_reader.hpp"

namespace mapnik { namespace vector_tile_impl {

    // TODO: consider using mapnik::value here instead
    using pbf_attr_value_type = mapnik::util::variant<std::string, float, double, int64_t, uint64_t, bool>;
    using layer_pbf_attr_type = std::vector<pbf_attr_value_type>;

    class tile_datasource_pbf : public datasource
    {
    public:
        tile_datasource_pbf(mapbox::util::pbf const& layer,
                        unsigned x,
                        unsigned y,
                        unsigned z,
                        unsigned tile_size);
        virtual ~tile_datasource_pbf();
        datasource::datasource_t type() const;
        featureset_ptr features(query const& q) const;
        featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const;
        void set_envelope(box2d<double> const& bbox);
        box2d<double> get_tile_extent() const;
        box2d<double> envelope() const;
        boost::optional<datasource_geometry_t> get_geometry_type() const;
        layer_descriptor get_descriptor() const;
    private:
        mutable mapnik::layer_descriptor desc_;
        mutable bool attributes_added_;
        mapbox::util::pbf layer_;
        unsigned x_;
        unsigned y_;
        unsigned z_;
        unsigned tile_size_;
        mutable bool extent_initialized_;
        mutable mapnik::box2d<double> extent_;
        double tile_x_;
        double tile_y_;
        double scale_;
        uint32_t layer_extent_;

        std::string name_;
        std::vector<mapbox::util::pbf> features_;
        std::vector<std::string> layer_keys_;
        layer_pbf_attr_type layer_values_;
    };

}} // end ns

#if !defined(MAPNIK_VECTOR_TILE_LIBRARY)
#include "vector_tile_datasource_pbf.ipp"
#endif

#endif // __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__
<commit_msg>make name available on tile_datasource_pbf<commit_after>#ifndef __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__
#define __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__

#include <mapnik/datasource.hpp>
#include <mapnik/box2d.hpp>
#include "pbf_reader.hpp"

namespace mapnik { namespace vector_tile_impl {

    // TODO: consider using mapnik::value here instead
    using pbf_attr_value_type = mapnik::util::variant<std::string, float, double, int64_t, uint64_t, bool>;
    using layer_pbf_attr_type = std::vector<pbf_attr_value_type>;

    class tile_datasource_pbf : public datasource
    {
    public:
        tile_datasource_pbf(mapbox::util::pbf const& layer,
                        unsigned x,
                        unsigned y,
                        unsigned z,
                        unsigned tile_size);
        virtual ~tile_datasource_pbf();
        datasource::datasource_t type() const;
        featureset_ptr features(query const& q) const;
        featureset_ptr features_at_point(coord2d const& pt, double tol = 0) const;
        void set_envelope(box2d<double> const& bbox);
        box2d<double> get_tile_extent() const;
        box2d<double> envelope() const;
        boost::optional<datasource_geometry_t> get_geometry_type() const;
        layer_descriptor get_descriptor() const;
        std::string const& get_name() { return name_; }
    private:
        mutable mapnik::layer_descriptor desc_;
        mutable bool attributes_added_;
        mapbox::util::pbf layer_;
        unsigned x_;
        unsigned y_;
        unsigned z_;
        unsigned tile_size_;
        mutable bool extent_initialized_;
        mutable mapnik::box2d<double> extent_;
        double tile_x_;
        double tile_y_;
        double scale_;
        uint32_t layer_extent_;

        std::string name_;
        std::vector<mapbox::util::pbf> features_;
        std::vector<std::string> layer_keys_;
        layer_pbf_attr_type layer_values_;
    };

}} // end ns

#if !defined(MAPNIK_VECTOR_TILE_LIBRARY)
#include "vector_tile_datasource_pbf.ipp"
#endif

#endif // __MAPNIK_VECTOR_TILE_DATASOURCE_PBF_H__
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Nathan Osman
 *
 * 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.
 */

#include <QtGlobal>

#if defined(Q_OS_WIN32)
#  include <windows.h>
#elif defined(Q_OS_UNIX)
#  include <sys/stat.h>
#  include <utime.h>
#endif

#include <QDateTime>

#include "file.h"

File::File(const QString &root, const QVariantMap &properties)
{
    mRelativeFilename = properties.value("name").toString();

    // Create the full path by combining the root with the relative filename
    mFile.setFileName(QDir::cleanPath(root + QDir::separator() + mRelativeFilename));

    mSize = properties.value("size").toLongLong();
    mReadOnly = properties.value("read_only").toBool();
    mExecutable = properties.value("executable").toBool();

    mCreated = properties.value("created").toInt();
    mLastRead = properties.value("last_read").toInt();
    mLastModified = properties.value("last_modified").toInt();
}

File::File(const QDir &root, const QFileInfo &info, int blockSize)
{
    mFile.setFileName(info.absoluteFilePath());
    mBlockSize = blockSize;

    mRelativeFilename = root.relativeFilePath(info.absoluteFilePath());

    mSize = info.size();
    mReadOnly = !info.isWritable();
    mExecutable = info.isExecutable();

    mCreated = info.created().toMSecsSinceEpoch();
    mLastRead = info.lastRead().toMSecsSinceEpoch();
    mLastModified = info.lastModified().toMSecsSinceEpoch();
}

bool File::readOnly() const
{
    return mReadOnly;
}

bool File::executable() const
{
    return mExecutable;
}

qint64 File::created() const
{
    return mCreated;
}

qint64 File::lastRead() const
{
    return mLastRead;
}

qint64 File::lastModified() const
{
    return mLastModified;
}

qint64 File::last_read() const
{
    return mLastRead;
}

qint64 File::last_modified() const
{
    return mLastModified;
}

QString File::type() const
{
    return "file";
}

QString File::name() const
{
    return mRelativeFilename;
}

qint64 File::size() const
{
    return mSize;
}

bool File::open(OpenMode openMode)
{
    if (openMode == Write && !QDir(QFileInfo(mFile.fileName()).absolutePath()).mkpath(".")) {
        return false;
    }
    return mFile.open(openMode == Read ? QIODevice::ReadOnly : QIODevice::WriteOnly);
}

QByteArray File::read()
{
    // Allocate a full block and then resize to actual data length
    QByteArray data;
    data.resize(mBlockSize);
    qint64 bytesRead = mFile.read(data.data(), mBlockSize);
    data.resize(bytesRead);

    if (bytesRead == -1) {
        emit error(mFile.errorString());
    }

    return data;
}

void File::write(const QByteArray &data)
{
    if (mFile.write(data) == -1) {
        emit error(mFile.errorString());
    }
}

#ifdef Q_OS_WIN32

// Adapted from https://support.microsoft.com/en-us/help/167296
void unixTimestampMsToFiletime(qint64 timestampMs, LPFILETIME pft)
{
    // Convert from MS to 100-nanosecond intervals
    quint64 ll = timestampMs * 10000;

    // Assign to members of FILETIME struct
    pft->dwLowDateTime = (DWORD)ll;
    pft->dwHighDateTime = ll >> 32;
}

#endif

void File::close()
{
    mFile.close();

#if defined(Q_OS_WIN32)

    FILETIME createdFiletime;
    FILETIME lastReadFiletime;
    FILETIME lastModifiedFiletime;

    // Perform the conversions
    unixTimestampMsToFiletime(mCreated, &createdFiletime);
    unixTimestampMsToFiletime(mLastRead, &lastReadFiletime);
    unixTimestampMsToFiletime(mLastModified, &lastModifiedFiletime);

    // Open the file
    HANDLE hFile = CreateFileW(
        reinterpret_cast<LPCWSTR>(mFile.fileName().utf16()),
        GENERIC_READ | FILE_WRITE_ATTRIBUTES,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );
    if (hFile == INVALID_HANDLE_VALUE) {
        // TODO: throw an error
        return;
    }

    // Set the attributes
    BOOL succeeded = SetFileTime(
        hFile,
        mCreated ? &createdFiletime : NULL,
        mLastRead ? &lastReadFiletime : NULL,
        mLastModified ? &lastModifiedFiletime : NULL
    );
    if (succeeded == FALSE) {
        // TODO: throw an error
    }

    CloseHandle(hFile);

#elif defined(Q_OS_UNIX)

    // Retrieve existing statistics
    struct stat oldStats;
    if (stat(mFile.fileName().toUtf8(), &oldStats)) {
        // TODO: throw error
        return;
    }

    // Create the struct with the new values
    struct utimbuf newTimes;
    newTimes.actime = mLastRead ? mLastRead / 1000 : oldStats.st_atim.tv_sec;
    newTimes.modtime = mLastModified ? mLastModified / 1000 : oldStats.st_mtim.tv_sec;

    // Set the new values
    if (utime(mFile.fileName().toUtf8(), &newTimes)) {
        // TODO: throw error
    }

#endif
}
<commit_msg>Complete implementation of remaining attributes on Unix (#73).<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Nathan Osman
 *
 * 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.
 */

#include <QtGlobal>

#if defined(Q_OS_WIN32)
#  include <windows.h>
#elif defined(Q_OS_UNIX)
#  include <sys/stat.h>
#  include <utime.h>
#endif

#include <QDateTime>

#include "file.h"

File::File(const QString &root, const QVariantMap &properties)
{
    mRelativeFilename = properties.value("name").toString();

    // Create the full path by combining the root with the relative filename
    mFile.setFileName(QDir::cleanPath(root + QDir::separator() + mRelativeFilename));

    mSize = properties.value("size").toLongLong();
    mReadOnly = properties.value("read_only").toBool();
    mExecutable = properties.value("executable").toBool();

    mCreated = properties.value("created").toInt();
    mLastRead = properties.value("last_read").toInt();
    mLastModified = properties.value("last_modified").toInt();
}

File::File(const QDir &root, const QFileInfo &info, int blockSize)
{
    mFile.setFileName(info.absoluteFilePath());
    mBlockSize = blockSize;

    mRelativeFilename = root.relativeFilePath(info.absoluteFilePath());

    mSize = info.size();
    mReadOnly = !info.isWritable();
    mExecutable = info.isExecutable();

    mCreated = info.created().toMSecsSinceEpoch();
    mLastRead = info.lastRead().toMSecsSinceEpoch();
    mLastModified = info.lastModified().toMSecsSinceEpoch();
}

bool File::readOnly() const
{
    return mReadOnly;
}

bool File::executable() const
{
    return mExecutable;
}

qint64 File::created() const
{
    return mCreated;
}

qint64 File::lastRead() const
{
    return mLastRead;
}

qint64 File::lastModified() const
{
    return mLastModified;
}

qint64 File::last_read() const
{
    return mLastRead;
}

qint64 File::last_modified() const
{
    return mLastModified;
}

QString File::type() const
{
    return "file";
}

QString File::name() const
{
    return mRelativeFilename;
}

qint64 File::size() const
{
    return mSize;
}

bool File::open(OpenMode openMode)
{
    if (openMode == Write && !QDir(QFileInfo(mFile.fileName()).absolutePath()).mkpath(".")) {
        return false;
    }
    return mFile.open(openMode == Read ? QIODevice::ReadOnly : QIODevice::WriteOnly);
}

QByteArray File::read()
{
    // Allocate a full block and then resize to actual data length
    QByteArray data;
    data.resize(mBlockSize);
    qint64 bytesRead = mFile.read(data.data(), mBlockSize);
    data.resize(bytesRead);

    if (bytesRead == -1) {
        emit error(mFile.errorString());
    }

    return data;
}

void File::write(const QByteArray &data)
{
    if (mFile.write(data) == -1) {
        emit error(mFile.errorString());
    }
}

#ifdef Q_OS_WIN32

// Adapted from https://support.microsoft.com/en-us/help/167296
void unixTimestampMsToFiletime(qint64 timestampMs, LPFILETIME pft)
{
    // Convert from MS to 100-nanosecond intervals
    quint64 ll = timestampMs * 10000;

    // Assign to members of FILETIME struct
    pft->dwLowDateTime = (DWORD)ll;
    pft->dwHighDateTime = ll >> 32;
}

#endif

void File::close()
{
    mFile.close();

#if defined(Q_OS_WIN32)

    FILETIME createdFiletime;
    FILETIME lastReadFiletime;
    FILETIME lastModifiedFiletime;

    // Perform the conversions
    unixTimestampMsToFiletime(mCreated, &createdFiletime);
    unixTimestampMsToFiletime(mLastRead, &lastReadFiletime);
    unixTimestampMsToFiletime(mLastModified, &lastModifiedFiletime);

    // Open the file
    HANDLE hFile = CreateFileW(
        reinterpret_cast<LPCWSTR>(mFile.fileName().utf16()),
        GENERIC_READ | FILE_WRITE_ATTRIBUTES,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );
    if (hFile == INVALID_HANDLE_VALUE) {
        // TODO: throw an error
        return;
    }

    // Set the attributes
    BOOL succeeded = SetFileTime(
        hFile,
        mCreated ? &createdFiletime : NULL,
        mLastRead ? &lastReadFiletime : NULL,
        mLastModified ? &lastModifiedFiletime : NULL
    );
    if (succeeded == FALSE) {
        // TODO: throw an error
    }

    CloseHandle(hFile);

#elif defined(Q_OS_UNIX)

    // Retrieve existing statistics
    struct stat oldStats;
    if (stat(mFile.fileName().toUtf8().constData(), &oldStats)) {
        // TODO: throw error
        return;
    }

    // Load the existing file mode
    mode_t fileMode = oldStats.st_mode;

    // If the file is marked as read-only, remove the write bits
    if (mReadOnly) {
        fileMode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
    }

    // If the file is marked as executable, add the executable bits
    if (mExecutable) {
        fileMode |= (S_IXUSR | S_IXGRP | S_IXOTH);
    }

    // If the value has changed, update the file
    if (oldStats.st_mode != fileMode &&
            chmod(mFile.fileName().toUtf8().constData(), fileMode)) {
        // TODO: throw error
        return;
    }

    // Create the struct with the new values
    struct utimbuf newTimes;
    newTimes.actime = mLastRead ? mLastRead / 1000 : oldStats.st_atim.tv_sec;
    newTimes.modtime = mLastModified ? mLastModified / 1000 : oldStats.st_mtim.tv_sec;

    // Set the new values
    if (utime(mFile.fileName().toUtf8(), &newTimes)) {
        // TODO: throw error
    }

#endif
}
<|endoftext|>


<commit_before>#include <iostream>

#include "Service.hpp"

/**
 * @brief Icecream vendor service implementation.
 * @details The icecream vendor is a service that serves customers
 * icecream depending on their flavor choice.
 */
class IcecreamVendor : public NSA::Service
{
public:
	IcecreamVendor() : Service("Icecream Service")
	{}

private:
	void serveIcecreamImpl(Service::Promise<std::string> promise, const std::string order)
	{
		printf("%s: Working on order (%s)\n", name.c_str(), order.c_str());
		const int duration = (rand() % 4) + 3;
		std::this_thread::sleep_for(std::chrono::seconds(duration));
		std::string compositeOrder = "Cone with: " + order;
		promise->set_value(compositeOrder);
		printf("%s: Finished order (%s). Took %d minutes\n", name.c_str(), order.c_str(), duration);
	}

public:
	/**
	 * @brief Serves a customer icecream.
	 * @details The average duration of the service is 3 to 6 minuts.
	 *
	 * @param order A string representation of the order.
	 * @return A future containing icecream.
	 */
	Service::Future<std::string> serveIcecream(const std::string order)
	{
		NSA_MAKE_PROMISE(IcecreamVendor::serveIcecreamImpl, std::string, order);
	}
};

/// A enum of randomflavors.
enum Flavor
{
	STRAWBERRY = 0,
	VANILLE,
	CHOCOLATE,
	BANANA,
	MILK,
	CHERRY,
	PRUNE,
	PEACH,
	LEMON,
	KIWI,
	CREAM,
	PINEAPPLE,
	APPLE,
	BACON,
	DURIAN,
	MELON,
	PITAYA,
	AVOCADO,
	CARROT,
	GARLIC,
	MANGO,
	ORANGE,
	PEAR,
	TOMATO,
	TOTAL_FLAVOR_COUNT
};

/**
 * @brief Simple function to convert a enum value to a string.
 * @param flavor A valid enum value of the flavor enum.
 * @return A string containing the flavor.
 */
std::string flavor2string(const Flavor &flavor)
{
	switch (flavor)
	{
		case STRAWBERRY: return "Strawberry";
		case VANILLE:	 return "Vanille";
		case CHOCOLATE:	 return "Chocolate";
		case BANANA:	 return "Banana";
		case MILK:		 return "Milk";
		case CHERRY:	 return "Cherry";
		case PRUNE:		 return "Prune";
		case PEACH:		 return "Peach";
		case LEMON:		 return "Lemon";
		case KIWI:		 return "Kiwi";
		case CREAM:		 return "Cream";
		case PINEAPPLE:	 return "Pineapple";
		case APPLE:		 return "Apple";
		case BACON:		 return "Bacon";
		case DURIAN:	 return "Durian";
		case MELON:		 return "Melon";
		case PITAYA:	 return "Pitaya";
		case AVOCADO:	 return "Avocado";
		case CARROT:	 return "Carrot";
		case GARLIC:	 return "Garlic";
		case MANGO:		 return "Mango";
		case ORANGE:	 return "Orange";
		case PEAR:		 return "Pear";
		case TOMATO :	 return "Tomato";
	}
}

/**
 * @brief A function which creates a random flavor.
 * @return A full string containing a random flavor.
 */
std::string createRandomFlavor()
{
	std::string returnValue = "";

	const std::size_t flavors = (rand() % 5) + 1;

	for (std::size_t i = 0; i < flavors; i++)
	{
		returnValue += flavor2string(static_cast<Flavor>(rand() % TOTAL_FLAVOR_COUNT));

		if (i < flavors - 1) returnValue += " ";
	}

	return returnValue;
}

/**
 * @brief Thread function which generates customers.
 * @details This threaded function creates customers
 * based on a regulated model for the icecream service
 * simulation.
 *
 * @param vendor A reference to the vendor to which send the customers.
 * @param done A status message to indicate the end of the simulation.
 */
void customers(IcecreamVendor &vendor, bool &done)
{
	// Define intervals of cusomters.
	const int morning = 7;
	const int midday  = 3;
	const int evening = 10;

	printf("Morning customers\n");
	for (std::size_t group = 0; group < 10; group++)
	{
		const std::size_t customers = (rand() % 3) + 1;
		std::this_thread::sleep_for(std::chrono::seconds(morning));

		printf("A group arives containing %d customers.\n", customers);

		for (std::size_t customer = 0; customer < customers; customer++)
			vendor.serveIcecream(createRandomFlavor());
	}

	printf("Midday customers\n");
	for (std::size_t group = 0; group < 10; group++)
	{
		const std::size_t customers = (rand() % 3) + 3;
		std::this_thread::sleep_for(std::chrono::seconds(midday));

		printf("A group arives containing %d customers.\n", customers);

		for (std::size_t customer = 0; customer < customers; customer++)
			vendor.serveIcecream(createRandomFlavor());
	}

	printf("Evening customers\n");
	for (std::size_t group = 0; group < 10; group++)
	{
		const std::size_t customers = (rand() % 4) + 3;
		std::this_thread::sleep_for(std::chrono::seconds(evening));

		printf("A group arives containing %d customers.\n", customers);

		for (std::size_t customer = 0; customer < customers; customer++)
			vendor.serveIcecream(createRandomFlavor());
	}

	done = true;
}

int main(int argc, char **argv)
{
	// Create a icecream vendor.
	IcecreamVendor vendor;

	// Add 3 workers to the vendor.
	vendor.detach(3);

	// Set the wait condition.
	bool done = false;

	// Send in the customers.
	printf("The store is open\n");
	std::thread customerThread(&customers, std::ref(vendor), std::ref(done));

	// Print out overview information.
	while (!done)
	{
		printf("Current waiting customers: %d. Total customers: %d\n",
			vendor.currentJobs(), vendor.totalJobs());
		std::this_thread::sleep_for(std::chrono::seconds(1));
	}
	printf("Store is closed, working of the final jobs.\n");

	// Join the costumer producing thread.
	customerThread.join();

	// Join the costumer cnsuming thread.
	vendor.join();

	printf("A total of %d customers where served today.\n", vendor.totalJobs());
	return EXIT_SUCCESS;
}<commit_msg>Updated the example, customers are generated by another service.<commit_after>#include <iostream>

#include "Service.hpp"

/**
 * @brief Icecream vendor service implementation.
 * @details The icecream vendor is a service that serves customers
 * icecream depending on their flavor choice.
 */
class IcecreamVendor : public NSA::Service
{
public:
	IcecreamVendor() : Service("Icecream Service")
	{}

private:
	void serveIcecreamImpl(Service::Promise<std::string> promise, const std::string order)
	{
		printf("%s: Working on order (%s)\n", name.c_str(), order.c_str());
		const int duration = (rand() % 4) + 3;
		std::this_thread::sleep_for(std::chrono::seconds(duration));
		std::string compositeOrder = "Cone with: " + order;
		promise->set_value(compositeOrder);
		printf("%s: Finished order (%s). Took %d minutes\n", name.c_str(), order.c_str(), duration);
	}

public:
	/**
	 * @brief Serves a customer icecream.
	 * @details The average duration of the service is 3 to 6 minuts.
	 *
	 * @param order A string representation of the order.
	 * @return A future containing icecream.
	 */
	Service::Future<std::string> serveIcecream(const std::string order)
	{
		NSA_MAKE_PROMISE(IcecreamVendor::serveIcecreamImpl, std::string, order);
	}
};

/// A enum of randomflavors.
enum Flavor
{
	STRAWBERRY = 0,
	VANILLE,
	CHOCOLATE,
	BANANA,
	MILK,
	CHERRY,
	PRUNE,
	PEACH,
	LEMON,
	KIWI,
	CREAM,
	PINEAPPLE,
	APPLE,
	BACON,
	DURIAN,
	MELON,
	PITAYA,
	AVOCADO,
	CARROT,
	GARLIC,
	MANGO,
	ORANGE,
	PEAR,
	TOMATO,
	TOTAL_FLAVOR_COUNT
};

/**
 * @brief Simple function to convert a enum value to a string.
 * @param flavor A valid enum value of the flavor enum.
 * @return A string containing the flavor.
 */
std::string flavor2string(const Flavor &flavor)
{
	switch (flavor)
	{
		case STRAWBERRY: return "Strawberry";
		case VANILLE:	 return "Vanille";
		case CHOCOLATE:	 return "Chocolate";
		case BANANA:	 return "Banana";
		case MILK:		 return "Milk";
		case CHERRY:	 return "Cherry";
		case PRUNE:		 return "Prune";
		case PEACH:		 return "Peach";
		case LEMON:		 return "Lemon";
		case KIWI:		 return "Kiwi";
		case CREAM:		 return "Cream";
		case PINEAPPLE:	 return "Pineapple";
		case APPLE:		 return "Apple";
		case BACON:		 return "Bacon";
		case DURIAN:	 return "Durian";
		case MELON:		 return "Melon";
		case PITAYA:	 return "Pitaya";
		case AVOCADO:	 return "Avocado";
		case CARROT:	 return "Carrot";
		case GARLIC:	 return "Garlic";
		case MANGO:		 return "Mango";
		case ORANGE:	 return "Orange";
		case PEAR:		 return "Pear";
		case TOMATO :	 return "Tomato";
	}
}

/**
 * @brief A function which creates a random flavor.
 * @return A full string containing a random flavor.
 */
std::string createRandomFlavor()
{
	std::string returnValue = "";

	const std::size_t flavors = (rand() % 5) + 1;

	for (std::size_t i = 0; i < flavors; i++)
	{
		returnValue += flavor2string(static_cast<Flavor>(rand() % TOTAL_FLAVOR_COUNT));

		if (i < flavors - 1) returnValue += " ";
	}

	return returnValue;
}

/**
 * @brief A service that creates customers.
 * @details This service generates customers based upon the specifications
 * described in the excercise.
 */
class Customers : public NSA::Service
{
public:
	/**
	 * @brief Default constructor.
	 * @param vendor A initialized vendor service.
	 */
	Customers(IcecreamVendor &vendor) : Service("Customers"),
		vendor(vendor)
	{}

	/**
	 * @brief Creates a random amount of customers.
	 * @return A future containing a true status.
	 */
	Service::Future<bool> simulateCustomers()
	{
		NSA_MAKE_PROMISE(Customers::simulateCustomersImp, bool);
	}

private:

	/**
	 * @brief Implementation of the service interface.
	 * @param promise Promises to give an update once it is finished.
	 */
	void simulateCustomersImp(Service::Promise<bool> promise)
	{
		// Define intervals of cusomters.
		const int morning = 7;
		const int midday  = 3;
		const int evening = 10;
		const int groups  = 1;

		printf("Morning customers\n");
		for (std::size_t group = 0; group < groups; group++)
		{
			const std::size_t customers = (rand() % 3) + 1;
			std::this_thread::sleep_for(std::chrono::seconds(morning));

			printf("A group arives containing %d customers.\n", customers);

			for (std::size_t customer = 0; customer < customers; customer++)
				vendor.serveIcecream(createRandomFlavor());
		}

		printf("Midday customers\n");
		for (std::size_t group = 0; group < groups; group++)
		{
			const std::size_t customers = (rand() % 3) + 3;
			std::this_thread::sleep_for(std::chrono::seconds(midday));

			printf("A group arives containing %d customers.\n", customers);

			for (std::size_t customer = 0; customer < customers; customer++)
				vendor.serveIcecream(createRandomFlavor());
		}

		printf("Evening customers\n");
		for (std::size_t group = 0; group < groups; group++)
		{
			const std::size_t customers = (rand() % 4) + 3;
			std::this_thread::sleep_for(std::chrono::seconds(evening));

			printf("A group arives containing %d customers.\n", customers);

			for (std::size_t customer = 0; customer < customers; customer++)
				vendor.serveIcecream(createRandomFlavor());
		}

		promise->set_value(true);
	}

	IcecreamVendor &vendor; ///< A reference to a vendor.

};

int main(int argc, char **argv)
{
	// Create a icecream vendor.
	IcecreamVendor vendor;
	Customers customers(vendor);

	// Add 3 workers to the vendor.
	const int storeWorkers = 3;
	printf("The store is open.\n");
	vendor.detach(storeWorkers);

	// Add 1 worker to simulate customers.
	printf("Customor simulation is ready.\n");
	customers.detach();

	// Status of the simulation worker.
	NSA::Service::Future<bool> simulationEnd = customers.simulateCustomers();
	std::future_status status;

	// Post status information as long as the simulation runs.
	do
	{
		printf("Current waiting customers: %d. Total customers: %d\n",
			vendor.currentJobs(), vendor.totalJobs());

		status = simulationEnd->wait_for(std::chrono::seconds(1));
	}
	while(status != std::future_status::ready);

	printf("Store is closed, working of the final jobs.\n");

	// Join the costumer producing thread.
	printf("Closing customer simulation.\n");
	customers.join();

	// Join the costumer cnsuming thread.
	printf("Closing store\n");
	vendor.join();

	/// The total number of customers is the total amount of jobs - the worker count.
	/// Each worker adds a final cleanup job after it is asked to join up.
	printf("A total of %d customers where served today.\n", vendor.totalJobs() - storeWorkers);
	return EXIT_SUCCESS;
}<|endoftext|>


<commit_before>//===-- lib/CodeGen/MachineInstrBundle.cpp --------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/MachineInstrBundle.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/Target/TargetSubtargetInfo.h"
using namespace llvm;

namespace {
  class UnpackMachineBundles : public MachineFunctionPass {
  public:
    static char ID; // Pass identification
    UnpackMachineBundles() : MachineFunctionPass(ID) {
      initializeUnpackMachineBundlesPass(*PassRegistry::getPassRegistry());
    }

    bool runOnMachineFunction(MachineFunction &MF) override;
  };
} // end anonymous namespace

char UnpackMachineBundles::ID = 0;
char &llvm::UnpackMachineBundlesID = UnpackMachineBundles::ID;
INITIALIZE_PASS(UnpackMachineBundles, "unpack-mi-bundles",
                "Unpack machine instruction bundles", false, false)

bool UnpackMachineBundles::runOnMachineFunction(MachineFunction &MF) {
  bool Changed = false;
  for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) {
    MachineBasicBlock *MBB = &*I;

    for (MachineBasicBlock::instr_iterator MII = MBB->instr_begin(),
           MIE = MBB->instr_end(); MII != MIE; ) {
      MachineInstr *MI = &*MII;

      // Remove BUNDLE instruction and the InsideBundle flags from bundled
      // instructions.
      if (MI->isBundle()) {
        while (++MII != MIE && MII->isBundledWithPred()) {
          MII->unbundleFromPred();
          for (unsigned i = 0, e = MII->getNumOperands(); i != e; ++i) {
            MachineOperand &MO = MII->getOperand(i);
            if (MO.isReg() && MO.isInternalRead())
              MO.setIsInternalRead(false);
          }
        }
        MI->eraseFromParent();

        Changed = true;
        continue;
      }

      ++MII;
    }
  }

  return Changed;
}

namespace {
  class FinalizeMachineBundles : public MachineFunctionPass {
  public:
    static char ID; // Pass identification
    FinalizeMachineBundles() : MachineFunctionPass(ID) {
      initializeFinalizeMachineBundlesPass(*PassRegistry::getPassRegistry());
    }

    bool runOnMachineFunction(MachineFunction &MF) override;
  };
} // end anonymous namespace

char FinalizeMachineBundles::ID = 0;
char &llvm::FinalizeMachineBundlesID = FinalizeMachineBundles::ID;
INITIALIZE_PASS(FinalizeMachineBundles, "finalize-mi-bundles",
                "Finalize machine instruction bundles", false, false)

bool FinalizeMachineBundles::runOnMachineFunction(MachineFunction &MF) {
  return llvm::finalizeBundles(MF);
}

/// finalizeBundle - Finalize a machine instruction bundle which includes
/// a sequence of instructions starting from FirstMI to LastMI (exclusive).
/// This routine adds a BUNDLE instruction to represent the bundle, it adds
/// IsInternalRead markers to MachineOperands which are defined inside the
/// bundle, and it copies externally visible defs and uses to the BUNDLE
/// instruction.
void llvm::finalizeBundle(MachineBasicBlock &MBB,
                          MachineBasicBlock::instr_iterator FirstMI,
                          MachineBasicBlock::instr_iterator LastMI) {
  assert(FirstMI != LastMI && "Empty bundle?");
  MIBundleBuilder Bundle(MBB, FirstMI, LastMI);

  const TargetMachine &TM = MBB.getParent()->getTarget();
  const TargetInstrInfo *TII = TM.getSubtargetImpl()->getInstrInfo();
  const TargetRegisterInfo *TRI = TM.getSubtargetImpl()->getRegisterInfo();

  MachineInstrBuilder MIB = BuildMI(*MBB.getParent(), FirstMI->getDebugLoc(),
                                    TII->get(TargetOpcode::BUNDLE));
  Bundle.prepend(MIB);

  SmallVector<unsigned, 32> LocalDefs;
  SmallSet<unsigned, 32> LocalDefSet;
  SmallSet<unsigned, 8> DeadDefSet;
  SmallSet<unsigned, 16> KilledDefSet;
  SmallVector<unsigned, 8> ExternUses;
  SmallSet<unsigned, 8> ExternUseSet;
  SmallSet<unsigned, 8> KilledUseSet;
  SmallSet<unsigned, 8> UndefUseSet;
  SmallVector<MachineOperand*, 4> Defs;
  for (; FirstMI != LastMI; ++FirstMI) {
    for (unsigned i = 0, e = FirstMI->getNumOperands(); i != e; ++i) {
      MachineOperand &MO = FirstMI->getOperand(i);
      if (!MO.isReg())
        continue;
      if (MO.isDef()) {
        Defs.push_back(&MO);
        continue;
      }

      unsigned Reg = MO.getReg();
      if (!Reg)
        continue;
      assert(TargetRegisterInfo::isPhysicalRegister(Reg));
      if (LocalDefSet.count(Reg)) {
        MO.setIsInternalRead();
        if (MO.isKill())
          // Internal def is now killed.
          KilledDefSet.insert(Reg);
      } else {
        if (ExternUseSet.insert(Reg)) {
          ExternUses.push_back(Reg);
          if (MO.isUndef())
            UndefUseSet.insert(Reg);
        }
        if (MO.isKill())
          // External def is now killed.
          KilledUseSet.insert(Reg);
      }
    }

    for (unsigned i = 0, e = Defs.size(); i != e; ++i) {
      MachineOperand &MO = *Defs[i];
      unsigned Reg = MO.getReg();
      if (!Reg)
        continue;

      if (LocalDefSet.insert(Reg)) {
        LocalDefs.push_back(Reg);
        if (MO.isDead()) {
          DeadDefSet.insert(Reg);
        }
      } else {
        // Re-defined inside the bundle, it's no longer killed.
        KilledDefSet.erase(Reg);
        if (!MO.isDead())
          // Previously defined but dead.
          DeadDefSet.erase(Reg);
      }

      if (!MO.isDead()) {
        for (MCSubRegIterator SubRegs(Reg, TRI); SubRegs.isValid(); ++SubRegs) {
          unsigned SubReg = *SubRegs;
          if (LocalDefSet.insert(SubReg))
            LocalDefs.push_back(SubReg);
        }
      }
    }

    Defs.clear();
  }

  SmallSet<unsigned, 32> Added;
  for (unsigned i = 0, e = LocalDefs.size(); i != e; ++i) {
    unsigned Reg = LocalDefs[i];
    if (Added.insert(Reg)) {
      // If it's not live beyond end of the bundle, mark it dead.
      bool isDead = DeadDefSet.count(Reg) || KilledDefSet.count(Reg);
      MIB.addReg(Reg, getDefRegState(true) | getDeadRegState(isDead) |
                 getImplRegState(true));
    }
  }

  for (unsigned i = 0, e = ExternUses.size(); i != e; ++i) {
    unsigned Reg = ExternUses[i];
    bool isKill = KilledUseSet.count(Reg);
    bool isUndef = UndefUseSet.count(Reg);
    MIB.addReg(Reg, getKillRegState(isKill) | getUndefRegState(isUndef) |
               getImplRegState(true));
  }
}

/// finalizeBundle - Same functionality as the previous finalizeBundle except
/// the last instruction in the bundle is not provided as an input. This is
/// used in cases where bundles are pre-determined by marking instructions
/// with 'InsideBundle' marker. It returns the MBB instruction iterator that
/// points to the end of the bundle.
MachineBasicBlock::instr_iterator
llvm::finalizeBundle(MachineBasicBlock &MBB,
                     MachineBasicBlock::instr_iterator FirstMI) {
  MachineBasicBlock::instr_iterator E = MBB.instr_end();
  MachineBasicBlock::instr_iterator LastMI = std::next(FirstMI);
  while (LastMI != E && LastMI->isInsideBundle())
    ++LastMI;
  finalizeBundle(MBB, FirstMI, LastMI);
  return LastMI;
}

/// finalizeBundles - Finalize instruction bundles in the specified
/// MachineFunction. Return true if any bundles are finalized.
bool llvm::finalizeBundles(MachineFunction &MF) {
  bool Changed = false;
  for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) {
    MachineBasicBlock &MBB = *I;
    MachineBasicBlock::instr_iterator MII = MBB.instr_begin();
    MachineBasicBlock::instr_iterator MIE = MBB.instr_end();
    if (MII == MIE)
      continue;
    assert(!MII->isInsideBundle() &&
           "First instr cannot be inside bundle before finalization!");

    for (++MII; MII != MIE; ) {
      if (!MII->isInsideBundle())
        ++MII;
      else {
        MII = finalizeBundle(MBB, std::prev(MII));
        Changed = true;
      }
    }
  }

  return Changed;
}

//===----------------------------------------------------------------------===//
// MachineOperand iterator
//===----------------------------------------------------------------------===//

MachineOperandIteratorBase::VirtRegInfo
MachineOperandIteratorBase::analyzeVirtReg(unsigned Reg,
                    SmallVectorImpl<std::pair<MachineInstr*, unsigned> > *Ops) {
  VirtRegInfo RI = { false, false, false };
  for(; isValid(); ++*this) {
    MachineOperand &MO = deref();
    if (!MO.isReg() || MO.getReg() != Reg)
      continue;

    // Remember each (MI, OpNo) that refers to Reg.
    if (Ops)
      Ops->push_back(std::make_pair(MO.getParent(), getOperandNo()));

    // Both defs and uses can read virtual registers.
    if (MO.readsReg()) {
      RI.Reads = true;
      if (MO.isDef())
        RI.Tied = true;
    }

    // Only defs can write.
    if (MO.isDef())
      RI.Writes = true;
    else if (!RI.Tied && MO.getParent()->isRegTiedToDefOperand(getOperandNo()))
      RI.Tied = true;
  }
  return RI;
}

MachineOperandIteratorBase::PhysRegInfo
MachineOperandIteratorBase::analyzePhysReg(unsigned Reg,
                                           const TargetRegisterInfo *TRI) {
  bool AllDefsDead = true;
  PhysRegInfo PRI = {false, false, false, false, false, false};

  assert(TargetRegisterInfo::isPhysicalRegister(Reg) &&
         "analyzePhysReg not given a physical register!");
  for (; isValid(); ++*this) {
    MachineOperand &MO = deref();

    if (MO.isRegMask() && MO.clobbersPhysReg(Reg))
      PRI.Clobbers = true;    // Regmask clobbers Reg.

    if (!MO.isReg())
      continue;

    unsigned MOReg = MO.getReg();
    if (!MOReg || !TargetRegisterInfo::isPhysicalRegister(MOReg))
      continue;

    bool IsRegOrSuperReg = MOReg == Reg || TRI->isSubRegister(MOReg, Reg);
    bool IsRegOrOverlapping = MOReg == Reg || TRI->regsOverlap(MOReg, Reg);

    if (IsRegOrSuperReg && MO.readsReg()) {
      // Reg or a super-reg is read, and perhaps killed also.
      PRI.Reads = true;
      PRI.Kills = MO.isKill();
    }

    if (IsRegOrOverlapping && MO.readsReg()) {
      PRI.ReadsOverlap = true;// Reg or an overlapping register is read.
    }

    if (!MO.isDef())
      continue;

    if (IsRegOrSuperReg) {
      PRI.Defines = true;     // Reg or a super-register is defined.
      if (!MO.isDead())
        AllDefsDead = false;
    }
    if (IsRegOrOverlapping)
      PRI.Clobbers = true;    // Reg or an overlapping reg is defined.
  }

  if (AllDefsDead && PRI.Defines)
    PRI.DefinesDead = true;   // Reg or super-register was defined and was dead.

  return PRI;
}
<commit_msg>Have MachineInstrBundle use the MachineFunction for subtarget access rather than the TargetMachine.<commit_after>//===-- lib/CodeGen/MachineInstrBundle.cpp --------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/CodeGen/MachineInstrBundle.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/Target/TargetSubtargetInfo.h"
using namespace llvm;

namespace {
  class UnpackMachineBundles : public MachineFunctionPass {
  public:
    static char ID; // Pass identification
    UnpackMachineBundles() : MachineFunctionPass(ID) {
      initializeUnpackMachineBundlesPass(*PassRegistry::getPassRegistry());
    }

    bool runOnMachineFunction(MachineFunction &MF) override;
  };
} // end anonymous namespace

char UnpackMachineBundles::ID = 0;
char &llvm::UnpackMachineBundlesID = UnpackMachineBundles::ID;
INITIALIZE_PASS(UnpackMachineBundles, "unpack-mi-bundles",
                "Unpack machine instruction bundles", false, false)

bool UnpackMachineBundles::runOnMachineFunction(MachineFunction &MF) {
  bool Changed = false;
  for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) {
    MachineBasicBlock *MBB = &*I;

    for (MachineBasicBlock::instr_iterator MII = MBB->instr_begin(),
           MIE = MBB->instr_end(); MII != MIE; ) {
      MachineInstr *MI = &*MII;

      // Remove BUNDLE instruction and the InsideBundle flags from bundled
      // instructions.
      if (MI->isBundle()) {
        while (++MII != MIE && MII->isBundledWithPred()) {
          MII->unbundleFromPred();
          for (unsigned i = 0, e = MII->getNumOperands(); i != e; ++i) {
            MachineOperand &MO = MII->getOperand(i);
            if (MO.isReg() && MO.isInternalRead())
              MO.setIsInternalRead(false);
          }
        }
        MI->eraseFromParent();

        Changed = true;
        continue;
      }

      ++MII;
    }
  }

  return Changed;
}

namespace {
  class FinalizeMachineBundles : public MachineFunctionPass {
  public:
    static char ID; // Pass identification
    FinalizeMachineBundles() : MachineFunctionPass(ID) {
      initializeFinalizeMachineBundlesPass(*PassRegistry::getPassRegistry());
    }

    bool runOnMachineFunction(MachineFunction &MF) override;
  };
} // end anonymous namespace

char FinalizeMachineBundles::ID = 0;
char &llvm::FinalizeMachineBundlesID = FinalizeMachineBundles::ID;
INITIALIZE_PASS(FinalizeMachineBundles, "finalize-mi-bundles",
                "Finalize machine instruction bundles", false, false)

bool FinalizeMachineBundles::runOnMachineFunction(MachineFunction &MF) {
  return llvm::finalizeBundles(MF);
}

/// finalizeBundle - Finalize a machine instruction bundle which includes
/// a sequence of instructions starting from FirstMI to LastMI (exclusive).
/// This routine adds a BUNDLE instruction to represent the bundle, it adds
/// IsInternalRead markers to MachineOperands which are defined inside the
/// bundle, and it copies externally visible defs and uses to the BUNDLE
/// instruction.
void llvm::finalizeBundle(MachineBasicBlock &MBB,
                          MachineBasicBlock::instr_iterator FirstMI,
                          MachineBasicBlock::instr_iterator LastMI) {
  assert(FirstMI != LastMI && "Empty bundle?");
  MIBundleBuilder Bundle(MBB, FirstMI, LastMI);

  MachineFunction &MF = *MBB.getParent();
  const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
  const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();

  MachineInstrBuilder MIB =
      BuildMI(MF, FirstMI->getDebugLoc(), TII->get(TargetOpcode::BUNDLE));
  Bundle.prepend(MIB);

  SmallVector<unsigned, 32> LocalDefs;
  SmallSet<unsigned, 32> LocalDefSet;
  SmallSet<unsigned, 8> DeadDefSet;
  SmallSet<unsigned, 16> KilledDefSet;
  SmallVector<unsigned, 8> ExternUses;
  SmallSet<unsigned, 8> ExternUseSet;
  SmallSet<unsigned, 8> KilledUseSet;
  SmallSet<unsigned, 8> UndefUseSet;
  SmallVector<MachineOperand*, 4> Defs;
  for (; FirstMI != LastMI; ++FirstMI) {
    for (unsigned i = 0, e = FirstMI->getNumOperands(); i != e; ++i) {
      MachineOperand &MO = FirstMI->getOperand(i);
      if (!MO.isReg())
        continue;
      if (MO.isDef()) {
        Defs.push_back(&MO);
        continue;
      }

      unsigned Reg = MO.getReg();
      if (!Reg)
        continue;
      assert(TargetRegisterInfo::isPhysicalRegister(Reg));
      if (LocalDefSet.count(Reg)) {
        MO.setIsInternalRead();
        if (MO.isKill())
          // Internal def is now killed.
          KilledDefSet.insert(Reg);
      } else {
        if (ExternUseSet.insert(Reg)) {
          ExternUses.push_back(Reg);
          if (MO.isUndef())
            UndefUseSet.insert(Reg);
        }
        if (MO.isKill())
          // External def is now killed.
          KilledUseSet.insert(Reg);
      }
    }

    for (unsigned i = 0, e = Defs.size(); i != e; ++i) {
      MachineOperand &MO = *Defs[i];
      unsigned Reg = MO.getReg();
      if (!Reg)
        continue;

      if (LocalDefSet.insert(Reg)) {
        LocalDefs.push_back(Reg);
        if (MO.isDead()) {
          DeadDefSet.insert(Reg);
        }
      } else {
        // Re-defined inside the bundle, it's no longer killed.
        KilledDefSet.erase(Reg);
        if (!MO.isDead())
          // Previously defined but dead.
          DeadDefSet.erase(Reg);
      }

      if (!MO.isDead()) {
        for (MCSubRegIterator SubRegs(Reg, TRI); SubRegs.isValid(); ++SubRegs) {
          unsigned SubReg = *SubRegs;
          if (LocalDefSet.insert(SubReg))
            LocalDefs.push_back(SubReg);
        }
      }
    }

    Defs.clear();
  }

  SmallSet<unsigned, 32> Added;
  for (unsigned i = 0, e = LocalDefs.size(); i != e; ++i) {
    unsigned Reg = LocalDefs[i];
    if (Added.insert(Reg)) {
      // If it's not live beyond end of the bundle, mark it dead.
      bool isDead = DeadDefSet.count(Reg) || KilledDefSet.count(Reg);
      MIB.addReg(Reg, getDefRegState(true) | getDeadRegState(isDead) |
                 getImplRegState(true));
    }
  }

  for (unsigned i = 0, e = ExternUses.size(); i != e; ++i) {
    unsigned Reg = ExternUses[i];
    bool isKill = KilledUseSet.count(Reg);
    bool isUndef = UndefUseSet.count(Reg);
    MIB.addReg(Reg, getKillRegState(isKill) | getUndefRegState(isUndef) |
               getImplRegState(true));
  }
}

/// finalizeBundle - Same functionality as the previous finalizeBundle except
/// the last instruction in the bundle is not provided as an input. This is
/// used in cases where bundles are pre-determined by marking instructions
/// with 'InsideBundle' marker. It returns the MBB instruction iterator that
/// points to the end of the bundle.
MachineBasicBlock::instr_iterator
llvm::finalizeBundle(MachineBasicBlock &MBB,
                     MachineBasicBlock::instr_iterator FirstMI) {
  MachineBasicBlock::instr_iterator E = MBB.instr_end();
  MachineBasicBlock::instr_iterator LastMI = std::next(FirstMI);
  while (LastMI != E && LastMI->isInsideBundle())
    ++LastMI;
  finalizeBundle(MBB, FirstMI, LastMI);
  return LastMI;
}

/// finalizeBundles - Finalize instruction bundles in the specified
/// MachineFunction. Return true if any bundles are finalized.
bool llvm::finalizeBundles(MachineFunction &MF) {
  bool Changed = false;
  for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) {
    MachineBasicBlock &MBB = *I;
    MachineBasicBlock::instr_iterator MII = MBB.instr_begin();
    MachineBasicBlock::instr_iterator MIE = MBB.instr_end();
    if (MII == MIE)
      continue;
    assert(!MII->isInsideBundle() &&
           "First instr cannot be inside bundle before finalization!");

    for (++MII; MII != MIE; ) {
      if (!MII->isInsideBundle())
        ++MII;
      else {
        MII = finalizeBundle(MBB, std::prev(MII));
        Changed = true;
      }
    }
  }

  return Changed;
}

//===----------------------------------------------------------------------===//
// MachineOperand iterator
//===----------------------------------------------------------------------===//

MachineOperandIteratorBase::VirtRegInfo
MachineOperandIteratorBase::analyzeVirtReg(unsigned Reg,
                    SmallVectorImpl<std::pair<MachineInstr*, unsigned> > *Ops) {
  VirtRegInfo RI = { false, false, false };
  for(; isValid(); ++*this) {
    MachineOperand &MO = deref();
    if (!MO.isReg() || MO.getReg() != Reg)
      continue;

    // Remember each (MI, OpNo) that refers to Reg.
    if (Ops)
      Ops->push_back(std::make_pair(MO.getParent(), getOperandNo()));

    // Both defs and uses can read virtual registers.
    if (MO.readsReg()) {
      RI.Reads = true;
      if (MO.isDef())
        RI.Tied = true;
    }

    // Only defs can write.
    if (MO.isDef())
      RI.Writes = true;
    else if (!RI.Tied && MO.getParent()->isRegTiedToDefOperand(getOperandNo()))
      RI.Tied = true;
  }
  return RI;
}

MachineOperandIteratorBase::PhysRegInfo
MachineOperandIteratorBase::analyzePhysReg(unsigned Reg,
                                           const TargetRegisterInfo *TRI) {
  bool AllDefsDead = true;
  PhysRegInfo PRI = {false, false, false, false, false, false};

  assert(TargetRegisterInfo::isPhysicalRegister(Reg) &&
         "analyzePhysReg not given a physical register!");
  for (; isValid(); ++*this) {
    MachineOperand &MO = deref();

    if (MO.isRegMask() && MO.clobbersPhysReg(Reg))
      PRI.Clobbers = true;    // Regmask clobbers Reg.

    if (!MO.isReg())
      continue;

    unsigned MOReg = MO.getReg();
    if (!MOReg || !TargetRegisterInfo::isPhysicalRegister(MOReg))
      continue;

    bool IsRegOrSuperReg = MOReg == Reg || TRI->isSubRegister(MOReg, Reg);
    bool IsRegOrOverlapping = MOReg == Reg || TRI->regsOverlap(MOReg, Reg);

    if (IsRegOrSuperReg && MO.readsReg()) {
      // Reg or a super-reg is read, and perhaps killed also.
      PRI.Reads = true;
      PRI.Kills = MO.isKill();
    }

    if (IsRegOrOverlapping && MO.readsReg()) {
      PRI.ReadsOverlap = true;// Reg or an overlapping register is read.
    }

    if (!MO.isDef())
      continue;

    if (IsRegOrSuperReg) {
      PRI.Defines = true;     // Reg or a super-register is defined.
      if (!MO.isDead())
        AllDefsDead = false;
    }
    if (IsRegOrOverlapping)
      PRI.Clobbers = true;    // Reg or an overlapping reg is defined.
  }

  if (AllDefsDead && PRI.Defines)
    PRI.DefinesDead = true;   // Reg or super-register was defined and was dead.

  return PRI;
}
<|endoftext|>


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

#include <string>
#include <system_error>
#include "geopm_error.h"

namespace geopm
{
    int exception_handler(std::exception_ptr eptr);
    class Exception: public std::runtime_error
    {
        public:
            Exception();
            Exception(int err);
            Exception(const std::string &what, int err);
            Exception(int err, const char *file, int line);
            Exception(const std::string &what, int err, const char *file, int line);
            virtual ~Exception();
            int err_value(void) const;
        private:
            int m_err;
    };
}

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

#include <string>
#include <system_error>
#include "geopm_error.h"

namespace geopm
{
    /// @brief Handle a thrown exception and return an error value.
    ///
    /// This exception handler is used by every geopm C interface to
    /// handle any exceptions that are thrown during execution of a
    /// C++ implementation.  If geopm has been configured with
    /// debugging enabled, then this handler will print an explanatory
    /// message to standard error.  In all cases it will convert the
    /// C++ exception into an error number which can be used with
    /// geopm_error_message() to obtain an error message.  Note that
    /// the error message printed when debugging is enabled has more
    /// specific information than the message produced by
    /// geopm_error_message().
    ///
    /// @param [in] eptr A pointer to a thrown exception such as
    ///        std::current_exception().
    ///
    /// @return Error number, positive numbers are system errors,
    ///         negative numbers are geopm errors.
    int exception_handler(std::exception_ptr eptr);

    /// @brief Class for all geopm specific exceptions.
    ///
    /// All exceptions explicitly thrown by the geopm library will be
    /// of this type.  It derives from std::runtime_error and adds one
    /// method called err_value() which returns the error code
    /// associated with the exception.  There are a number of
    /// different constructors.
    class Exception: public std::runtime_error
    {
        public:
            /// @brief Empty constructor.
            ///
            /// Uses errno to determine the error code.  Enables an
            /// abbreviated what() result.  If errno is zero then
            /// GEOPM_ERROR_RUNTIME (-1) is used for the error code.
            Exception();
            /// @brief Error number only constructor.
            ///
            /// User provides error code.  Enables an abbreviated
            /// what() result.
            ///
            /// @param [in] err Error code, positive values are system
            ///        errors (see errno(3)), negative values are
            ///        geopm errors.  If zero is specified
            ///        GEOPM_ERROR_RUNTIME (-1) is assumed.
            Exception(int err);
            /// @brief Message and error number constructor.
            ///
            /// User provides message and error code.  The error
            /// message provided is appended to the abbreviated what()
            /// result.
            ///
            /// @param [in] what Extension to error message returned
            ///        by what() method.
            ///
            /// @param [in] err Error code, positive values are system
            ///        errors (see errno(3)), negative values are
            ///        geopm errors.  If zero is specified
            ///        GEOPM_ERROR_RUNTIME (-1) is assumed.
            Exception(const std::string &what, int err);
            /// @brief Error number and line number constructor.
            ///
            /// User provides error code, file name, and line number.
            /// The what() method produces the abbreviated message
            /// with the file and line number information appended.
            ///
            /// @param [in] err Error code, positive values are system
            ///        errors (see errno(3)), negative values are
            ///        geopm errors.  If zero is specified
            ///        GEOPM_ERROR_RUNTIME (-1) is assumed.
            ///
            /// @param [in] file Name of source file where exception
            ///        was thrown, e.g. preprocessor __FILE__.
            ///
            /// @param [in] line Line number in source file where
            ///        exception was thrown, e.g. preprocessor
            ///        __LINE__.
            Exception(int err, const char *file, int line);
            /// @brief Message, error number, file and line
            ///        constructor.
            ///
            /// User provides message, error cede, file name and line
            /// number.  The what() method appends the user specified
            /// message, file name and line number to the abbreviated
            /// message.  This is the most verbose messaging available
            /// with the Exception class.
            ///
            /// @param [in] what Extension to error message returned
            ///        by what() method.
            ///
            /// @param [in] err Error code, positive values are system
            ///        errors (see errno(3)), negative values are
            ///        geopm errors.  If zero is specified
            ///        GEOPM_ERROR_RUNTIME (-1) is assumed.
            ///
            /// @param [in] file Name of source file where exception
            ///        was thrown, e.g. preprocessor __FILE__.
            ///
            /// @param [in] line Line number in source file where
            ///        exception was thrown, e.g. preprocessor
            ///        __LINE__.
            Exception(const std::string &what, int err, const char *file, int line);
            /// @brief Exception destructor, virtual.
            virtual ~Exception();
            /// @brief Returns the integer error code associated with
            ///        the exception.
            ///
            /// Returns the non-zero error code associated with the
            /// exception.  Negative error codes are geopm specific
            /// and documented in the geopm_error(3) man page.
            /// Positive error codes are system errors and are
            /// documented in the system errno(3) man page.  A brief
            /// description of all error codes can be obtained with
            /// the geopm_error_message(3) interface.
            int err_value(void) const;
        private:
            /// The error code associated with the exception.
            int m_err;
    };
}

#endif
<|endoftext|>


<commit_before>
#include "Network.hpp"
#include "Server.hpp"
#include "CoordinateSegmentation.hpp"
#include "Message.hpp"
#include "ServerMessageQueue.hpp"
#include "ServerMessageReceiver.hpp"
#include "Statistics.hpp"
#include "Options.hpp"

#include "Forwarder.hpp"
#include "ObjectSegmentation.hpp"
#include "OSegLookupQueue.hpp"

#include "ObjectConnection.hpp"

#include "ForwarderServiceQueue.hpp"

#include "Random.hpp"

// FIXME we shouldn't have oseg specific things here, this should be delegated
// to OSeg as necessary
#include "CBR_OSeg.pbj.hpp"

namespace CBR
{

class ForwarderServerMessageRouter : public Router<Message*> {
  public:
    ForwarderServerMessageRouter(ForwarderServiceQueue* svc_queues, ForwarderServiceQueue::ServiceID service_id)
            : mForwarderServiceQueue(svc_queues),
              mServiceID(service_id)
    {
    }

    WARN_UNUSED
    virtual bool route(Message* msg) {
        return (mForwarderServiceQueue->push(mServiceID, msg) == QueueEnum::PushSucceeded);
    }

  private:
    ForwarderServiceQueue* mForwarderServiceQueue;
    ForwarderServiceQueue::ServiceID mServiceID;
};

bool AlwaysPush(const UUID&, size_t cursize , size_t totsize) {return true;}

// -- First, all the boring boiler plate type stuff; initialization,
// -- destruction, delegation to base classes

  /*
    Constructor for Forwarder
  */
Forwarder::Forwarder(SpaceContext* ctx)
        :    mContext(ctx),
             mOutgoingMessages(NULL),
             mServerMessageQueue(NULL),
             mServerMessageReceiver(NULL),
             mOSegLookups(NULL),
             mUniqueConnIDs(0),
             mServiceIDSource(0)
{
    mOutgoingMessages = new ForwarderServiceQueue(mContext->id(), 16384, (ForwarderServiceQueue::Listener*)this);

    // Fill in the rest of the context
    mContext->mServerRouter = this;
    mContext->mObjectRouter = this;
    mContext->mServerDispatcher = this;
    mContext->mObjectDispatcher = this;

    mSSTDatagramLayer = BaseDatagramLayer<UUID>::createDatagramLayer(UUID::null(), this, this);

    // Messages destined for objects are subscribed to here so we can easily pick them
    // out and decide whether they can be delivered directly or need forwarding
    this->registerMessageRecipient(SERVER_PORT_OBJECT_MESSAGE_ROUTING, this);

    // Generate router queues for services we provide
    mObjectMessageRouter = createServerMessageService("object-messages");
    mOSegCacheUpdateRouter = createServerMessageService("oseg-cache-update");
}

  //Don't need to do anything special for destructor
  Forwarder::~Forwarder()
  {
      delete mObjectMessageRouter;
      delete mOSegCacheUpdateRouter;

      this->unregisterMessageRecipient(SERVER_PORT_OBJECT_MESSAGE_ROUTING, this);
  }

  /*
    Assigning time and mObjects, which should have been constructed in Server's constructor.
  */
void Forwarder::initialize(ObjectSegmentation* oseg, ServerMessageQueue* smq, ServerMessageReceiver* smr, uint32 oseg_lookup_queue_size)
  {
    mOSegLookups = new OSegLookupQueue(mContext->mainStrand, oseg, &AlwaysPush, oseg_lookup_queue_size);
    mServerMessageQueue = smq;
    mServerMessageReceiver = smr;
  }

void Forwarder::dispatchMessage(Message*msg) const {
    mContext->trace()->serverDatagramReceived(mContext->time, mContext->time, msg->source_server(), msg->id(), msg->serializedSize());

    ServerMessageDispatcher::dispatchMessage(msg);
}

void Forwarder::dispatchMessage(const CBR::Protocol::Object::ObjectMessage&msg) const {
    ObjectMessageDispatcher::dispatchMessage(msg);
}

// -- Object Connection Management - Object connections are available locally,
// -- and represent direct connections to endpoints.

void Forwarder::addObjectConnection(const UUID& dest_obj, ObjectConnection* conn) {
  UniqueObjConn uoc;
  uoc.id = mUniqueConnIDs;
  uoc.conn = conn;
  ++mUniqueConnIDs;

  mObjectConnections[dest_obj] = uoc;
}

void Forwarder::enableObjectConnection(const UUID& dest_obj) {
    ObjectConnection* conn = getObjectConnection(dest_obj);
    if (conn == NULL) {
        SILOG(forwarder,warn,"Tried to enable connection for unknown object.");
        return;
    }

    conn->enable();
}

ObjectConnection* Forwarder::removeObjectConnection(const UUID& dest_obj) {
    //    ObjectConnection* conn = mObjectConnections[dest_obj];
    UniqueObjConn uoc = mObjectConnections[dest_obj];
    ObjectConnection* conn = uoc.conn;
    mObjectConnections.erase(dest_obj);
    return conn;
}

ObjectConnection* Forwarder::getObjectConnection(const UUID& dest_obj) {
    ObjectConnectionMap::iterator it = mObjectConnections.find(dest_obj);
    return (it == mObjectConnections.end()) ? NULL : it->second.conn;
}

ObjectConnection* Forwarder::getObjectConnection(const UUID& dest_obj, uint64& ider )
{
    ObjectConnectionMap::iterator it = mObjectConnections.find(dest_obj);
    if (it == mObjectConnections.end())
    {
      ider = 0;
      return NULL;
    }
    ider = it->second.id;
    return it->second.conn;
}

// -- Server message handling.  These methods handle server to server messages,
// -- where the payload is either an already serialized ODP message, meaning its
// -- information isn't available, or may simply be between two space servers so
// -- that object information doesn't even exist.

Router<Message*>* Forwarder::createServerMessageService(const String& name) {
    ServiceMap::iterator it = mServiceIDMap.find(name);
    assert(it == mServiceIDMap.end());

    ForwarderServiceQueue::ServiceID svc_id = mServiceIDSource++;
    mServiceIDMap[name] = svc_id;
    return new ForwarderServerMessageRouter(mOutgoingMessages, svc_id);
}

void Forwarder::forwarderServiceMessageReady(ServerID dest_server) {
    mServerMessageQueue->messageReady(dest_server);
}

// -- Object Message Entry Points - entry points into the forwarder for object
// -- messages.  Sources include object hosts and other space servers.

// --- From object hosts
void Forwarder::routeObjectHostMessage(CBR::Protocol::Object::ObjectMessage* obj_msg) {
    // Messages destined for the space skip the object message queue and just get dispatched
    if (obj_msg->dest_object() == UUID::null()) {
        dispatchMessage(*obj_msg);
        delete obj_msg;
        return;
    }

    bool forwarded = forward(obj_msg);
    if (!forwarded) {
        TIMESTAMP(obj_msg, Trace::DROPPED_DURING_FORWARDING);
        delete obj_msg;
    }
}

// --- From local space server services
bool Forwarder::route(CBR::Protocol::Object::ObjectMessage* msg) {
    return forward(msg, NullServerID);
}

// --- Forwarded from other space servers
void Forwarder::receiveMessage(Message* msg) {
    // Forwarder only subscribes as a recipient for object messages
    // so it can easily check whether it can deliver directly
    // or needs to forward them.
    assert(msg->dest_port() == SERVER_PORT_OBJECT_MESSAGE_ROUTING);

    CBR::Protocol::Object::ObjectMessage* obj_msg = new CBR::Protocol::Object::ObjectMessage();
    bool parsed = parsePBJMessage(obj_msg, msg->payload());
    assert(parsed);

    TIMESTAMP(obj_msg, Trace::HANDLE_SPACE_MESSAGE);

    UUID dest = obj_msg->dest_object();

    // Special case: Object 0 is the space itself
    if (dest == UUID::null()) {
        dispatchMessage(*obj_msg);
        delete obj_msg;
        return;
    }

    // Otherwise, try to forward it
    bool forward_success = forward(obj_msg, msg->source_server());

    if (!forward_success) {
        TIMESTAMP(obj_msg, Trace::DROPPED_DURING_FORWARDING);
        delete obj_msg;
    }

    delete msg;
}

// -- Real Routing - Given an object message, from any source, decide where it
// -- needs to go and send it out in that direction.

bool Forwarder::forward(CBR::Protocol::Object::ObjectMessage* msg, ServerID forwardFrom)
{
    UUID dest_obj = msg->dest_object();

    TIMESTAMP_START(tstamp, msg);
    TIMESTAMP_END(tstamp, Trace::FORWARDING_STARTED);

    // Check if we can forward locally
    ObjectConnection* conn = getObjectConnection(msg->dest_object());
    if (conn != NULL) {
        TIMESTAMP_END(tstamp, Trace::FORWARDED_LOCALLY_SLOW_PATH);

        bool send_success = false;
        if (conn->enabled())
            send_success = conn->send(msg);

        return send_success;
    }

    // If we can't forward locally, do an OSeg lookup to find out where we need
    // to forward the message to
    TIMESTAMP_END(tstamp, Trace::OSEG_LOOKUP_STARTED);

    bool accepted = mOSegLookups->lookup(
        msg,
        boost::bind(&Forwarder::routeObjectMessageToServer, this, _1, _2, _3, forwardFrom)
                                         );

    return accepted;
}

bool Forwarder::routeObjectMessageToServer(CBR::Protocol::Object::ObjectMessage* obj_msg, ServerID dest_serv, OSegLookupQueue::ResolvedFrom resolved_from, ServerID forwardFrom)
{
    Trace::MessagePath mp = (resolved_from == OSegLookupQueue::ResolvedFromCache)
        ? Trace::OSEG_CACHE_LOOKUP_FINISHED
        : Trace::OSEG_SERVER_LOOKUP_FINISHED;

    // Timestamp the message as having finished an OSeg Lookup
    TIMESTAMP(obj_msg, mp);
    TIMESTAMP(obj_msg, Trace::OSEG_LOOKUP_FINISHED);

  //send out all server updates associated with an object with this message:
  UUID obj_id =  obj_msg->dest_object();

  Message* svr_obj_msg = new Message(
      mContext->id(),
      SERVER_PORT_OBJECT_MESSAGE_ROUTING,
      dest_serv,
      SERVER_PORT_OBJECT_MESSAGE_ROUTING,
      obj_msg
  );

  TIMESTAMP(obj_msg, Trace::SPACE_TO_SPACE_ENQUEUED);

  bool send_success = mObjectMessageRouter->route(svr_obj_msg);
  if (!send_success) {
      delete svr_obj_msg;
      TIMESTAMP(obj_msg, Trace::DROPPED_AT_SPACE_ENQUEUED);
  }else {
      // timestamping handled by Message routing code
      delete obj_msg;
  }

  // Note that this is done *after* the real message is sent since it is an optimization and
  // we don't want it blocking useful traffic
  if (forwardFrom != NullServerID) {
      // FIXME we used to kind of keep track of sending the same OSeg cache fix to a server multiple
      // times, but it really only applied for the rate of lookups/migrations.  We should a) determine
      // if this is actually a problem and b) if it is, take a more principled approach to solving it.
#ifdef CRAQ_DEBUG
      std::cout<<"\n\n bftm debug Sending an oseg cache update message at time:  "<<mContext->time.raw()<<"\n";
      std::cout<<"\t for object:  "<<obj_id.toString()<<"\n";
      std::cout<<"\t to server:   "<<mServersToUpdate[obj_id][s]<<"\n";
      std::cout<<"\t obj on:      "<<dest_serv<<"\n\n";
#endif

      CBR::Protocol::OSeg::UpdateOSegMessage contents;
      contents.set_servid_sending_update(mContext->id());
      contents.set_servid_obj_on(dest_serv);
      contents.set_m_objid(obj_id);

      Message* up_os = new Message(
          mContext->id(),
          SERVER_PORT_OSEG_UPDATE,
          forwardFrom,
          SERVER_PORT_OSEG_UPDATE,
          serializePBJMessage(contents)
      );

      bool oseg_cache_msg_success = mOSegCacheUpdateRouter->route(up_os);
      // Ignore the success of this send.  If it failed the remote ends cache
      // will just continue to be incorrect, but forwarding will cover the error
  }

  return send_success;
}

Message* Forwarder::serverMessageFront(ServerID dest) {
    Message* next_msg = mOutgoingMessages->front(dest);
    return next_msg;
}

Message* Forwarder::serverMessagePull(ServerID dest) {
    Message* next_msg = mOutgoingMessages->front(dest);
    if (next_msg == NULL)
        return NULL;

    mContext->trace()->serverDatagramQueued(mContext->time, next_msg->dest_server(), next_msg->id(), next_msg->serializedSize());

    Message* pop_msg = mOutgoingMessages->pop(dest);
    assert(pop_msg == next_msg);
    return pop_msg;
}

void Forwarder::serverMessageReceived(Message* msg) {
    assert(msg != NULL);
    TIMESTAMP_PAYLOAD(msg, Trace::SPACE_TO_SPACE_SMR_DEQUEUED);
    dispatchMessage(msg);
}

} //end namespace
<commit_msg>Ensure that triggering of ServerMessageQueue by pushing onto ForwarderServiceQueue is handled in the main strand.<commit_after>
#include "Network.hpp"
#include "Server.hpp"
#include "CoordinateSegmentation.hpp"
#include "Message.hpp"
#include "ServerMessageQueue.hpp"
#include "ServerMessageReceiver.hpp"
#include "Statistics.hpp"
#include "Options.hpp"

#include "Forwarder.hpp"
#include "ObjectSegmentation.hpp"
#include "OSegLookupQueue.hpp"

#include "ObjectConnection.hpp"

#include "ForwarderServiceQueue.hpp"

#include "Random.hpp"

// FIXME we shouldn't have oseg specific things here, this should be delegated
// to OSeg as necessary
#include "CBR_OSeg.pbj.hpp"

namespace CBR
{

class ForwarderServerMessageRouter : public Router<Message*> {
  public:
    ForwarderServerMessageRouter(ForwarderServiceQueue* svc_queues, ForwarderServiceQueue::ServiceID service_id)
            : mForwarderServiceQueue(svc_queues),
              mServiceID(service_id)
    {
    }

    WARN_UNUSED
    virtual bool route(Message* msg) {
        return (mForwarderServiceQueue->push(mServiceID, msg) == QueueEnum::PushSucceeded);
    }

  private:
    ForwarderServiceQueue* mForwarderServiceQueue;
    ForwarderServiceQueue::ServiceID mServiceID;
};

bool AlwaysPush(const UUID&, size_t cursize , size_t totsize) {return true;}

// -- First, all the boring boiler plate type stuff; initialization,
// -- destruction, delegation to base classes

  /*
    Constructor for Forwarder
  */
Forwarder::Forwarder(SpaceContext* ctx)
        :    mContext(ctx),
             mOutgoingMessages(NULL),
             mServerMessageQueue(NULL),
             mServerMessageReceiver(NULL),
             mOSegLookups(NULL),
             mUniqueConnIDs(0),
             mServiceIDSource(0)
{
    mOutgoingMessages = new ForwarderServiceQueue(mContext->id(), 16384, (ForwarderServiceQueue::Listener*)this);

    // Fill in the rest of the context
    mContext->mServerRouter = this;
    mContext->mObjectRouter = this;
    mContext->mServerDispatcher = this;
    mContext->mObjectDispatcher = this;

    mSSTDatagramLayer = BaseDatagramLayer<UUID>::createDatagramLayer(UUID::null(), this, this);

    // Messages destined for objects are subscribed to here so we can easily pick them
    // out and decide whether they can be delivered directly or need forwarding
    this->registerMessageRecipient(SERVER_PORT_OBJECT_MESSAGE_ROUTING, this);

    // Generate router queues for services we provide
    mObjectMessageRouter = createServerMessageService("object-messages");
    mOSegCacheUpdateRouter = createServerMessageService("oseg-cache-update");
}

  //Don't need to do anything special for destructor
  Forwarder::~Forwarder()
  {
      delete mObjectMessageRouter;
      delete mOSegCacheUpdateRouter;

      this->unregisterMessageRecipient(SERVER_PORT_OBJECT_MESSAGE_ROUTING, this);
  }

  /*
    Assigning time and mObjects, which should have been constructed in Server's constructor.
  */
void Forwarder::initialize(ObjectSegmentation* oseg, ServerMessageQueue* smq, ServerMessageReceiver* smr, uint32 oseg_lookup_queue_size)
  {
    mOSegLookups = new OSegLookupQueue(mContext->mainStrand, oseg, &AlwaysPush, oseg_lookup_queue_size);
    mServerMessageQueue = smq;
    mServerMessageReceiver = smr;
  }

void Forwarder::dispatchMessage(Message*msg) const {
    mContext->trace()->serverDatagramReceived(mContext->time, mContext->time, msg->source_server(), msg->id(), msg->serializedSize());

    ServerMessageDispatcher::dispatchMessage(msg);
}

void Forwarder::dispatchMessage(const CBR::Protocol::Object::ObjectMessage&msg) const {
    ObjectMessageDispatcher::dispatchMessage(msg);
}

// -- Object Connection Management - Object connections are available locally,
// -- and represent direct connections to endpoints.

void Forwarder::addObjectConnection(const UUID& dest_obj, ObjectConnection* conn) {
  UniqueObjConn uoc;
  uoc.id = mUniqueConnIDs;
  uoc.conn = conn;
  ++mUniqueConnIDs;

  mObjectConnections[dest_obj] = uoc;
}

void Forwarder::enableObjectConnection(const UUID& dest_obj) {
    ObjectConnection* conn = getObjectConnection(dest_obj);
    if (conn == NULL) {
        SILOG(forwarder,warn,"Tried to enable connection for unknown object.");
        return;
    }

    conn->enable();
}

ObjectConnection* Forwarder::removeObjectConnection(const UUID& dest_obj) {
    //    ObjectConnection* conn = mObjectConnections[dest_obj];
    UniqueObjConn uoc = mObjectConnections[dest_obj];
    ObjectConnection* conn = uoc.conn;
    mObjectConnections.erase(dest_obj);
    return conn;
}

ObjectConnection* Forwarder::getObjectConnection(const UUID& dest_obj) {
    ObjectConnectionMap::iterator it = mObjectConnections.find(dest_obj);
    return (it == mObjectConnections.end()) ? NULL : it->second.conn;
}

ObjectConnection* Forwarder::getObjectConnection(const UUID& dest_obj, uint64& ider )
{
    ObjectConnectionMap::iterator it = mObjectConnections.find(dest_obj);
    if (it == mObjectConnections.end())
    {
      ider = 0;
      return NULL;
    }
    ider = it->second.id;
    return it->second.conn;
}

// -- Server message handling.  These methods handle server to server messages,
// -- where the payload is either an already serialized ODP message, meaning its
// -- information isn't available, or may simply be between two space servers so
// -- that object information doesn't even exist.

Router<Message*>* Forwarder::createServerMessageService(const String& name) {
    ServiceMap::iterator it = mServiceIDMap.find(name);
    assert(it == mServiceIDMap.end());

    ForwarderServiceQueue::ServiceID svc_id = mServiceIDSource++;
    mServiceIDMap[name] = svc_id;
    return new ForwarderServerMessageRouter(mOutgoingMessages, svc_id);
}

void Forwarder::forwarderServiceMessageReady(ServerID dest_server) {
    mContext->mainStrand->post(
        std::tr1::bind(&ServerMessageQueue::messageReady, mServerMessageQueue, dest_server)
                               );
}

// -- Object Message Entry Points - entry points into the forwarder for object
// -- messages.  Sources include object hosts and other space servers.

// --- From object hosts
void Forwarder::routeObjectHostMessage(CBR::Protocol::Object::ObjectMessage* obj_msg) {
    // Messages destined for the space skip the object message queue and just get dispatched
    if (obj_msg->dest_object() == UUID::null()) {
        dispatchMessage(*obj_msg);
        delete obj_msg;
        return;
    }

    bool forwarded = forward(obj_msg);
    if (!forwarded) {
        TIMESTAMP(obj_msg, Trace::DROPPED_DURING_FORWARDING);
        delete obj_msg;
    }
}

// --- From local space server services
bool Forwarder::route(CBR::Protocol::Object::ObjectMessage* msg) {
    return forward(msg, NullServerID);
}

// --- Forwarded from other space servers
void Forwarder::receiveMessage(Message* msg) {
    // Forwarder only subscribes as a recipient for object messages
    // so it can easily check whether it can deliver directly
    // or needs to forward them.
    assert(msg->dest_port() == SERVER_PORT_OBJECT_MESSAGE_ROUTING);

    CBR::Protocol::Object::ObjectMessage* obj_msg = new CBR::Protocol::Object::ObjectMessage();
    bool parsed = parsePBJMessage(obj_msg, msg->payload());
    assert(parsed);

    TIMESTAMP(obj_msg, Trace::HANDLE_SPACE_MESSAGE);

    UUID dest = obj_msg->dest_object();

    // Special case: Object 0 is the space itself
    if (dest == UUID::null()) {
        dispatchMessage(*obj_msg);
        delete obj_msg;
        return;
    }

    // Otherwise, try to forward it
    bool forward_success = forward(obj_msg, msg->source_server());

    if (!forward_success) {
        TIMESTAMP(obj_msg, Trace::DROPPED_DURING_FORWARDING);
        delete obj_msg;
    }

    delete msg;
}

// -- Real Routing - Given an object message, from any source, decide where it
// -- needs to go and send it out in that direction.

bool Forwarder::forward(CBR::Protocol::Object::ObjectMessage* msg, ServerID forwardFrom)
{
    UUID dest_obj = msg->dest_object();

    TIMESTAMP_START(tstamp, msg);
    TIMESTAMP_END(tstamp, Trace::FORWARDING_STARTED);

    // Check if we can forward locally
    ObjectConnection* conn = getObjectConnection(msg->dest_object());
    if (conn != NULL) {
        TIMESTAMP_END(tstamp, Trace::FORWARDED_LOCALLY_SLOW_PATH);

        bool send_success = false;
        if (conn->enabled())
            send_success = conn->send(msg);

        return send_success;
    }

    // If we can't forward locally, do an OSeg lookup to find out where we need
    // to forward the message to
    TIMESTAMP_END(tstamp, Trace::OSEG_LOOKUP_STARTED);

    bool accepted = mOSegLookups->lookup(
        msg,
        boost::bind(&Forwarder::routeObjectMessageToServer, this, _1, _2, _3, forwardFrom)
                                         );

    return accepted;
}

bool Forwarder::routeObjectMessageToServer(CBR::Protocol::Object::ObjectMessage* obj_msg, ServerID dest_serv, OSegLookupQueue::ResolvedFrom resolved_from, ServerID forwardFrom)
{
    Trace::MessagePath mp = (resolved_from == OSegLookupQueue::ResolvedFromCache)
        ? Trace::OSEG_CACHE_LOOKUP_FINISHED
        : Trace::OSEG_SERVER_LOOKUP_FINISHED;

    // Timestamp the message as having finished an OSeg Lookup
    TIMESTAMP(obj_msg, mp);
    TIMESTAMP(obj_msg, Trace::OSEG_LOOKUP_FINISHED);

  //send out all server updates associated with an object with this message:
  UUID obj_id =  obj_msg->dest_object();

  Message* svr_obj_msg = new Message(
      mContext->id(),
      SERVER_PORT_OBJECT_MESSAGE_ROUTING,
      dest_serv,
      SERVER_PORT_OBJECT_MESSAGE_ROUTING,
      obj_msg
  );

  TIMESTAMP(obj_msg, Trace::SPACE_TO_SPACE_ENQUEUED);

  bool send_success = mObjectMessageRouter->route(svr_obj_msg);
  if (!send_success) {
      delete svr_obj_msg;
      TIMESTAMP(obj_msg, Trace::DROPPED_AT_SPACE_ENQUEUED);
  }else {
      // timestamping handled by Message routing code
      delete obj_msg;
  }

  // Note that this is done *after* the real message is sent since it is an optimization and
  // we don't want it blocking useful traffic
  if (forwardFrom != NullServerID) {
      // FIXME we used to kind of keep track of sending the same OSeg cache fix to a server multiple
      // times, but it really only applied for the rate of lookups/migrations.  We should a) determine
      // if this is actually a problem and b) if it is, take a more principled approach to solving it.
#ifdef CRAQ_DEBUG
      std::cout<<"\n\n bftm debug Sending an oseg cache update message at time:  "<<mContext->time.raw()<<"\n";
      std::cout<<"\t for object:  "<<obj_id.toString()<<"\n";
      std::cout<<"\t to server:   "<<mServersToUpdate[obj_id][s]<<"\n";
      std::cout<<"\t obj on:      "<<dest_serv<<"\n\n";
#endif

      CBR::Protocol::OSeg::UpdateOSegMessage contents;
      contents.set_servid_sending_update(mContext->id());
      contents.set_servid_obj_on(dest_serv);
      contents.set_m_objid(obj_id);

      Message* up_os = new Message(
          mContext->id(),
          SERVER_PORT_OSEG_UPDATE,
          forwardFrom,
          SERVER_PORT_OSEG_UPDATE,
          serializePBJMessage(contents)
      );

      bool oseg_cache_msg_success = mOSegCacheUpdateRouter->route(up_os);
      // Ignore the success of this send.  If it failed the remote ends cache
      // will just continue to be incorrect, but forwarding will cover the error
  }

  return send_success;
}

Message* Forwarder::serverMessageFront(ServerID dest) {
    Message* next_msg = mOutgoingMessages->front(dest);
    return next_msg;
}

Message* Forwarder::serverMessagePull(ServerID dest) {
    Message* next_msg = mOutgoingMessages->front(dest);
    if (next_msg == NULL)
        return NULL;

    mContext->trace()->serverDatagramQueued(mContext->time, next_msg->dest_server(), next_msg->id(), next_msg->serializedSize());

    Message* pop_msg = mOutgoingMessages->pop(dest);
    assert(pop_msg == next_msg);
    return pop_msg;
}

void Forwarder::serverMessageReceived(Message* msg) {
    assert(msg != NULL);
    TIMESTAMP_PAYLOAD(msg, Trace::SPACE_TO_SPACE_SMR_DEQUEUED);
    dispatchMessage(msg);
}

} //end namespace
<|endoftext|>