<commit_before>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/fastos/fastos.h>
#include <vespa/log/log.h>
LOG_SETUP(".proton.server.documentretriever");

#include "documentretriever.h"
#include <vespa/document/base/field.h>
#include <vespa/document/datatype/positiondatatype.h>
#include <vespa/document/fieldvalue/structfieldvalue.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/searchcommon/common/schema.h>
#include <vespa/searchcore/proton/attribute/document_field_retriever.h>
#include <vespa/searchlib/attribute/attributeguard.h>
#include <vespa/searchlib/attribute/iattributemanager.h>
#include <vespa/searchlib/docstore/idocumentstore.h>
#include <vespa/searchlib/query/base.h>
#include <vespa/vespalib/geo/zcurve.h>

using document::Document;
using document::DocumentType;
using document::DocumentTypeRepo;
using document::PositionDataType;
using document::StructFieldValue;
using search::AttributeGuard;
using search::DocumentIdT;
using search::IAttributeManager;
using search::IDocumentStore;
using search::index::Schema;
using storage::spi::Timestamp;
using vespalib::geo::ZCurve;

namespace proton {

DocumentRetriever
::DocumentRetriever(const DocTypeName &docTypeName,
                    const DocumentTypeRepo &repo,
                    const Schema &schema,
                    const IDocumentMetaStoreContext &meta_store,
                    const IAttributeManager &attr_manager,
                    const IDocumentStore &doc_store)
    : DocumentRetrieverBase(docTypeName, repo, meta_store, true),
      _schema(schema),
      _attr_manager(attr_manager),
      _doc_store(doc_store),
      _possiblePositionFields()
{
    const DocumentType * documentType = repo.getDocumentType(docTypeName.getName());
    document::Field::Set fields = documentType->getFieldSet();
    int32_t positionDataTypeId = PositionDataType::getInstance().getId();
    LOG(debug, "checking document type '%s' for position fields", docTypeName.getName().c_str());
    for (const document::Field * field : fields) {
        if (field->getDataType().getId() == positionDataTypeId) {
            LOG(debug, "Field '%s' is a position field", field->getName().c_str());
            const vespalib::string & zcurve_name = PositionDataType::getZCurveFieldName(field->getName());
            AttributeGuard::UP attr = attr_manager.getAttribute(zcurve_name);
            if (attr) {
                LOG(debug, "Field '%s' is a registered attribute field", zcurve_name.c_str());
                _possiblePositionFields.emplace_back(field, zcurve_name);
            }
        }
    }
}

namespace {

FieldValue::UP positionFromZcurve(int64_t zcurve) {
    int32_t x, y;
    ZCurve::decode(zcurve, &x, &y);

    FieldValue::UP value = PositionDataType::getInstance().createFieldValue();
    StructFieldValue *position = static_cast<StructFieldValue *>(value.get());
    position->set(PositionDataType::FIELD_X, x);
    position->set(PositionDataType::FIELD_Y, y);
    return value;
}

void fillInPositionFields(Document &doc, DocumentIdT lid, const DocumentRetriever::PositionFields & possiblePositionFields, const IAttributeManager & attr_manager)
{
    for (const auto & it : possiblePositionFields) {
        if (doc.hasValue(*it.first)) {
            AttributeGuard::UP attr = attr_manager.getAttribute(it.second);
            if (attr.get() && attr->valid()) {
                int64_t zcurve = (*attr)->getInt(lid);
                doc.setValue(*it.first, *positionFromZcurve(zcurve));
            }
        }
    }
}

class PopulateVisitor : public search::IDocumentVisitor
{
public:
    PopulateVisitor(const DocumentRetriever & retriever, search::IDocumentVisitor & visitor) :
        _retriever(retriever),
        _visitor(visitor)
    { }
    void visit(uint32_t lid, document::Document::UP doc) override {
        if (doc) {
            _retriever.populate(lid, *doc);
            _visitor.visit(lid, std::move(doc));
        }
    }
private:
    const DocumentRetriever  & _retriever;
    search::IDocumentVisitor & _visitor;
};

}  // namespace

Document::UP DocumentRetriever::getDocument(DocumentIdT lid) const
{
    Document::UP doc = _doc_store.read(lid, getDocumentTypeRepo());
    if (doc) {
        populate(lid, *doc);
    }
    return doc;
}

void DocumentRetriever::visitDocuments(const LidVector & lids, search::IDocumentVisitor & visitor, ReadConsistency) const
{
    PopulateVisitor populater(*this, visitor);
    _doc_store.visit(lids, getDocumentTypeRepo(), populater);
}

void DocumentRetriever::populate(DocumentIdT lid, Document & doc) const
{
    for (uint32_t i = 0; i < _schema.getNumAttributeFields(); ++i) {
        const Schema::AttributeField &field = _schema.getAttributeField(i);
        AttributeGuard::UP attr = _attr_manager.getAttribute(field.getName());
        if (attr.get() && attr->valid()) {
            DocumentFieldRetriever::populate(lid, doc, field, **attr, _schema.isIndexField(field.getName()));
        }
    }
    fillInPositionFields(doc, lid, _possiblePositionFields, _attr_manager);
}

const Schema &
DocumentRetriever::getSchema(void) const
{
    return _schema;
}

const IAttributeManager *
DocumentRetriever::getAttrMgr(void) const
{
    return &_attr_manager;
}

}  // namespace proton
<commit_msg>Do not go past numDocs..<commit_after>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/fastos/fastos.h>
#include <vespa/log/log.h>
LOG_SETUP(".proton.server.documentretriever");

#include "documentretriever.h"
#include <vespa/document/base/field.h>
#include <vespa/document/datatype/positiondatatype.h>
#include <vespa/document/fieldvalue/structfieldvalue.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/searchcommon/common/schema.h>
#include <vespa/searchcore/proton/attribute/document_field_retriever.h>
#include <vespa/searchlib/attribute/attributeguard.h>
#include <vespa/searchlib/attribute/iattributemanager.h>
#include <vespa/searchlib/docstore/idocumentstore.h>
#include <vespa/searchlib/query/base.h>
#include <vespa/vespalib/geo/zcurve.h>

using document::Document;
using document::DocumentType;
using document::DocumentTypeRepo;
using document::PositionDataType;
using document::StructFieldValue;
using search::AttributeGuard;
using search::DocumentIdT;
using search::IAttributeManager;
using search::IDocumentStore;
using search::index::Schema;
using storage::spi::Timestamp;
using vespalib::geo::ZCurve;

namespace proton {

DocumentRetriever
::DocumentRetriever(const DocTypeName &docTypeName,
                    const DocumentTypeRepo &repo,
                    const Schema &schema,
                    const IDocumentMetaStoreContext &meta_store,
                    const IAttributeManager &attr_manager,
                    const IDocumentStore &doc_store)
    : DocumentRetrieverBase(docTypeName, repo, meta_store, true),
      _schema(schema),
      _attr_manager(attr_manager),
      _doc_store(doc_store),
      _possiblePositionFields()
{
    const DocumentType * documentType = repo.getDocumentType(docTypeName.getName());
    document::Field::Set fields = documentType->getFieldSet();
    int32_t positionDataTypeId = PositionDataType::getInstance().getId();
    LOG(debug, "checking document type '%s' for position fields", docTypeName.getName().c_str());
    for (const document::Field * field : fields) {
        if (field->getDataType().getId() == positionDataTypeId) {
            LOG(debug, "Field '%s' is a position field", field->getName().c_str());
            const vespalib::string & zcurve_name = PositionDataType::getZCurveFieldName(field->getName());
            AttributeGuard::UP attr = attr_manager.getAttribute(zcurve_name);
            if (attr) {
                LOG(debug, "Field '%s' is a registered attribute field", zcurve_name.c_str());
                _possiblePositionFields.emplace_back(field, zcurve_name);
            }
        }
    }
}

namespace {

FieldValue::UP positionFromZcurve(int64_t zcurve) {
    int32_t x, y;
    ZCurve::decode(zcurve, &x, &y);

    FieldValue::UP value = PositionDataType::getInstance().createFieldValue();
    StructFieldValue *position = static_cast<StructFieldValue *>(value.get());
    position->set(PositionDataType::FIELD_X, x);
    position->set(PositionDataType::FIELD_Y, y);
    return value;
}

void fillInPositionFields(Document &doc, DocumentIdT lid, const DocumentRetriever::PositionFields & possiblePositionFields, const IAttributeManager & attr_manager)
{
    for (const auto & it : possiblePositionFields) {
        if (doc.hasValue(*it.first)) {
            AttributeGuard::UP attr = attr_manager.getAttribute(it.second);
            if (attr.get() && attr->valid()) {
                int64_t zcurve = (*attr)->getInt(lid);
                doc.setValue(*it.first, *positionFromZcurve(zcurve));
            }
        }
    }
}

class PopulateVisitor : public search::IDocumentVisitor
{
public:
    PopulateVisitor(const DocumentRetriever & retriever, search::IDocumentVisitor & visitor) :
        _retriever(retriever),
        _visitor(visitor)
    { }
    void visit(uint32_t lid, document::Document::UP doc) override {
        if (doc) {
            _retriever.populate(lid, *doc);
            _visitor.visit(lid, std::move(doc));
        }
    }
private:
    const DocumentRetriever  & _retriever;
    search::IDocumentVisitor & _visitor;
};

}  // namespace

Document::UP DocumentRetriever::getDocument(DocumentIdT lid) const
{
    Document::UP doc = _doc_store.read(lid, getDocumentTypeRepo());
    if (doc) {
        populate(lid, *doc);
    }
    return doc;
}

void DocumentRetriever::visitDocuments(const LidVector & lids, search::IDocumentVisitor & visitor, ReadConsistency) const
{
    PopulateVisitor populater(*this, visitor);
    _doc_store.visit(lids, getDocumentTypeRepo(), populater);
}

void DocumentRetriever::populate(DocumentIdT lid, Document & doc) const
{
    for (uint32_t i = 0; i < _schema.getNumAttributeFields(); ++i) {
        const Schema::AttributeField &field = _schema.getAttributeField(i);
        AttributeGuard::UP attrGuard = _attr_manager.getAttribute(field.getName());
        if (attrGuard.get() && attrGuard->valid()) {
            const search::attribute::IAttributeVector & attr = **attrGuard;
            if (lid < attr.getNumDocs()) {
                DocumentFieldRetriever::populate(lid, doc, field, attr, _schema.isIndexField(field.getName()));
            }
        }
    }
    fillInPositionFields(doc, lid, _possiblePositionFields, _attr_manager);
}

const Schema &
DocumentRetriever::getSchema(void) const
{
    return _schema;
}

const IAttributeManager *
DocumentRetriever::getAttrMgr(void) const
{
    return &_attr_manager;
}

}  // namespace proton
<|endoftext|>


<commit_before>/*
	Copyright 2022 Jordi SUBIRANA

	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 <Atema/VulkanRenderer/VulkanRenderWindow.hpp>
#include <Atema/VulkanRenderer/VulkanDevice.hpp>
#include <Atema/VulkanRenderer/VulkanFramebuffer.hpp>
#include <Atema/VulkanRenderer/VulkanImage.hpp>
#include <Atema/VulkanRenderer/VulkanInstance.hpp>
#include <Atema/VulkanRenderer/VulkanPhysicalDevice.hpp>
#include <Atema/VulkanRenderer/VulkanRenderFrame.hpp>
#include <Atema/VulkanRenderer/VulkanRenderPass.hpp>
#include <Atema/VulkanRenderer/VulkanSwapChain.hpp>

using namespace at;

VulkanRenderWindow::VulkanRenderWindow(VulkanDevice& device, const RenderWindow::Settings& settings) :
	RenderWindow(settings),
	m_device(device),
	m_colorFormat(settings.colorFormat),
	m_depthFormat(settings.depthFormat),
	m_surface(VK_NULL_HANDLE),
	m_currentSwapChainImage(0),
	m_recreateSwapChain(false),
	m_currentFrameIndex(0),
	m_frameCount(settings.maxFramesInFlight)
{
	createSurface();

	initializePresentQueue();

	createRenderPass();

	createSwapChainResources();
}

VulkanRenderWindow::~VulkanRenderWindow()
{
	destroySwapChainResources();

	destroySurface();
}

VulkanDevice& VulkanRenderWindow::getDevice() noexcept
{
	return m_device;
}

ImageFormat VulkanRenderWindow::getColorFormat() const noexcept
{
	return m_colorFormat;
}

ImageFormat VulkanRenderWindow::getDepthFormat() const noexcept
{
	return m_depthFormat;
}

size_t VulkanRenderWindow::getMaxFramesInFlight() const noexcept
{
	return m_frameCount;
}

RenderFrame& VulkanRenderWindow::acquireFrame()
{
	if (m_recreateSwapChain)
	{
		createSwapChainResources();

		m_recreateSwapChain = false;
	}

	auto& renderFrame = *m_renderFrames[m_currentFrameIndex];

	renderFrame.wait();

	const auto acquireResult = m_swapChain->acquireNextImage(m_currentSwapChainImage, renderFrame.getImageAvailableSemaphoreHandle(), VK_NULL_HANDLE);

	// Out of date : recreate swapchain right now
	if (acquireResult == SwapChainResult::OutOfDate)
	{
		m_recreateSwapChain = true;

		return acquireFrame();
	}
	// Suboptimal : recreate next frame, we can still use it
	else if (acquireResult == SwapChainResult::Suboptimal)
	{
		m_recreateSwapChain = true;
	}
	// Impossible to acquire
	else if (acquireResult != SwapChainResult::Success)
	{
		ATEMA_ERROR("Failed to acquire a valid swapchain image");
	}

	// Check if a previous frame is using this image (i.e. there is its fence to wait on)
	if (m_imageFences[m_currentSwapChainImage])
	{
		m_imageFences[m_currentSwapChainImage]->wait();
	}

	// Mark the image as now being in use by this frame
	m_imageFences[m_currentSwapChainImage] = renderFrame.getFence();

	renderFrame.setImageIndex(m_currentSwapChainImage);

	m_currentFrameIndex = (m_currentFrameIndex + 1) % m_frameCount;

	return renderFrame;
}

VkSurfaceKHR VulkanRenderWindow::getSurface() const noexcept
{
	return m_surface;
}

uint32_t VulkanRenderWindow::getPresentQueueFamilyIndex() const noexcept
{
	return m_presentQueueFamilyIndex;
}

Ptr<RenderPass> VulkanRenderWindow::getRenderPass() const noexcept
{
	return m_renderPass;
}

Ptr<Framebuffer> VulkanRenderWindow::getFramebuffer(size_t imageIndex) const noexcept
{
	return m_framebuffers[imageIndex];
}

void VulkanRenderWindow::present(const VulkanRenderFrame& renderFrame)
{
	auto vkSemaphore = std::static_pointer_cast<VulkanSemaphore>(renderFrame.getRenderFinishedSemaphore())->getHandle();

	std::vector<VkSemaphore> vkSemaphores = { vkSemaphore };

	VkPresentInfoKHR presentInfo{};

	presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
	// Which semaphores to wait on before presentation can happen
	presentInfo.waitSemaphoreCount = static_cast<uint32_t>(vkSemaphores.size());
	presentInfo.pWaitSemaphores = vkSemaphores.data();

	auto vkSwapChain = std::static_pointer_cast<VulkanSwapChain>(m_swapChain);

	VkSwapchainKHR swapChains[] = { vkSwapChain->getHandle() };
	auto imageIndex = renderFrame.getImageIndex();

	presentInfo.swapchainCount = 1;
	presentInfo.pSwapchains = swapChains;
	presentInfo.pImageIndices = &imageIndex;
	// Array of VkResult values to check for every individual swap chain if presentation was successful
	//presentInfo.pResults = nullptr; // Optional

	const auto result = m_device.vkQueuePresentKHR(m_presentQueue, &presentInfo);

	const auto swapChainResult = Vulkan::getSwapChainResult(result);

	if (swapChainResult == SwapChainResult::OutOfDate || swapChainResult == SwapChainResult::Suboptimal)
	{
		m_recreateSwapChain = true;
	}
	else if (swapChainResult != SwapChainResult::Success)
	{
		ATEMA_ERROR("SwapChain presentation failed");
	}
}

void VulkanRenderWindow::onResize(const Vector2u& newSize)
{
	m_recreateSwapChain = true;

	RenderWindow::onResized(newSize);
}

void VulkanRenderWindow::createSurface()
{
	const auto& instance = m_device.getInstance();

	// Ensure the surface is destroyed
	destroySurface();

	// Create surface
#ifdef ATEMA_SYSTEM_WINDOWS
	VkWin32SurfaceCreateInfoKHR createInfo{};
	createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
	createInfo.hwnd = static_cast<HWND>(getHandle());
	createInfo.hinstance = GetModuleHandle(nullptr);

	ATEMA_VK_CHECK(instance.vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &m_surface));
#else
#error VulkanRenderWindow is not available on this OS
#endif
}

void VulkanRenderWindow::destroySurface()
{
	if (m_surface != VK_NULL_HANDLE)
	{
		const auto& instance = m_device.getInstance();

		instance.vkDestroySurfaceKHR(instance, m_surface, nullptr);

		m_surface = VK_NULL_HANDLE;
	}
}

void VulkanRenderWindow::initializePresentQueue()
{
	const auto& instance = m_device.getInstance();
	const auto& physicalDevice = m_device.getPhysicalDevice();

	constexpr uint32_t invalidIndex = std::numeric_limits<uint32_t>::max();

	const std::unordered_map<uint32_t, VkQueue> queues =
	{
		{ m_device.getQueueFamilyIndex(QueueType::Graphics), m_device.getQueue(QueueType::Graphics) },
		{ m_device.getQueueFamilyIndex(QueueType::Compute), m_device.getQueue(QueueType::Compute) },
		{ m_device.getQueueFamilyIndex(QueueType::Transfer), m_device.getQueue(QueueType::Transfer) }
	};

	// Present queue families
	m_presentQueueFamilyIndex = invalidIndex;

	uint32_t queueFamilyIndex = 0;
	for (auto& queueFamily : physicalDevice.getQueueFamilyProperties())
	{
		auto queueIt = queues.find(queueFamilyIndex);

		// Only check on enabled queue families
		if (queueIt == queues.end())
			continue;

		VkBool32 presentationSupported = false;
		instance.vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, m_surface, &presentationSupported);

		if (presentationSupported)
		{
			m_presentQueueFamilyIndex = queueFamilyIndex;
			m_presentQueue = queueIt->second;
			break;
		}

		queueFamilyIndex++;
	}

	if (m_presentQueueFamilyIndex == invalidIndex)
	{
		ATEMA_ERROR("Current device does not support presentation on this window");
	}
}

void VulkanRenderWindow::createRenderPass()
{
	// RenderPass
	RenderPass::Settings renderPassSettings;
	renderPassSettings.attachments.resize(2);
	renderPassSettings.attachments[0].format = m_colorFormat;
	renderPassSettings.attachments[0].finalLayout = ImageLayout::Present;
	renderPassSettings.attachments[1].format = m_depthFormat;

	renderPassSettings.subpasses.resize(1);
	renderPassSettings.subpasses[0].color = { 0 };
	renderPassSettings.subpasses[0].depthStencil = 1;

	m_renderPass = std::static_pointer_cast<RenderPass>(std::make_shared<VulkanRenderPass>(m_device, renderPassSettings));
}

void VulkanRenderWindow::createSwapChainResources()
{
	const auto windowSize = getSize();

	// Ensure the resources are deleted
	destroySwapChainResources();

	// Swapchain creation
	VulkanSwapChain::Settings swapChainSettings;
	swapChainSettings.imageFormat = m_colorFormat;

	m_swapChain = std::make_shared<VulkanSwapChain>(*this, swapChainSettings);

	// Depth image
	Image::Settings depthSettings;
	depthSettings.width = windowSize.x;
	depthSettings.height = windowSize.y;
	depthSettings.format = m_depthFormat;
	depthSettings.usages = ImageUsage::RenderTarget;

	m_depthImage = std::static_pointer_cast<Image>(std::make_shared<VulkanImage>(m_device, depthSettings));

	// Framebuffers
	Framebuffer::Settings framebufferSettings;
	framebufferSettings.renderPass = m_renderPass;
	framebufferSettings.width = windowSize.x;
	framebufferSettings.height = windowSize.y;

	const auto& swapChainImages = m_swapChain->getImages();

	m_framebuffers.reserve(swapChainImages.size());

	for (auto& swapChainImage : swapChainImages)
	{
		framebufferSettings.images =
		{
			swapChainImage,
			m_depthImage
		};

		auto framebuffer = std::make_shared<VulkanFramebuffer>(m_device, framebufferSettings);

		m_framebuffers.emplace_back(std::static_pointer_cast<Framebuffer>(framebuffer));
	}

	// RenderFrames
	m_renderFrames.resize(m_frameCount);

	size_t frameIndex = 0;
	for (auto& renderFrame : m_renderFrames)
	{
		renderFrame = std::make_shared<VulkanRenderFrame>(*this, frameIndex++);
	}

	// Image fences
	m_imageFences.resize(swapChainImages.size());

	m_currentFrameIndex = 0;
	m_currentSwapChainImage = 0;
}

void VulkanRenderWindow::destroySwapChainResources()
{
	// Ensure no resource are currently in use
	m_device.waitForIdle();

	m_imageFences.clear();

	m_renderFrames.clear();

	m_framebuffers.clear();

	m_depthImage.reset();

	m_swapChain.reset();
}
<commit_msg>Fix typo<commit_after>/*
	Copyright 2022 Jordi SUBIRANA

	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 <Atema/VulkanRenderer/VulkanRenderWindow.hpp>
#include <Atema/VulkanRenderer/VulkanDevice.hpp>
#include <Atema/VulkanRenderer/VulkanFramebuffer.hpp>
#include <Atema/VulkanRenderer/VulkanImage.hpp>
#include <Atema/VulkanRenderer/VulkanInstance.hpp>
#include <Atema/VulkanRenderer/VulkanPhysicalDevice.hpp>
#include <Atema/VulkanRenderer/VulkanRenderFrame.hpp>
#include <Atema/VulkanRenderer/VulkanRenderPass.hpp>
#include <Atema/VulkanRenderer/VulkanSwapChain.hpp>

using namespace at;

VulkanRenderWindow::VulkanRenderWindow(VulkanDevice& device, const RenderWindow::Settings& settings) :
	RenderWindow(settings),
	m_device(device),
	m_colorFormat(settings.colorFormat),
	m_depthFormat(settings.depthFormat),
	m_surface(VK_NULL_HANDLE),
	m_currentSwapChainImage(0),
	m_recreateSwapChain(false),
	m_currentFrameIndex(0),
	m_frameCount(settings.maxFramesInFlight)
{
	createSurface();

	initializePresentQueue();

	createRenderPass();

	createSwapChainResources();
}

VulkanRenderWindow::~VulkanRenderWindow()
{
	destroySwapChainResources();

	destroySurface();
}

VulkanDevice& VulkanRenderWindow::getDevice() noexcept
{
	return m_device;
}

ImageFormat VulkanRenderWindow::getColorFormat() const noexcept
{
	return m_colorFormat;
}

ImageFormat VulkanRenderWindow::getDepthFormat() const noexcept
{
	return m_depthFormat;
}

size_t VulkanRenderWindow::getMaxFramesInFlight() const noexcept
{
	return m_frameCount;
}

RenderFrame& VulkanRenderWindow::acquireFrame()
{
	if (m_recreateSwapChain)
	{
		createSwapChainResources();

		m_recreateSwapChain = false;
	}

	auto& renderFrame = *m_renderFrames[m_currentFrameIndex];

	renderFrame.wait();

	const auto acquireResult = m_swapChain->acquireNextImage(m_currentSwapChainImage, renderFrame.getImageAvailableSemaphoreHandle(), VK_NULL_HANDLE);

	// Out of date : recreate swapchain right now
	if (acquireResult == SwapChainResult::OutOfDate)
	{
		m_recreateSwapChain = true;

		return acquireFrame();
	}
	// Suboptimal : recreate next frame, we can still use it
	else if (acquireResult == SwapChainResult::Suboptimal)
	{
		m_recreateSwapChain = true;
	}
	// Impossible to acquire
	else if (acquireResult != SwapChainResult::Success)
	{
		ATEMA_ERROR("Failed to acquire a valid swapchain image");
	}

	// Check if a previous frame is using this image (i.e. there is its fence to wait on)
	if (m_imageFences[m_currentSwapChainImage])
	{
		m_imageFences[m_currentSwapChainImage]->wait();
	}

	// Mark the image as now being in use by this frame
	m_imageFences[m_currentSwapChainImage] = renderFrame.getFence();

	renderFrame.setImageIndex(m_currentSwapChainImage);

	m_currentFrameIndex = (m_currentFrameIndex + 1) % m_frameCount;

	return renderFrame;
}

VkSurfaceKHR VulkanRenderWindow::getSurface() const noexcept
{
	return m_surface;
}

uint32_t VulkanRenderWindow::getPresentQueueFamilyIndex() const noexcept
{
	return m_presentQueueFamilyIndex;
}

Ptr<RenderPass> VulkanRenderWindow::getRenderPass() const noexcept
{
	return m_renderPass;
}

Ptr<Framebuffer> VulkanRenderWindow::getFramebuffer(size_t imageIndex) const noexcept
{
	return m_framebuffers[imageIndex];
}

void VulkanRenderWindow::present(const VulkanRenderFrame& renderFrame)
{
	auto vkSemaphore = std::static_pointer_cast<VulkanSemaphore>(renderFrame.getRenderFinishedSemaphore())->getHandle();

	std::vector<VkSemaphore> vkSemaphores = { vkSemaphore };

	VkPresentInfoKHR presentInfo{};

	presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
	// Which semaphores to wait on before presentation can happen
	presentInfo.waitSemaphoreCount = static_cast<uint32_t>(vkSemaphores.size());
	presentInfo.pWaitSemaphores = vkSemaphores.data();

	auto vkSwapChain = std::static_pointer_cast<VulkanSwapChain>(m_swapChain);

	VkSwapchainKHR swapChains[] = { vkSwapChain->getHandle() };
	auto imageIndex = renderFrame.getImageIndex();

	presentInfo.swapchainCount = 1;
	presentInfo.pSwapchains = swapChains;
	presentInfo.pImageIndices = &imageIndex;
	// Array of VkResult values to check for every individual swap chain if presentation was successful
	//presentInfo.pResults = nullptr; // Optional

	const auto result = m_device.vkQueuePresentKHR(m_presentQueue, &presentInfo);

	const auto swapChainResult = Vulkan::getSwapChainResult(result);

	if (swapChainResult == SwapChainResult::OutOfDate || swapChainResult == SwapChainResult::Suboptimal)
	{
		m_recreateSwapChain = true;
	}
	else if (swapChainResult != SwapChainResult::Success)
	{
		ATEMA_ERROR("SwapChain presentation failed");
	}
}

void VulkanRenderWindow::onResize(const Vector2u& newSize)
{
	m_recreateSwapChain = true;

	RenderWindow::onResize(newSize);
}

void VulkanRenderWindow::createSurface()
{
	const auto& instance = m_device.getInstance();

	// Ensure the surface is destroyed
	destroySurface();

	// Create surface
#ifdef ATEMA_SYSTEM_WINDOWS
	VkWin32SurfaceCreateInfoKHR createInfo{};
	createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
	createInfo.hwnd = static_cast<HWND>(getHandle());
	createInfo.hinstance = GetModuleHandle(nullptr);

	ATEMA_VK_CHECK(instance.vkCreateWin32SurfaceKHR(instance, &createInfo, nullptr, &m_surface));
#else
#error VulkanRenderWindow is not available on this OS
#endif
}

void VulkanRenderWindow::destroySurface()
{
	if (m_surface != VK_NULL_HANDLE)
	{
		const auto& instance = m_device.getInstance();

		instance.vkDestroySurfaceKHR(instance, m_surface, nullptr);

		m_surface = VK_NULL_HANDLE;
	}
}

void VulkanRenderWindow::initializePresentQueue()
{
	const auto& instance = m_device.getInstance();
	const auto& physicalDevice = m_device.getPhysicalDevice();

	constexpr uint32_t invalidIndex = std::numeric_limits<uint32_t>::max();

	const std::unordered_map<uint32_t, VkQueue> queues =
	{
		{ m_device.getQueueFamilyIndex(QueueType::Graphics), m_device.getQueue(QueueType::Graphics) },
		{ m_device.getQueueFamilyIndex(QueueType::Compute), m_device.getQueue(QueueType::Compute) },
		{ m_device.getQueueFamilyIndex(QueueType::Transfer), m_device.getQueue(QueueType::Transfer) }
	};

	// Present queue families
	m_presentQueueFamilyIndex = invalidIndex;

	uint32_t queueFamilyIndex = 0;
	for (auto& queueFamily : physicalDevice.getQueueFamilyProperties())
	{
		auto queueIt = queues.find(queueFamilyIndex);

		// Only check on enabled queue families
		if (queueIt == queues.end())
			continue;

		VkBool32 presentationSupported = false;
		instance.vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, m_surface, &presentationSupported);

		if (presentationSupported)
		{
			m_presentQueueFamilyIndex = queueFamilyIndex;
			m_presentQueue = queueIt->second;
			break;
		}

		queueFamilyIndex++;
	}

	if (m_presentQueueFamilyIndex == invalidIndex)
	{
		ATEMA_ERROR("Current device does not support presentation on this window");
	}
}

void VulkanRenderWindow::createRenderPass()
{
	// RenderPass
	RenderPass::Settings renderPassSettings;
	renderPassSettings.attachments.resize(2);
	renderPassSettings.attachments[0].format = m_colorFormat;
	renderPassSettings.attachments[0].finalLayout = ImageLayout::Present;
	renderPassSettings.attachments[1].format = m_depthFormat;

	renderPassSettings.subpasses.resize(1);
	renderPassSettings.subpasses[0].color = { 0 };
	renderPassSettings.subpasses[0].depthStencil = 1;

	m_renderPass = std::static_pointer_cast<RenderPass>(std::make_shared<VulkanRenderPass>(m_device, renderPassSettings));
}

void VulkanRenderWindow::createSwapChainResources()
{
	const auto windowSize = getSize();

	// Ensure the resources are deleted
	destroySwapChainResources();

	// Swapchain creation
	VulkanSwapChain::Settings swapChainSettings;
	swapChainSettings.imageFormat = m_colorFormat;

	m_swapChain = std::make_shared<VulkanSwapChain>(*this, swapChainSettings);

	// Depth image
	Image::Settings depthSettings;
	depthSettings.width = windowSize.x;
	depthSettings.height = windowSize.y;
	depthSettings.format = m_depthFormat;
	depthSettings.usages = ImageUsage::RenderTarget;

	m_depthImage = std::static_pointer_cast<Image>(std::make_shared<VulkanImage>(m_device, depthSettings));

	// Framebuffers
	Framebuffer::Settings framebufferSettings;
	framebufferSettings.renderPass = m_renderPass;
	framebufferSettings.width = windowSize.x;
	framebufferSettings.height = windowSize.y;

	const auto& swapChainImages = m_swapChain->getImages();

	m_framebuffers.reserve(swapChainImages.size());

	for (auto& swapChainImage : swapChainImages)
	{
		framebufferSettings.images =
		{
			swapChainImage,
			m_depthImage
		};

		auto framebuffer = std::make_shared<VulkanFramebuffer>(m_device, framebufferSettings);

		m_framebuffers.emplace_back(std::static_pointer_cast<Framebuffer>(framebuffer));
	}

	// RenderFrames
	m_renderFrames.resize(m_frameCount);

	size_t frameIndex = 0;
	for (auto& renderFrame : m_renderFrames)
	{
		renderFrame = std::make_shared<VulkanRenderFrame>(*this, frameIndex++);
	}

	// Image fences
	m_imageFences.resize(swapChainImages.size());

	m_currentFrameIndex = 0;
	m_currentSwapChainImage = 0;
}

void VulkanRenderWindow::destroySwapChainResources()
{
	// Ensure no resource are currently in use
	m_device.waitForIdle();

	m_imageFences.clear();

	m_renderFrames.clear();

	m_framebuffers.clear();

	m_depthImage.reset();

	m_swapChain.reset();
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Ford Motor Company 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 HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "application_manager/message.h"

namespace {
bool BinaryDataPredicate(uint8_t i, uint8_t j) {
  return (i == j);
}
}

namespace application_manager {

MessageType MessageTypeFromRpcType(protocol_handler::RpcType rpc_type) {
  switch (rpc_type) {
    case protocol_handler::kRpcTypeRequest:
      return kRequest;
    case protocol_handler::kRpcTypeResponse:
      return kResponse;
    case protocol_handler::kRpcTypeNotification:
      return kNotification;
    case protocol_handler::kRpcTypeReserved:
    default:
      DCHECK(false);
      return kUnknownType;
  }
}

Message::Message(protocol_handler::MessagePriority priority)
    : function_id_(0)
    , correlation_id_(0)
    , type_(kUnknownType)
    , priority_(priority)
    , connection_key_(0)
    , binary_data_(NULL)
    , data_size_(0)
    , payload_size_(0)
    , version_(
          protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_UNKNOWN) {}

Message::Message(const Message& message)
    : function_id_(0)
    , correlation_id_(0)
    , type_(kUnknownType)
    , priority_(message.priority_)
    , connection_key_(0)
    , binary_data_(NULL)
    , data_size_(0)
    , payload_size_(0)
    , version_(
          protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_UNKNOWN) {
  *this = message;
}

Message& Message::operator=(const Message& message) {
  set_function_id(message.function_id_);
  set_correlation_id(message.correlation_id_);
  set_connection_key(message.connection_key_);
  set_message_type(message.type_);
  set_data_size(message.data_size_);
  set_payload_size(message.payload_size_);
  if (message.binary_data_) {
    set_binary_data(message.binary_data_);
  }
  set_json_message(message.json_message_);
  set_protocol_version(message.protocol_version());
  priority_ = message.priority_;

  return *this;
}

bool Message::operator==(const Message& message) const {
  bool function_id = function_id_ == message.function_id_;
  bool correlation_id = correlation_id_ == message.correlation_id_;
  bool connection_key = connection_key_ == message.connection_key_;
  bool type = type_ == message.type_;
  bool json_message = json_message_ == message.json_message_;
  bool version = version_ == message.version_;
  bool data_size = data_size_ == message.data_size_;
  bool payload_size = payload_size_ == message.payload_size_;
  bool binary_data = true;
  if (binary_data_) {
    binary_data = std::equal(binary_data_->begin(),
                             binary_data_->end(),
                             message.binary_data_->begin(),
                             BinaryDataPredicate);
  }

  return function_id && correlation_id && connection_key && type &&
         binary_data && json_message && version && data_size && payload_size;
}

Message::~Message() {
  if (binary_data_) {
    delete binary_data_;
  }
}

int32_t Message::function_id() const {
  return function_id_;
}

#ifdef SDL_REMOTE_CONTROL
std::string Message::function_name() const {
  return function_name_;
}
#endif  // SDL_REMOTE_CONTROL

int32_t Message::correlation_id() const {
  return correlation_id_;
}

int32_t Message::connection_key() const {
  return connection_key_;
}

MessageType Message::type() const {
  return type_;
}

protocol_handler::MajorProtocolVersion Message::protocol_version() const {
  return version_;
}

const std::string& Message::json_message() const {
  return json_message_;
}

const BinaryData* Message::binary_data() const {
  return binary_data_;
}

bool Message::has_binary_data() const {
  return (binary_data_ != NULL);
}

size_t Message::data_size() const {
  return data_size_;
}

size_t Message::payload_size() const {
  return payload_size_;
}

void Message::set_function_id(int32_t id) {
  function_id_ = id;
}

#ifdef SDL_REMOTE_CONTROL
void Message::set_function_name(const std::string& name) {
  function_name_ = name;
}
#endif  // SDL_REMOTE_CONTROL

void Message::set_correlation_id(int32_t id) {
  correlation_id_ = id;
}

void Message::set_connection_key(int32_t key) {
  connection_key_ = key;
}

void Message::set_message_type(MessageType type) {
  type_ = type;
}

void Message::set_binary_data(BinaryData* data) {
  if (NULL == data) {
    NOTREACHED();
    return;
  }

  if (binary_data_) {
    delete binary_data_;
  }

  binary_data_ = new BinaryData(*data);
}

void Message::set_json_message(const std::string& json_message) {
  json_message_ = json_message;
}

void Message::set_protocol_version(
    protocol_handler::MajorProtocolVersion version) {
  version_ = version;
}

const smart_objects::SmartObject& Message::smart_object() const {
  return smart_object_;
}

void Message::set_smart_object(const smart_objects::SmartObject& object) {
  smart_object_ = object;
}

void Message::set_data_size(size_t data_size) {
  data_size_ = data_size;
}

void Message::set_payload_size(size_t payload_size) {
  payload_size_ = payload_size;
}

bool Message::is_sufficient_version(
    protocol_handler::MajorProtocolVersion minVersion,
    protocol_handler::MajorProtocolVersion version) {
  return version >= minVersion &&
         version <= protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_5;
}
}  // namespace application_manager
<commit_msg>rename variable<commit_after>/*
 * Copyright (c) 2017, Ford Motor Company
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Ford Motor Company 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 HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "application_manager/message.h"

namespace {
bool BinaryDataPredicate(uint8_t i, uint8_t j) {
  return (i == j);
}
}

namespace application_manager {

MessageType MessageTypeFromRpcType(protocol_handler::RpcType rpc_type) {
  switch (rpc_type) {
    case protocol_handler::kRpcTypeRequest:
      return kRequest;
    case protocol_handler::kRpcTypeResponse:
      return kResponse;
    case protocol_handler::kRpcTypeNotification:
      return kNotification;
    case protocol_handler::kRpcTypeReserved:
    default:
      DCHECK(false);
      return kUnknownType;
  }
}

Message::Message(protocol_handler::MessagePriority priority)
    : function_id_(0)
    , correlation_id_(0)
    , type_(kUnknownType)
    , priority_(priority)
    , connection_key_(0)
    , binary_data_(NULL)
    , data_size_(0)
    , payload_size_(0)
    , version_(
          protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_UNKNOWN) {}

Message::Message(const Message& message)
    : function_id_(0)
    , correlation_id_(0)
    , type_(kUnknownType)
    , priority_(message.priority_)
    , connection_key_(0)
    , binary_data_(NULL)
    , data_size_(0)
    , payload_size_(0)
    , version_(
          protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_UNKNOWN) {
  *this = message;
}

Message& Message::operator=(const Message& message) {
  set_function_id(message.function_id_);
  set_correlation_id(message.correlation_id_);
  set_connection_key(message.connection_key_);
  set_message_type(message.type_);
  set_data_size(message.data_size_);
  set_payload_size(message.payload_size_);
  if (message.binary_data_) {
    set_binary_data(message.binary_data_);
  }
  set_json_message(message.json_message_);
  set_protocol_version(message.protocol_version());
  priority_ = message.priority_;

  return *this;
}

bool Message::operator==(const Message& message) const {
  bool function_id = function_id_ == message.function_id_;
  bool correlation_id = correlation_id_ == message.correlation_id_;
  bool connection_key = connection_key_ == message.connection_key_;
  bool type = type_ == message.type_;
  bool json_message = json_message_ == message.json_message_;
  bool version = version_ == message.version_;
  bool data_size = data_size_ == message.data_size_;
  bool payload_size = payload_size_ == message.payload_size_;
  bool binary_data_available = true;
  if (binary_data_) {
    binary_data_available = std::equal(binary_data_->begin(),
                             binary_data_->end(),
                             message.binary_data_->begin(),
                             BinaryDataPredicate);
  }

  return function_id && correlation_id && connection_key && type &&
         binary_data_available && json_message && version && data_size && payload_size;
}

Message::~Message() {
  if (binary_data_) {
    delete binary_data_;
  }
}

int32_t Message::function_id() const {
  return function_id_;
}

#ifdef SDL_REMOTE_CONTROL
std::string Message::function_name() const {
  return function_name_;
}
#endif  // SDL_REMOTE_CONTROL

int32_t Message::correlation_id() const {
  return correlation_id_;
}

int32_t Message::connection_key() const {
  return connection_key_;
}

MessageType Message::type() const {
  return type_;
}

protocol_handler::MajorProtocolVersion Message::protocol_version() const {
  return version_;
}

const std::string& Message::json_message() const {
  return json_message_;
}

const BinaryData* Message::binary_data() const {
  return binary_data_;
}

bool Message::has_binary_data() const {
  return (binary_data_ != NULL);
}

size_t Message::data_size() const {
  return data_size_;
}

size_t Message::payload_size() const {
  return payload_size_;
}

void Message::set_function_id(int32_t id) {
  function_id_ = id;
}

#ifdef SDL_REMOTE_CONTROL
void Message::set_function_name(const std::string& name) {
  function_name_ = name;
}
#endif  // SDL_REMOTE_CONTROL

void Message::set_correlation_id(int32_t id) {
  correlation_id_ = id;
}

void Message::set_connection_key(int32_t key) {
  connection_key_ = key;
}

void Message::set_message_type(MessageType type) {
  type_ = type;
}

void Message::set_binary_data(BinaryData* data) {
  if (NULL == data) {
    NOTREACHED();
    return;
  }

  if (binary_data_) {
    delete binary_data_;
  }

  binary_data_ = new BinaryData(*data);
}

void Message::set_json_message(const std::string& json_message) {
  json_message_ = json_message;
}

void Message::set_protocol_version(
    protocol_handler::MajorProtocolVersion version) {
  version_ = version;
}

const smart_objects::SmartObject& Message::smart_object() const {
  return smart_object_;
}

void Message::set_smart_object(const smart_objects::SmartObject& object) {
  smart_object_ = object;
}

void Message::set_data_size(size_t data_size) {
  data_size_ = data_size;
}

void Message::set_payload_size(size_t payload_size) {
  payload_size_ = payload_size;
}

bool Message::is_sufficient_version(
    protocol_handler::MajorProtocolVersion minVersion,
    protocol_handler::MajorProtocolVersion version) {
  return version >= minVersion &&
         version <= protocol_handler::MajorProtocolVersion::PROTOCOL_VERSION_5;
}
}  // namespace application_manager
<|endoftext|>


<commit_before>/*
 * MIT License
 *
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * 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 <map>
#include <signal.h>
#include <iostream>
#include "Util/MD5.h"
#include "Util/File.h"
#include "Util/logger.h"
#include "Util/SSLBox.h"
#include "Util/onceToken.h"
#include "Util/CMD.h"
#include "Network/TcpServer.h"
#include "Poller/EventPoller.h"
#include "Common/config.h"
#include "Rtsp/UDPServer.h"
#include "Rtsp/RtspSession.h"
#include "Rtmp/RtmpSession.h"
#include "Shell/ShellSession.h"
#include "Rtmp/FlvMuxer.h"
#include "Player/PlayerProxy.h"
#include "Http/WebSocketSession.h"
#include "WebApi.h"
#include "WebHook.h"

#if !defined(_WIN32)
#include "System.h"
#endif//!defined(_WIN32)

using namespace std;
using namespace toolkit;
using namespace mediakit;

namespace mediakit {
////////////HTTP配置///////////
namespace Http {
#define HTTP_FIELD "http."
#define HTTP_PORT 80
const string kPort = HTTP_FIELD"port";
#define HTTPS_PORT 443
const string kSSLPort = HTTP_FIELD"sslport";
onceToken token1([](){
    mINI::Instance()[kPort] = HTTP_PORT;
    mINI::Instance()[kSSLPort] = HTTPS_PORT;
},nullptr);
}//namespace Http

////////////SHELL配置///////////
namespace Shell {
#define SHELL_FIELD "shell."
#define SHELL_PORT 9000
const string kPort = SHELL_FIELD"port";
onceToken token1([](){
    mINI::Instance()[kPort] = SHELL_PORT;
},nullptr);
} //namespace Shell

////////////RTSP服务器配置///////////
namespace Rtsp {
#define RTSP_FIELD "rtsp."
#define RTSP_PORT 554
#define RTSPS_PORT 322
const string kPort = RTSP_FIELD"port";
const string kSSLPort = RTSP_FIELD"sslport";
onceToken token1([](){
    mINI::Instance()[kPort] = RTSP_PORT;
    mINI::Instance()[kSSLPort] = RTSPS_PORT;
},nullptr);

} //namespace Rtsp

////////////RTMP服务器配置///////////
namespace Rtmp {
#define RTMP_FIELD "rtmp."
#define RTMP_PORT 1935
const string kPort = RTMP_FIELD"port";
onceToken token1([](){
    mINI::Instance()[kPort] = RTMP_PORT;
},nullptr);
} //namespace RTMP
}  // namespace mediakit

class CMD_main : public CMD {
public:
    CMD_main() {
        _parser.reset(new OptionParser(nullptr));

#if !defined(_WIN32)
        (*_parser) << Option('d',/*该选项简称，如果是\x00则说明无简称*/
                             "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgNone,/*该选项后面必须跟值*/
                             nullptr,/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "是否以Daemon方式启动",/*该选项说明文字*/
                             nullptr);
#endif//!defined(_WIN32)

        (*_parser) << Option('l',/*该选项简称，如果是\x00则说明无简称*/
                             "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             to_string(LTrace).data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "日志等级,LTrace~LError(0~4)",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('m',/*该选项简称，如果是\x00则说明无简称*/
                             "max_day",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             "7",/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "日志最多保存天数",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('c',/*该选项简称，如果是\x00则说明无简称*/
                             "config",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             (exeDir() + "config.ini").data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "配置文件路径",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('s',/*该选项简称，如果是\x00则说明无简称*/
                             "ssl",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             (exeDir() + "ssl.p12").data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "ssl证书文件或文件夹,支持p12/pem类型",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('t',/*该选项简称，如果是\x00则说明无简称*/
                             "threads",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             to_string(thread::hardware_concurrency()).data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "启动事件触发线程数",/*该选项说明文字*/
                             nullptr);
    }

    virtual ~CMD_main() {}
    virtual const char *description() const {
        return "主程序命令参数";
    }
};

#if !defined(_WIN32)
static void inline listen_shell_input(){
    cout << "> 欢迎进入命令模式，你可以输入\"help\"命令获取帮助" << endl;
    cout << "> " << std::flush;

    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);

    SockUtil::setNoBlocked(STDIN_FILENO);
    auto oninput = [](int event) {
        if (event & Event_Read) {
            char buf[1024];
            int n = read(STDIN_FILENO, buf, sizeof(buf));
            if (n > 0) {
                buf[n] = '\0';
                try {
                    CMDRegister::Instance()(buf);
                    cout << "> " << std::flush;
                } catch (ExitException &ex) {
                    InfoL << "ExitException";
                    kill(getpid(), SIGINT);
                } catch (std::exception &ex) {
                    cout << ex.what() << endl;
                }
            } else {
                DebugL << get_uv_errmsg();
                EventPollerPool::Instance().getFirstPoller()->delEvent(STDIN_FILENO);
            }
        }

        if (event & Event_Error) {
            WarnL << "Event_Error";
            EventPollerPool::Instance().getFirstPoller()->delEvent(STDIN_FILENO);
        }
    };
    EventPollerPool::Instance().getFirstPoller()->addEvent(STDIN_FILENO, Event_Read | Event_Error | Event_LT,oninput);
}
#endif//!defined(_WIN32)

//全局变量，在WebApi中用于保存配置文件用
string g_ini_file;

int start_main(int argc,char *argv[]) {
    {
        CMD_main cmd_main;
        try {
            cmd_main.operator()(argc, argv);
        } catch (std::exception &ex) {
            cout << ex.what() << endl;
            return -1;
        }

        bool bDaemon = cmd_main.hasKey("daemon");
        LogLevel logLevel = (LogLevel) cmd_main["level"].as<int>();
        logLevel = MIN(MAX(logLevel, LTrace), LError);
        g_ini_file = cmd_main["config"];
        string ssl_file = cmd_main["ssl"];
        int threads = cmd_main["threads"];

        //设置日志
        Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
#ifndef ANDROID
        auto fileChannel = std::make_shared<FileChannel>("FileChannel", exeDir() + "log/", logLevel);
        //日志最多保存天数
        fileChannel->setMaxDay(cmd_main["max_day"]);
        Logger::Instance().add(fileChannel);
#endif//

#if !defined(_WIN32)
        if (bDaemon) {
            //启动守护进程
            System::startDaemon();
        }
        //开启崩溃捕获等
        System::systemSetup();
#endif//!defined(_WIN32)

        //启动异步日志线程
        Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
        //加载配置文件，如果配置文件不存在就创建一个
        loadIniConfig(g_ini_file.data());

        if(!File::is_dir(ssl_file.data())){
            //不是文件夹，加载证书，证书包含公钥和私钥
            SSL_Initor::Instance().loadCertificate(ssl_file.data());
        }else{
            //加载文件夹下的所有证书
            File::scanDir(ssl_file,[](const string &path, bool isDir){
                if(!isDir){
                    //最后的一个证书会当做默认证书(客户端ssl握手时未指定主机)
                    SSL_Initor::Instance().loadCertificate(path.data());
                }
                return true;
            });
        }

        uint16_t shellPort = mINI::Instance()[Shell::kPort];
        uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
        uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
        uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort];
        uint16_t httpPort = mINI::Instance()[Http::kPort];
        uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];

        //设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
        EventPollerPool::setPoolSize(threads);

        //简单的telnet服务器，可用于服务器调试，但是不能使用23端口，否则telnet上了莫名其妙的现象
        //测试方法:telnet 127.0.0.1 9000
        TcpServer::Ptr shellSrv(new TcpServer());
        TcpServer::Ptr rtspSrv(new TcpServer());
        TcpServer::Ptr rtmpSrv(new TcpServer());
        TcpServer::Ptr httpSrv(new TcpServer());

        shellSrv->start<ShellSession>(shellPort);
        rtspSrv->start<RtspSession>(rtspPort);//默认554
        rtmpSrv->start<RtmpSession>(rtmpPort);//默认1935
        //http服务器
        httpSrv->start<HttpSession>(httpPort);//默认80

        //如果支持ssl，还可以开启https服务器
        TcpServer::Ptr httpsSrv(new TcpServer());
        //https服务器,支持websocket
        httpsSrv->start<HttpsSession>(httpsPort);//默认443

        //支持ssl加密的rtsp服务器，可用于诸如亚马逊echo show这样的设备访问
        TcpServer::Ptr rtspSSLSrv(new TcpServer());
        rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort);//默认322

        installWebApi();
        InfoL << "已启动http api 接口";
        installWebHook();
        InfoL << "已启动http hook 接口";

#if !defined(_WIN32) && !defined(ANDROID)
        if (!bDaemon) {
            //交互式shell输入
            listen_shell_input();
        }
#endif

        //设置退出信号处理函数
        static semaphore sem;
        signal(SIGINT, [](int) {
            InfoL << "SIGINT:exit";
            signal(SIGINT, SIG_IGN);// 设置退出信号
            sem.post();
        });// 设置退出信号

#if !defined(_WIN32)
        signal(SIGHUP, [](int) { mediakit::loadIniConfig(g_ini_file.data()); });
#endif
        sem.wait();
    }
    unInstallWebApi();
    unInstallWebHook();
    //休眠1秒再退出，防止资源释放顺序错误
    InfoL << "程序退出中,请等待...";
    sleep(1);
    InfoL << "程序退出完毕!";
	return 0;
}

#ifndef DISABLE_MAIN
int main(int argc,char *argv[]) {
    return start_main(argc,argv);
}
#endif //DISABLE_MAIN

<commit_msg>添加启动失败处理逻辑<commit_after>/*
 * MIT License
 *
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * 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 <map>
#include <signal.h>
#include <iostream>
#include "Util/MD5.h"
#include "Util/File.h"
#include "Util/logger.h"
#include "Util/SSLBox.h"
#include "Util/onceToken.h"
#include "Util/CMD.h"
#include "Network/TcpServer.h"
#include "Poller/EventPoller.h"
#include "Common/config.h"
#include "Rtsp/UDPServer.h"
#include "Rtsp/RtspSession.h"
#include "Rtmp/RtmpSession.h"
#include "Shell/ShellSession.h"
#include "Rtmp/FlvMuxer.h"
#include "Player/PlayerProxy.h"
#include "Http/WebSocketSession.h"
#include "WebApi.h"
#include "WebHook.h"

#if !defined(_WIN32)
#include "System.h"
#endif//!defined(_WIN32)

using namespace std;
using namespace toolkit;
using namespace mediakit;

namespace mediakit {
////////////HTTP配置///////////
namespace Http {
#define HTTP_FIELD "http."
#define HTTP_PORT 80
const string kPort = HTTP_FIELD"port";
#define HTTPS_PORT 443
const string kSSLPort = HTTP_FIELD"sslport";
onceToken token1([](){
    mINI::Instance()[kPort] = HTTP_PORT;
    mINI::Instance()[kSSLPort] = HTTPS_PORT;
},nullptr);
}//namespace Http

////////////SHELL配置///////////
namespace Shell {
#define SHELL_FIELD "shell."
#define SHELL_PORT 9000
const string kPort = SHELL_FIELD"port";
onceToken token1([](){
    mINI::Instance()[kPort] = SHELL_PORT;
},nullptr);
} //namespace Shell

////////////RTSP服务器配置///////////
namespace Rtsp {
#define RTSP_FIELD "rtsp."
#define RTSP_PORT 554
#define RTSPS_PORT 322
const string kPort = RTSP_FIELD"port";
const string kSSLPort = RTSP_FIELD"sslport";
onceToken token1([](){
    mINI::Instance()[kPort] = RTSP_PORT;
    mINI::Instance()[kSSLPort] = RTSPS_PORT;
},nullptr);

} //namespace Rtsp

////////////RTMP服务器配置///////////
namespace Rtmp {
#define RTMP_FIELD "rtmp."
#define RTMP_PORT 1935
const string kPort = RTMP_FIELD"port";
onceToken token1([](){
    mINI::Instance()[kPort] = RTMP_PORT;
},nullptr);
} //namespace RTMP
}  // namespace mediakit

class CMD_main : public CMD {
public:
    CMD_main() {
        _parser.reset(new OptionParser(nullptr));

#if !defined(_WIN32)
        (*_parser) << Option('d',/*该选项简称，如果是\x00则说明无简称*/
                             "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgNone,/*该选项后面必须跟值*/
                             nullptr,/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "是否以Daemon方式启动",/*该选项说明文字*/
                             nullptr);
#endif//!defined(_WIN32)

        (*_parser) << Option('l',/*该选项简称，如果是\x00则说明无简称*/
                             "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             to_string(LTrace).data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "日志等级,LTrace~LError(0~4)",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('m',/*该选项简称，如果是\x00则说明无简称*/
                             "max_day",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             "7",/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "日志最多保存天数",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('c',/*该选项简称，如果是\x00则说明无简称*/
                             "config",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             (exeDir() + "config.ini").data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "配置文件路径",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('s',/*该选项简称，如果是\x00则说明无简称*/
                             "ssl",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             (exeDir() + "ssl.p12").data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "ssl证书文件或文件夹,支持p12/pem类型",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('t',/*该选项简称，如果是\x00则说明无简称*/
                             "threads",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             to_string(thread::hardware_concurrency()).data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "启动事件触发线程数",/*该选项说明文字*/
                             nullptr);
    }

    virtual ~CMD_main() {}
    virtual const char *description() const {
        return "主程序命令参数";
    }
};

#if !defined(_WIN32)
static void inline listen_shell_input(){
    cout << "> 欢迎进入命令模式，你可以输入\"help\"命令获取帮助" << endl;
    cout << "> " << std::flush;

    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);

    SockUtil::setNoBlocked(STDIN_FILENO);
    auto oninput = [](int event) {
        if (event & Event_Read) {
            char buf[1024];
            int n = read(STDIN_FILENO, buf, sizeof(buf));
            if (n > 0) {
                buf[n] = '\0';
                try {
                    CMDRegister::Instance()(buf);
                    cout << "> " << std::flush;
                } catch (ExitException &ex) {
                    InfoL << "ExitException";
                    kill(getpid(), SIGINT);
                } catch (std::exception &ex) {
                    cout << ex.what() << endl;
                }
            } else {
                DebugL << get_uv_errmsg();
                EventPollerPool::Instance().getFirstPoller()->delEvent(STDIN_FILENO);
            }
        }

        if (event & Event_Error) {
            WarnL << "Event_Error";
            EventPollerPool::Instance().getFirstPoller()->delEvent(STDIN_FILENO);
        }
    };
    EventPollerPool::Instance().getFirstPoller()->addEvent(STDIN_FILENO, Event_Read | Event_Error | Event_LT,oninput);
}
#endif//!defined(_WIN32)

//全局变量，在WebApi中用于保存配置文件用
string g_ini_file;

int start_main(int argc,char *argv[]) {
    {
        CMD_main cmd_main;
        try {
            cmd_main.operator()(argc, argv);
        } catch (std::exception &ex) {
            cout << ex.what() << endl;
            return -1;
        }

        bool bDaemon = cmd_main.hasKey("daemon");
        LogLevel logLevel = (LogLevel) cmd_main["level"].as<int>();
        logLevel = MIN(MAX(logLevel, LTrace), LError);
        g_ini_file = cmd_main["config"];
        string ssl_file = cmd_main["ssl"];
        int threads = cmd_main["threads"];

        //设置日志
        Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
#ifndef ANDROID
        auto fileChannel = std::make_shared<FileChannel>("FileChannel", exeDir() + "log/", logLevel);
        //日志最多保存天数
        fileChannel->setMaxDay(cmd_main["max_day"]);
        Logger::Instance().add(fileChannel);
#endif//

#if !defined(_WIN32)
        pid_t pid = getpid();
        if (bDaemon) {
            //启动守护进程
            System::startDaemon();
        }
        //开启崩溃捕获等
        System::systemSetup();
#endif//!defined(_WIN32)

        //启动异步日志线程
        Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
        //加载配置文件，如果配置文件不存在就创建一个
        loadIniConfig(g_ini_file.data());

        if(!File::is_dir(ssl_file.data())){
            //不是文件夹，加载证书，证书包含公钥和私钥
            SSL_Initor::Instance().loadCertificate(ssl_file.data());
        }else{
            //加载文件夹下的所有证书
            File::scanDir(ssl_file,[](const string &path, bool isDir){
                if(!isDir){
                    //最后的一个证书会当做默认证书(客户端ssl握手时未指定主机)
                    SSL_Initor::Instance().loadCertificate(path.data());
                }
                return true;
            });
        }

        uint16_t shellPort = mINI::Instance()[Shell::kPort];
        uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
        uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
        uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort];
        uint16_t httpPort = mINI::Instance()[Http::kPort];
        uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];

        //设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
        EventPollerPool::setPoolSize(threads);

        //简单的telnet服务器，可用于服务器调试，但是不能使用23端口，否则telnet上了莫名其妙的现象
        //测试方法:telnet 127.0.0.1 9000
        TcpServer::Ptr shellSrv(new TcpServer());
        TcpServer::Ptr rtspSrv(new TcpServer());
        TcpServer::Ptr rtmpSrv(new TcpServer());
        TcpServer::Ptr httpSrv(new TcpServer());
        //如果支持ssl，还可以开启https服务器
        TcpServer::Ptr httpsSrv(new TcpServer());
        //支持ssl加密的rtsp服务器，可用于诸如亚马逊echo show这样的设备访问
        TcpServer::Ptr rtspSSLSrv(new TcpServer());

        try {
            //rtsp服务器，端口默认554
            rtspSrv->start<RtspSession>(rtspPort);//默认554
            //rtsps服务器，端口默认322
            rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort);
            //rtmp服务器，端口默认1935
            rtmpSrv->start<RtmpSession>(rtmpPort);
            //http服务器，端口默认80
            httpSrv->start<HttpSession>(httpPort);
            //https服务器，端口默认443
            httpsSrv->start<HttpsSession>(httpsPort);
            //telnet远程调试服务器
            shellSrv->start<ShellSession>(shellPort);
        }catch (std::exception &ex){
            WarnL << "端口占用或无权限:" << ex.what() << endl;
            ErrorL << "程序启动失败，请修改配置文件中端口号后重试!" << endl;
            sleep(1);
#if !defined(_WIN32)
            if(pid != getpid()){
                kill(pid,SIGINT);
            }
#endif
            return -1;
        }

        installWebApi();
        InfoL << "已启动http api 接口";
        installWebHook();
        InfoL << "已启动http hook 接口";

#if !defined(_WIN32) && !defined(ANDROID)
        if (!bDaemon) {
            //交互式shell输入
            listen_shell_input();
        }
#endif

        //设置退出信号处理函数
        static semaphore sem;
        signal(SIGINT, [](int) {
            InfoL << "SIGINT:exit";
            signal(SIGINT, SIG_IGN);// 设置退出信号
            sem.post();
        });// 设置退出信号

#if !defined(_WIN32)
        signal(SIGHUP, [](int) { mediakit::loadIniConfig(g_ini_file.data()); });
#endif
        sem.wait();
    }
    unInstallWebApi();
    unInstallWebHook();
    //休眠1秒再退出，防止资源释放顺序错误
    InfoL << "程序退出中,请等待...";
    sleep(1);
    InfoL << "程序退出完毕!";
	return 0;
}

#ifndef DISABLE_MAIN
int main(int argc,char *argv[]) {
    return start_main(argc,argv);
}
#endif //DISABLE_MAIN

<|endoftext|>


<commit_before>#pragma once

#include <agency/parallel_executor.hpp>
#include <agency/detail/array.hpp>
#include <agency/detail/shape_tuple.hpp>
#include <agency/detail/index_tuple.hpp>
#include <agency/functional.hpp>

namespace agency
{

template<class InnerExecutor, class OuterExecutor = this_thread::parallel_executor>
class executor_array
{
  public:
    using outer_executor_type = OuterExecutor;
    using inner_executor_type = InnerExecutor;

  private:
    using inner_traits = executor_traits<inner_executor_type>;
    using outer_traits = executor_traits<outer_executor_type>;

    using outer_execution_category = typename outer_traits::execution_category;
    using inner_execution_category = typename inner_traits::execution_category;

    constexpr static size_t inner_depth = inner_traits::execution_depth;

  public:
    using execution_category = nested_execution_tag<outer_execution_category,inner_execution_category>;

    using outer_shape_type = typename outer_traits::shape_type;
    using inner_shape_type = typename inner_traits::shape_type;

    using outer_index_type = typename outer_traits::index_type;
    using inner_index_type = typename inner_traits::index_type;

    using shape_type = detail::nested_shape_t<outer_execution_category,inner_execution_category,outer_shape_type,inner_shape_type>;
    using index_type = detail::nested_index_t<outer_execution_category,inner_execution_category,outer_index_type,inner_index_type>;

    __AGENCY_ANNOTATION
    static shape_type make_shape(const outer_shape_type& outer_shape, const inner_shape_type& inner_shape)
    {
      return detail::make_nested_shape<outer_execution_category,inner_execution_category>(outer_shape, inner_shape);
    }

    __AGENCY_ANNOTATION
    executor_array() = default;

    __AGENCY_ANNOTATION
    executor_array(const executor_array&) = default;

    __AGENCY_ANNOTATION
    executor_array(size_t n, const inner_executor_type& exec = inner_executor_type())
      : inner_executors_(n, exec)
    {}

    template<class T>
    using future = typename outer_traits::template future<T>;

    template<class T>
    using allocator = typename outer_traits::template allocator<T>;

    template<class T>
    using container = agency::detail::array<T, shape_type, allocator<T>>;

  private:
    template<class Futures, class UniquePtr1, class UniquePtr2>
    struct wait_for_futures_and_move_result
    {
      mutable Futures    futures_;
      mutable UniquePtr1 result_ptr_;
      mutable UniquePtr2 shared_arg_ptr_;

      __AGENCY_ANNOTATION
      typename std::pointer_traits<UniquePtr1>::element_type
        operator()() const
      {
        for(auto& f : futures_)
        {
          f.wait();
        }

        return std::move(*result_ptr_);
      }
    };

    template<class Futures, class UniquePtr1, class UniquePtr2>
    __AGENCY_ANNOTATION
    wait_for_futures_and_move_result<typename std::decay<Futures>::type,UniquePtr1,UniquePtr2>
      make_wait_for_futures_and_move_result(Futures&& futures, UniquePtr1&& result_ptr, UniquePtr2&& shared_arg_ptr)
    {
      return wait_for_futures_and_move_result<typename std::decay<Futures>::type,UniquePtr1,UniquePtr2>{std::move(futures),std::move(result_ptr),std::move(shared_arg_ptr)};
    }

    __AGENCY_ANNOTATION
    static outer_shape_type outer_shape(const shape_type& shape)
    {
      // the outer portion is always the head of the tuple
      return __tu::tuple_head(shape);
    }

    __AGENCY_ANNOTATION
    static inner_shape_type inner_shape(const shape_type& shape)
    {
      // the inner portion is the tail of the tuple, but if the
      // inner executor is not nested, then the tuple needs to be unwrapped
      return detail::unwrap_tuple_if_not_nested<inner_execution_category>(detail::forward_tail(shape));
    }

    __AGENCY_ANNOTATION
    static index_type make_index(const outer_index_type& outer_idx, const inner_index_type& inner_idx)
    {
      return detail::make_nested_index<outer_execution_category,inner_execution_category>(outer_idx, inner_idx);
    }

    __AGENCY_ANNOTATION
    size_t select_inner_executor(const outer_index_type& idx, const outer_shape_type& shape) const
    {
      size_t rank = detail::index_cast<size_t>(idx, shape, inner_executors_.size());

      // round robin through inner executors
      return rank % inner_executors_.size();
    }

  public:
    // XXX this functor is public to allow nvcc to instantiate kernels with it
    template<class Function, class... Factories>
    struct then_execute_functor
    {
      executor_array& exec;
      mutable Function f;
      detail::tuple<Factories...> inner_factories;
      outer_shape_type outer_shape;
      inner_shape_type inner_shape;

      template<class T1, class... Types>
      struct inner_functor
      {
        executor_array& exec;
        mutable Function f;
        outer_index_type outer_idx;
        T1& results;
        detail::tuple<Types&...> outer_args;

        template<size_t... Indices, class... InnerArgs>
        __AGENCY_ANNOTATION
        void impl(detail::index_sequence<Indices...>, const inner_index_type& inner_idx, InnerArgs&... inner_args) const
        {
          auto idx = exec.make_index(outer_idx, inner_idx);

          results[idx] = agency::invoke(f, idx, detail::get<Indices>(outer_args)..., inner_args...);
        }

        template<class... Args>
        __AGENCY_ANNOTATION
        void operator()(const inner_index_type& inner_idx, Args&... inner_shared_args) const
        {
          impl(detail::index_sequence_for<Types...>(), inner_idx, inner_shared_args...);
        }
      };

      template<size_t... Indices, class Arg1, class... Args>
      __AGENCY_ANNOTATION
      void impl(detail::index_sequence<Indices...>, const outer_index_type& outer_idx, Arg1& results, Args&... outer_args) const
      {
        auto inner_executor_idx = exec.select_inner_executor(outer_idx, outer_shape);

        inner_traits::execute(
          exec.inner_executor(inner_executor_idx),
          inner_functor<Arg1,Args...>{exec,f,outer_idx,results,detail::tie(outer_args...)},
          inner_shape,
          detail::get<Indices>(inner_factories)...
        );
      }

      template<class Arg1, class... Args>
      __AGENCY_ANNOTATION
      void operator()(const outer_index_type& outer_idx, Arg1& results, Args&... outer_args) const
      {
        impl(detail::index_sequence_for<Factories...>(), outer_idx, results, outer_args...);
      }
    };

    template<class Function, class Factory1, class Future, class Factory2, class... Factories,
             class = typename std::enable_if<
               sizeof...(Factories) == inner_depth
             >::type>
    future<typename std::result_of<Factory1(shape_type)>::type>
      then_execute(Function f, Factory1 result_factory, shape_type shape, Future& past, Factory2 outer_factory, Factories... inner_factories)
    {
      // separate the shape into inner and outer portions
      auto outer_shape = this->outer_shape(shape);
      auto inner_shape = this->inner_shape(shape);

      using result_type = decltype(result_factory(shape));
      auto results_fut = outer_traits::template make_ready_future<result_type>(outer_executor(), result_factory(shape));
      auto futures = agency::detail::make_tuple(std::move(results_fut), std::move(past));

      // XXX avoid lambdas to workaround nvcc limitation
      //using past_arg_type = typename future_traits<Future>::value_type;
      //using outer_shared_arg_type = decltype(outer_factory());

      //return outer_traits::template when_all_execute_and_select<0>(outer_executor(), [=](const outer_index_type& outer_idx, result_type& results, past_arg_type& past_arg, outer_shared_arg_type& outer_shared_arg) mutable
      //{
      //  auto inner_executor_idx = select_inner_executor(outer_idx, outer_shape);

      //  inner_traits::execute(inner_executor(inner_executor_idx), [=,&outer_shared_arg,&results](const inner_index_type& inner_idx, decltype(inner_factories())&... inner_shared_args) mutable
      //  {
      //    auto idx = make_index(outer_idx, inner_idx);
      //    results[idx] = agency::invoke(f, idx, past_arg, outer_shared_arg, inner_shared_args...);
      //  },
      //  inner_shape,
      //  inner_factories...);
      //},
      //outer_shape,
      //futures,
      //outer_factory);

      auto functor = then_execute_functor<Function,Factories...>{*this, f, detail::make_tuple(inner_factories...), outer_shape, inner_shape};
      return outer_traits::template when_all_execute_and_select<0>(outer_executor(), functor, outer_shape, futures, outer_factory);
    }

    template<class Function, class T1, class T2, class... Factories>
    struct async_execute_functor
    {
      executor_array& exec;
      mutable Function f;
      T1* results_ptr;
      T2* outer_shared_arg_ptr;
      detail::tuple<Factories...> inner_factories;
      outer_shape_type outer_shape;
      inner_shape_type inner_shape;

      struct inner_functor
      {
        executor_array& exec;
        mutable Function f;
        outer_index_type outer_idx;
        T1& results;
        T2& outer_arg;

        template<class... Args>
        __AGENCY_ANNOTATION
        void operator()(const inner_index_type& inner_idx, Args&... inner_shared_args) const
        {
          auto idx = exec.make_index(outer_idx, inner_idx);

          results[idx] = agency::invoke(f, idx, outer_arg, inner_shared_args...);
        }
      };

      template<size_t... Indices>
      __AGENCY_ANNOTATION
      typename inner_traits::template future<void>
        impl(detail::index_sequence<Indices...>, const outer_index_type& outer_idx) const
      {
        auto inner_executor_idx = exec.select_inner_executor(outer_idx, outer_shape);

        return inner_traits::async_execute(
          exec.inner_executor(inner_executor_idx),
          inner_functor{exec,f,outer_idx,*results_ptr,*outer_shared_arg_ptr},
          inner_shape,
          detail::get<Indices>(inner_factories)...
        );
      }

      __AGENCY_ANNOTATION
      typename inner_traits::template future<void>
        operator()(const outer_index_type& outer_idx) const
      {
        return impl(detail::index_sequence_for<Factories...>(), outer_idx);
      }
    };

    // XXX this implementation is only valid for outer_execution_category != sequential_execution_tag
    template<class Function, class Factory1, class Factory2, class... Factories,
             class = typename std::enable_if<
               sizeof...(Factories) == inner_depth
             >::type>
    future<typename std::result_of<Factory1(shape_type)>::type>
      async_execute(Function f, Factory1 result_factory, shape_type shape, Factory2 outer_factory, Factories... inner_factories)
    {
      // separate the shape into inner and outer portions
      auto outer_shape = this->outer_shape(shape);
      auto inner_shape = this->inner_shape(shape);

      // create the results via the result_factory
      using result_type = decltype(result_factory(shape));
      auto results_ptr = detail::allocate_unique(allocator<result_type>(), result_factory(shape));
      result_type* results_raw_ptr = results_ptr.get();

      // create the outer shared argument via the outer_factory
      using outer_shared_arg_type = decltype(outer_factory());
      auto outer_shared_arg_ptr = detail::allocate_unique(allocator<outer_shared_arg_type>(), outer_factory());
      outer_shared_arg_type* outer_shared_arg_raw_ptr = outer_shared_arg_ptr.get();

      // eagerly execute() with the outer executor so that these async_execute() calls issue immediately

      // XXX avoid lambdas to workaround nvcc limitation
      //auto futures = outer_traits::execute(outer_executor(), [=](const outer_index_type& outer_idx) mutable
      //{
      //  auto inner_executor_idx = select_inner_executor(outer_idx, outer_shape);

      //  return inner_traits::async_execute(inner_executor(inner_executor_idx), [=](const inner_index_type& inner_idx, decltype(inner_factories())&... inner_shared_args) mutable
      //  {
      //    auto idx = make_index(outer_idx, inner_idx);
      //    (*results_raw_ptr)[idx] = agency::invoke(f, idx, *outer_shared_arg_raw_ptr, inner_shared_args...);
      //  },
      //  inner_shape,
      //  inner_factories...);
      //},
      //outer_shape);

      auto functor = async_execute_functor<Function,result_type,outer_shared_arg_type,Factories...>{*this, f, results_raw_ptr, outer_shared_arg_raw_ptr, detail::make_tuple(inner_factories...), outer_shape, inner_shape};
      auto futures = outer_traits::execute(outer_executor(), functor, outer_shape);

      // create a continuation to synchronize the futures and return the result
      auto continuation = make_wait_for_futures_and_move_result(std::move(futures), std::move(results_ptr), std::move(outer_shared_arg_ptr));

      // async_execute() with the outer executor to launch the continuation
      return outer_traits::async_execute(outer_executor(), std::move(continuation));
    }

  private:
    outer_executor_type            outer_executor_;

    // XXX consider using container here instead of array
    agency::detail::array<inner_executor_type, size_t, allocator<inner_executor_type>> inner_executors_;

  public:
    __AGENCY_ANNOTATION
    auto begin() ->
      decltype(inner_executors_.begin())
    {
      return inner_executors_.begin();
    }

    __AGENCY_ANNOTATION
    auto begin() const ->
      decltype(inner_executors_.begin())
    {
      return inner_executors_.begin();
    }

    __AGENCY_ANNOTATION
    auto end() ->
      decltype(inner_executors_.end())
    {
      return inner_executors_.end();
    }

    __AGENCY_ANNOTATION
    auto end() const ->
      decltype(inner_executors_.end())
    {
      return inner_executors_.end();
    }

    __AGENCY_ANNOTATION
    size_t size() const
    {
      return inner_executors_.size();
    }

    __AGENCY_ANNOTATION
    outer_executor_type& outer_executor()
    {
      return outer_executor_;
    }

    __AGENCY_ANNOTATION
    const outer_executor_type& outer_executor() const
    {
      return outer_executor_;
    }

    __AGENCY_ANNOTATION
    inner_executor_type& inner_executor(size_t i)
    {
      return begin()[i];
    }

    __AGENCY_ANNOTATION
    inner_executor_type& inner_executor(size_t i) const
    {
      return begin()[i];
    }
};

} // end agency

<commit_msg>Implement async_execute() for sequential outer executors<commit_after>#pragma once

#include <agency/parallel_executor.hpp>
#include <agency/detail/array.hpp>
#include <agency/detail/shape_tuple.hpp>
#include <agency/detail/index_tuple.hpp>
#include <agency/functional.hpp>

namespace agency
{

template<class InnerExecutor, class OuterExecutor = this_thread::parallel_executor>
class executor_array
{
  public:
    using outer_executor_type = OuterExecutor;
    using inner_executor_type = InnerExecutor;

  private:
    using inner_traits = executor_traits<inner_executor_type>;
    using outer_traits = executor_traits<outer_executor_type>;

    using outer_execution_category = typename outer_traits::execution_category;
    using inner_execution_category = typename inner_traits::execution_category;

    constexpr static size_t inner_depth = inner_traits::execution_depth;

  public:
    using execution_category = nested_execution_tag<outer_execution_category,inner_execution_category>;

    using outer_shape_type = typename outer_traits::shape_type;
    using inner_shape_type = typename inner_traits::shape_type;

    using outer_index_type = typename outer_traits::index_type;
    using inner_index_type = typename inner_traits::index_type;

    using shape_type = detail::nested_shape_t<outer_execution_category,inner_execution_category,outer_shape_type,inner_shape_type>;
    using index_type = detail::nested_index_t<outer_execution_category,inner_execution_category,outer_index_type,inner_index_type>;

    __AGENCY_ANNOTATION
    static shape_type make_shape(const outer_shape_type& outer_shape, const inner_shape_type& inner_shape)
    {
      return detail::make_nested_shape<outer_execution_category,inner_execution_category>(outer_shape, inner_shape);
    }

    __AGENCY_ANNOTATION
    executor_array() = default;

    __AGENCY_ANNOTATION
    executor_array(const executor_array&) = default;

    __AGENCY_ANNOTATION
    executor_array(size_t n, const inner_executor_type& exec = inner_executor_type())
      : inner_executors_(n, exec)
    {}

    template<class T>
    using future = typename outer_traits::template future<T>;

    template<class T>
    using allocator = typename outer_traits::template allocator<T>;

    template<class T>
    using container = agency::detail::array<T, shape_type, allocator<T>>;

  private:
    template<class Futures, class UniquePtr1, class UniquePtr2>
    struct wait_for_futures_and_move_result
    {
      mutable Futures    futures_;
      mutable UniquePtr1 result_ptr_;
      mutable UniquePtr2 shared_arg_ptr_;

      __AGENCY_ANNOTATION
      typename std::pointer_traits<UniquePtr1>::element_type
        operator()() const
      {
        for(auto& f : futures_)
        {
          f.wait();
        }

        return std::move(*result_ptr_);
      }
    };

    template<class Futures, class UniquePtr1, class UniquePtr2>
    __AGENCY_ANNOTATION
    wait_for_futures_and_move_result<typename std::decay<Futures>::type,UniquePtr1,UniquePtr2>
      make_wait_for_futures_and_move_result(Futures&& futures, UniquePtr1&& result_ptr, UniquePtr2&& shared_arg_ptr)
    {
      return wait_for_futures_and_move_result<typename std::decay<Futures>::type,UniquePtr1,UniquePtr2>{std::move(futures),std::move(result_ptr),std::move(shared_arg_ptr)};
    }

    __AGENCY_ANNOTATION
    static outer_shape_type outer_shape(const shape_type& shape)
    {
      // the outer portion is always the head of the tuple
      return __tu::tuple_head(shape);
    }

    __AGENCY_ANNOTATION
    static inner_shape_type inner_shape(const shape_type& shape)
    {
      // the inner portion is the tail of the tuple, but if the
      // inner executor is not nested, then the tuple needs to be unwrapped
      return detail::unwrap_tuple_if_not_nested<inner_execution_category>(detail::forward_tail(shape));
    }

    __AGENCY_ANNOTATION
    static index_type make_index(const outer_index_type& outer_idx, const inner_index_type& inner_idx)
    {
      return detail::make_nested_index<outer_execution_category,inner_execution_category>(outer_idx, inner_idx);
    }

    __AGENCY_ANNOTATION
    size_t select_inner_executor(const outer_index_type& idx, const outer_shape_type& shape) const
    {
      size_t rank = detail::index_cast<size_t>(idx, shape, inner_executors_.size());

      // round robin through inner executors
      return rank % inner_executors_.size();
    }

  public:
    // XXX this functor is public to allow nvcc to instantiate kernels with it
    template<class Function, class... Factories>
    struct then_execute_functor
    {
      executor_array& exec;
      mutable Function f;
      detail::tuple<Factories...> inner_factories;
      outer_shape_type outer_shape;
      inner_shape_type inner_shape;

      template<class T1, class... Types>
      struct inner_functor
      {
        executor_array& exec;
        mutable Function f;
        outer_index_type outer_idx;
        T1& results;
        detail::tuple<Types&...> outer_args;

        template<size_t... Indices, class... InnerArgs>
        __AGENCY_ANNOTATION
        void impl(detail::index_sequence<Indices...>, const inner_index_type& inner_idx, InnerArgs&... inner_args) const
        {
          auto idx = exec.make_index(outer_idx, inner_idx);

          results[idx] = agency::invoke(f, idx, detail::get<Indices>(outer_args)..., inner_args...);
        }

        template<class... Args>
        __AGENCY_ANNOTATION
        void operator()(const inner_index_type& inner_idx, Args&... inner_shared_args) const
        {
          impl(detail::index_sequence_for<Types...>(), inner_idx, inner_shared_args...);
        }
      };

      template<size_t... Indices, class Arg1, class... Args>
      __AGENCY_ANNOTATION
      void impl(detail::index_sequence<Indices...>, const outer_index_type& outer_idx, Arg1& results, Args&... outer_args) const
      {
        auto inner_executor_idx = exec.select_inner_executor(outer_idx, outer_shape);

        inner_traits::execute(
          exec.inner_executor(inner_executor_idx),
          inner_functor<Arg1,Args...>{exec,f,outer_idx,results,detail::tie(outer_args...)},
          inner_shape,
          detail::get<Indices>(inner_factories)...
        );
      }

      template<class Arg1, class... Args>
      __AGENCY_ANNOTATION
      void operator()(const outer_index_type& outer_idx, Arg1& results, Args&... outer_args) const
      {
        impl(detail::index_sequence_for<Factories...>(), outer_idx, results, outer_args...);
      }
    };

    template<class Function, class Factory1, class Future, class Factory2, class... Factories,
             class = typename std::enable_if<
               sizeof...(Factories) == inner_depth
             >::type>
    future<typename std::result_of<Factory1(shape_type)>::type>
      then_execute(Function f, Factory1 result_factory, shape_type shape, Future& past, Factory2 outer_factory, Factories... inner_factories)
    {
      // separate the shape into inner and outer portions
      auto outer_shape = this->outer_shape(shape);
      auto inner_shape = this->inner_shape(shape);

      using result_type = decltype(result_factory(shape));
      auto results_fut = outer_traits::template make_ready_future<result_type>(outer_executor(), result_factory(shape));
      auto futures = agency::detail::make_tuple(std::move(results_fut), std::move(past));

      // XXX avoid lambdas to workaround nvcc limitation
      //using past_arg_type = typename future_traits<Future>::value_type;
      //using outer_shared_arg_type = decltype(outer_factory());

      //return outer_traits::template when_all_execute_and_select<0>(outer_executor(), [=](const outer_index_type& outer_idx, result_type& results, past_arg_type& past_arg, outer_shared_arg_type& outer_shared_arg) mutable
      //{
      //  auto inner_executor_idx = select_inner_executor(outer_idx, outer_shape);

      //  inner_traits::execute(inner_executor(inner_executor_idx), [=,&outer_shared_arg,&results](const inner_index_type& inner_idx, decltype(inner_factories())&... inner_shared_args) mutable
      //  {
      //    auto idx = make_index(outer_idx, inner_idx);
      //    results[idx] = agency::invoke(f, idx, past_arg, outer_shared_arg, inner_shared_args...);
      //  },
      //  inner_shape,
      //  inner_factories...);
      //},
      //outer_shape,
      //futures,
      //outer_factory);

      auto functor = then_execute_functor<Function,Factories...>{*this, f, detail::make_tuple(inner_factories...), outer_shape, inner_shape};
      return outer_traits::template when_all_execute_and_select<0>(outer_executor(), functor, outer_shape, futures, outer_factory);
    }

    template<class Function, class T1, class T2, class... Factories>
    struct async_execute_functor
    {
      executor_array& exec;
      mutable Function f;
      T1* results_ptr;
      T2* outer_shared_arg_ptr;
      detail::tuple<Factories...> inner_factories;
      outer_shape_type outer_shape;
      inner_shape_type inner_shape;

      struct inner_functor
      {
        executor_array& exec;
        mutable Function f;
        outer_index_type outer_idx;
        T1& results;
        T2& outer_arg;

        template<class... Args>
        __AGENCY_ANNOTATION
        void operator()(const inner_index_type& inner_idx, Args&... inner_shared_args) const
        {
          auto idx = exec.make_index(outer_idx, inner_idx);

          results[idx] = agency::invoke(f, idx, outer_arg, inner_shared_args...);
        }
      };

      template<size_t... Indices>
      __AGENCY_ANNOTATION
      typename inner_traits::template future<void>
        impl(detail::index_sequence<Indices...>, const outer_index_type& outer_idx) const
      {
        auto inner_executor_idx = exec.select_inner_executor(outer_idx, outer_shape);

        return inner_traits::async_execute(
          exec.inner_executor(inner_executor_idx),
          inner_functor{exec,f,outer_idx,*results_ptr,*outer_shared_arg_ptr},
          inner_shape,
          detail::get<Indices>(inner_factories)...
        );
      }

      __AGENCY_ANNOTATION
      typename inner_traits::template future<void>
        operator()(const outer_index_type& outer_idx) const
      {
        return impl(detail::index_sequence_for<Factories...>(), outer_idx);
      }
    };

    // when the outer executor is sequential, we can't eagerly issue the inner async_execute()s like we do in the other implementation
    template<class Function, class Factory1, class Factory2, class... Factories,
             class = typename std::enable_if<
               sizeof...(Factories) == inner_depth
             >::type>
    future<typename std::result_of<Factory1(shape_type)>::type>
      async_execute_impl(sequential_execution_tag, Function f, Factory1 result_factory, shape_type shape, Factory2 outer_factory, Factories... inner_factories)
    {
      auto ready = outer_traits::template make_ready_future<void>(outer_executor());

      return then_execute(f, result_factory, shape, ready, outer_factory, inner_factories...);
    }

    // this implementation is only valid for outer_execution_category != sequential_execution_tag
    template<class ExecutionCategory, class Function, class Factory1, class Factory2, class... Factories,
             class = typename std::enable_if<
               sizeof...(Factories) == inner_depth
             >::type>
    future<typename std::result_of<Factory1(shape_type)>::type>
      async_execute_impl(ExecutionCategory, Function f, Factory1 result_factory, shape_type shape, Factory2 outer_factory, Factories... inner_factories)
    {
      // separate the shape into inner and outer portions
      auto outer_shape = this->outer_shape(shape);
      auto inner_shape = this->inner_shape(shape);

      // create the results via the result_factory
      using result_type = decltype(result_factory(shape));
      auto results_ptr = detail::allocate_unique(allocator<result_type>(), result_factory(shape));
      result_type* results_raw_ptr = results_ptr.get();

      // create the outer shared argument via the outer_factory
      using outer_shared_arg_type = decltype(outer_factory());
      auto outer_shared_arg_ptr = detail::allocate_unique(allocator<outer_shared_arg_type>(), outer_factory());
      outer_shared_arg_type* outer_shared_arg_raw_ptr = outer_shared_arg_ptr.get();

      // eagerly execute() with the outer executor so that these async_execute() calls issue immediately

      // XXX avoid lambdas to workaround nvcc limitation
      //auto futures = outer_traits::execute(outer_executor(), [=](const outer_index_type& outer_idx) mutable
      //{
      //  auto inner_executor_idx = select_inner_executor(outer_idx, outer_shape);

      //  return inner_traits::async_execute(inner_executor(inner_executor_idx), [=](const inner_index_type& inner_idx, decltype(inner_factories())&... inner_shared_args) mutable
      //  {
      //    auto idx = make_index(outer_idx, inner_idx);
      //    (*results_raw_ptr)[idx] = agency::invoke(f, idx, *outer_shared_arg_raw_ptr, inner_shared_args...);
      //  },
      //  inner_shape,
      //  inner_factories...);
      //},
      //outer_shape);

      auto functor = async_execute_functor<Function,result_type,outer_shared_arg_type,Factories...>{*this, f, results_raw_ptr, outer_shared_arg_raw_ptr, detail::make_tuple(inner_factories...), outer_shape, inner_shape};
      auto futures = outer_traits::execute(outer_executor(), functor, outer_shape);

      // create a continuation to synchronize the futures and return the result
      auto continuation = make_wait_for_futures_and_move_result(std::move(futures), std::move(results_ptr), std::move(outer_shared_arg_ptr));

      // async_execute() with the outer executor to launch the continuation
      return outer_traits::async_execute(outer_executor(), std::move(continuation));
    }

  public:
    template<class Function, class Factory1, class Factory2, class... Factories,
             class = typename std::enable_if<
               sizeof...(Factories) == inner_depth
             >::type>
    future<typename std::result_of<Factory1(shape_type)>::type>
      async_execute(Function f, Factory1 result_factory, shape_type shape, Factory2 outer_factory, Factories... inner_factories)
    {
      return async_execute_impl(outer_execution_category(), f, result_factory, shape, outer_factory, inner_factories...);
    }

  private:
    outer_executor_type            outer_executor_;

    // XXX consider using container here instead of array
    agency::detail::array<inner_executor_type, size_t, allocator<inner_executor_type>> inner_executors_;

  public:
    __AGENCY_ANNOTATION
    auto begin() ->
      decltype(inner_executors_.begin())
    {
      return inner_executors_.begin();
    }

    __AGENCY_ANNOTATION
    auto begin() const ->
      decltype(inner_executors_.begin())
    {
      return inner_executors_.begin();
    }

    __AGENCY_ANNOTATION
    auto end() ->
      decltype(inner_executors_.end())
    {
      return inner_executors_.end();
    }

    __AGENCY_ANNOTATION
    auto end() const ->
      decltype(inner_executors_.end())
    {
      return inner_executors_.end();
    }

    __AGENCY_ANNOTATION
    size_t size() const
    {
      return inner_executors_.size();
    }

    __AGENCY_ANNOTATION
    outer_executor_type& outer_executor()
    {
      return outer_executor_;
    }

    __AGENCY_ANNOTATION
    const outer_executor_type& outer_executor() const
    {
      return outer_executor_;
    }

    __AGENCY_ANNOTATION
    inner_executor_type& inner_executor(size_t i)
    {
      return begin()[i];
    }

    __AGENCY_ANNOTATION
    inner_executor_type& inner_executor(size_t i) const
    {
      return begin()[i];
    }
};

} // end agency

<|endoftext|>