<commit_before>#ifndef TOPAZ_GL_IMPL_BACKEND_VK2_IMAGE_HPP
#define TOPAZ_GL_IMPL_BACKEND_VK2_IMAGE_HPP
#if TZ_VULKAN
#include "core/vector.hpp"
#include "core/containers/enum_field.hpp"
#include "gl/impl/backend/vk2/image_format.hpp"
#include "gl/impl/backend/vk2/gpu_mem.hpp"
#include "vk_mem_alloc.h"
#include <optional>

namespace tz::gl::vk2
{
	/**
	 * @ingroup tz_gl_vk_image
	 * Images are always in a layout. Images can only perform certain operations in a given layout.
	 */
	enum class ImageLayout
	{
		/// - Cannot be used for most operations. If used instead of the image's true layout in a layout transition, the contents of the image's memory will become undefined.
		Undefined = VK_IMAGE_LAYOUT_UNDEFINED,
		/// - Supports all operations, however is unlikely to perform optimally for any such operations.
		General = VK_IMAGE_LAYOUT_GENERAL,
		/// - Only useable as a colour/resolve attachment within a @ref Framebuffer.
		ColourAttachment = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
		/// - Only useable as a depth/stencil attachment.
		DepthStencilAttachment = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
		/// - Read-only access in a @ref Shader as a sampled image.
		ShaderResource = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
		/// - Only useable as a source image of some transfer command.
		TransferSource = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
		/// - Only useable as a destination image of some transfer command.
		TransferDestination = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
		/// - Useable as a presentable image.
		Present = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies the number of samples stored per image pixel.
	 */
	enum class SampleCount
	{
		One = VK_SAMPLE_COUNT_1_BIT,
		Two = VK_SAMPLE_COUNT_2_BIT,
		Four = VK_SAMPLE_COUNT_4_BIT,
		Eight = VK_SAMPLE_COUNT_8_BIT,
		Sixteen = VK_SAMPLE_COUNT_16_BIT,
		ThirtyTwo = VK_SAMPLE_COUNT_32_BIT,
		SixtyFour = VK_SAMPLE_COUNT_64_BIT
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies how the image is laid out in memory.
	 */
	enum class ImageTiling
	{
		/// Image texels are laid out in an implementation-defined manner.
		Optimal = VK_IMAGE_TILING_OPTIMAL,
		/// Image texels are laid out in memory in row-major order, possibly with some padding on each row.
		Linear = VK_IMAGE_TILING_LINEAR
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies intended usage of an @ref Image.
	 */
	enum class ImageUsage
	{
		/// - Image can be used as a source in a transfer command.
		TransferSource = VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
		/// - Image can be used as a destination in a transfer command.
		TransferDestination = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
		/// - Image can be used as a read-only shader resource.
		SampledImage = VK_IMAGE_USAGE_SAMPLED_BIT,
		/// - Image can be used as a read/write shader resource.
		StorageImage = VK_IMAGE_USAGE_STORAGE_BIT,
		/// - Image is suitable as a colour attachment within a @ref Framebuffer.
		ColourAttachment = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
		/// - Image is suitable as a depth/stencil attachment within a @ref Framebuffer.
		DepthStencilAttachment = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
		/// - Image is suitable as an input attachment within a @ref Framebuffer.
		InputAttachment = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT
	};

	using ImageUsageField = tz::EnumField<ImageUsage>;

	class Swapchain;
	class LogicalDevice;
	namespace hardware
	{
		class Queue;
	}

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies parameters of an Image referring to an existing Swapchain image.
	 */
	struct SwapchainImageInfo
	{
		/// Swapchain owning this image. Must not be null.
		const Swapchain* swapchain;
		/// Swapchains have a variable number of images. This is the index of the image collection owned by `swapchain`.
		std::uint32_t image_index;
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies creation flags for an @ref Image.
	 */
	struct ImageInfo
	{
		/// LogicalDevice owning this image. Must not be null.
		const LogicalDevice* device;
		/// Format of the image.
		ImageFormat format;
		/// Dimensions of the image, in pixels.
		tz::Vec2ui dimensions;
		/// Field of expected usages of the image. For example, if you wish to transition the image to @ref ImageLayout::TransferTo then this usage field must contain @ref ImageUsage::TransferDestination.
		ImageUsageField usage;
		/// Describes where the image is laid out in memory.
		MemoryResidency residency;
		/// Specifies how many mip levels there are. Default 1.
		std::uint32_t mip_levels = 1;
		/// Specifies how many layers there are. Default 1.
		std::uint32_t array_layers = 1;
		/// Specifies how many times the image is sampled per pixel. Default 1.
		SampleCount sample_count = SampleCount::One;
		/// Specifies image tiling. Default ImageTiling::Optimal.
		ImageTiling image_tiling = ImageTiling::Optimal;
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Represents an Image owned by the Vulkan API. This includes Swapchain images!
	 */
	class Image
	{
	public:
		/**
		 * Create an Image that refers to a Swapchain image.
		 * @param info Information about the Swapchain and which image to refer to.
		 */
		Image(SwapchainImageInfo sinfo);
		Image(ImageInfo info);
		Image(const Image& copy) = delete;
		Image(Image&& move);
		~Image();

		Image& operator=(const Image& rhs) = delete;
		Image& operator=(Image&& rhs);

		/**
		 * Retrieve the underlying format of the image.
		 */
		ImageFormat get_format() const;
		/**
		 * Retrieve the current layout of the image.
		 */
		ImageLayout get_layout() const;
		/**
		 * Retrieve the dimensions of the image.
		 * @return {width, height} of the image, in pixels.
		 */
		Vec2ui get_dimensions() const;
		/**
		 * Retrieve the @ref LogicalDevice that 'owns' the image.
		 *
		 * Swapchain images are owned by the presentation device. For that reason, these do not belong to this device per-se. In this case this will return the @ref LogicalDevice responsible for retrieving the image (Most certainly the LogicalDevice that initially spawned its owner Swapchain.)
		 */
		const LogicalDevice& get_device() const;

		void* map();

		template<typename T>
		std::span<T> map_as()
		{
			auto* ptr = reinterpret_cast<T*>(this->map());
			if(ptr == nullptr)
			{
				return {ptr, 0};
			}
			return {ptr, this->vma_alloc_info.size / sizeof(T)};
		}

		void unmap();
		std::size_t get_linear_row_length() const;

		std::string debug_get_name() const;
		void debug_set_name(std::string name);

		using NativeType = VkImage;
		NativeType native() const;
		static Image null();
		bool is_null() const;

		bool operator==(const Image& rhs) const;

		friend class hardware::Queue;
	private:
		Image();
		void set_layout(ImageLayout layout);

		VkImage image;
		ImageFormat format;
		ImageLayout layout;
		ImageTiling tiling;
		MemoryResidency residency;
		Vec2ui dimensions;
		const LogicalDevice* device;
		bool destroy_on_destructor;
		std::optional<VmaAllocation> vma_alloc;
		VmaAllocationInfo vma_alloc_info;
		std::string debug_name = "";
	};
}

#endif // TZ_VULKAN
#endif // TOPAZ_GL_IMPL_BACKEND_VK2_IMAGE_HPP
<commit_msg>* Fixed missing <string> include<commit_after>#ifndef TOPAZ_GL_IMPL_BACKEND_VK2_IMAGE_HPP
#define TOPAZ_GL_IMPL_BACKEND_VK2_IMAGE_HPP
#if TZ_VULKAN
#include "core/vector.hpp"
#include "core/containers/enum_field.hpp"
#include "gl/impl/backend/vk2/image_format.hpp"
#include "gl/impl/backend/vk2/gpu_mem.hpp"
#include "vk_mem_alloc.h"
#include <optional>
#include <string>

namespace tz::gl::vk2
{
	/**
	 * @ingroup tz_gl_vk_image
	 * Images are always in a layout. Images can only perform certain operations in a given layout.
	 */
	enum class ImageLayout
	{
		/// - Cannot be used for most operations. If used instead of the image's true layout in a layout transition, the contents of the image's memory will become undefined.
		Undefined = VK_IMAGE_LAYOUT_UNDEFINED,
		/// - Supports all operations, however is unlikely to perform optimally for any such operations.
		General = VK_IMAGE_LAYOUT_GENERAL,
		/// - Only useable as a colour/resolve attachment within a @ref Framebuffer.
		ColourAttachment = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
		/// - Only useable as a depth/stencil attachment.
		DepthStencilAttachment = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
		/// - Read-only access in a @ref Shader as a sampled image.
		ShaderResource = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
		/// - Only useable as a source image of some transfer command.
		TransferSource = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
		/// - Only useable as a destination image of some transfer command.
		TransferDestination = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
		/// - Useable as a presentable image.
		Present = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies the number of samples stored per image pixel.
	 */
	enum class SampleCount
	{
		One = VK_SAMPLE_COUNT_1_BIT,
		Two = VK_SAMPLE_COUNT_2_BIT,
		Four = VK_SAMPLE_COUNT_4_BIT,
		Eight = VK_SAMPLE_COUNT_8_BIT,
		Sixteen = VK_SAMPLE_COUNT_16_BIT,
		ThirtyTwo = VK_SAMPLE_COUNT_32_BIT,
		SixtyFour = VK_SAMPLE_COUNT_64_BIT
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies how the image is laid out in memory.
	 */
	enum class ImageTiling
	{
		/// Image texels are laid out in an implementation-defined manner.
		Optimal = VK_IMAGE_TILING_OPTIMAL,
		/// Image texels are laid out in memory in row-major order, possibly with some padding on each row.
		Linear = VK_IMAGE_TILING_LINEAR
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies intended usage of an @ref Image.
	 */
	enum class ImageUsage
	{
		/// - Image can be used as a source in a transfer command.
		TransferSource = VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
		/// - Image can be used as a destination in a transfer command.
		TransferDestination = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
		/// - Image can be used as a read-only shader resource.
		SampledImage = VK_IMAGE_USAGE_SAMPLED_BIT,
		/// - Image can be used as a read/write shader resource.
		StorageImage = VK_IMAGE_USAGE_STORAGE_BIT,
		/// - Image is suitable as a colour attachment within a @ref Framebuffer.
		ColourAttachment = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
		/// - Image is suitable as a depth/stencil attachment within a @ref Framebuffer.
		DepthStencilAttachment = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
		/// - Image is suitable as an input attachment within a @ref Framebuffer.
		InputAttachment = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT
	};

	using ImageUsageField = tz::EnumField<ImageUsage>;

	class Swapchain;
	class LogicalDevice;
	namespace hardware
	{
		class Queue;
	}

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies parameters of an Image referring to an existing Swapchain image.
	 */
	struct SwapchainImageInfo
	{
		/// Swapchain owning this image. Must not be null.
		const Swapchain* swapchain;
		/// Swapchains have a variable number of images. This is the index of the image collection owned by `swapchain`.
		std::uint32_t image_index;
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Specifies creation flags for an @ref Image.
	 */
	struct ImageInfo
	{
		/// LogicalDevice owning this image. Must not be null.
		const LogicalDevice* device;
		/// Format of the image.
		ImageFormat format;
		/// Dimensions of the image, in pixels.
		tz::Vec2ui dimensions;
		/// Field of expected usages of the image. For example, if you wish to transition the image to @ref ImageLayout::TransferTo then this usage field must contain @ref ImageUsage::TransferDestination.
		ImageUsageField usage;
		/// Describes where the image is laid out in memory.
		MemoryResidency residency;
		/// Specifies how many mip levels there are. Default 1.
		std::uint32_t mip_levels = 1;
		/// Specifies how many layers there are. Default 1.
		std::uint32_t array_layers = 1;
		/// Specifies how many times the image is sampled per pixel. Default 1.
		SampleCount sample_count = SampleCount::One;
		/// Specifies image tiling. Default ImageTiling::Optimal.
		ImageTiling image_tiling = ImageTiling::Optimal;
	};

	/**
	 * @ingroup tz_gl_vk_image
	 * Represents an Image owned by the Vulkan API. This includes Swapchain images!
	 */
	class Image
	{
	public:
		/**
		 * Create an Image that refers to a Swapchain image.
		 * @param info Information about the Swapchain and which image to refer to.
		 */
		Image(SwapchainImageInfo sinfo);
		Image(ImageInfo info);
		Image(const Image& copy) = delete;
		Image(Image&& move);
		~Image();

		Image& operator=(const Image& rhs) = delete;
		Image& operator=(Image&& rhs);

		/**
		 * Retrieve the underlying format of the image.
		 */
		ImageFormat get_format() const;
		/**
		 * Retrieve the current layout of the image.
		 */
		ImageLayout get_layout() const;
		/**
		 * Retrieve the dimensions of the image.
		 * @return {width, height} of the image, in pixels.
		 */
		Vec2ui get_dimensions() const;
		/**
		 * Retrieve the @ref LogicalDevice that 'owns' the image.
		 *
		 * Swapchain images are owned by the presentation device. For that reason, these do not belong to this device per-se. In this case this will return the @ref LogicalDevice responsible for retrieving the image (Most certainly the LogicalDevice that initially spawned its owner Swapchain.)
		 */
		const LogicalDevice& get_device() const;

		void* map();

		template<typename T>
		std::span<T> map_as()
		{
			auto* ptr = reinterpret_cast<T*>(this->map());
			if(ptr == nullptr)
			{
				return {ptr, 0};
			}
			return {ptr, this->vma_alloc_info.size / sizeof(T)};
		}

		void unmap();
		std::size_t get_linear_row_length() const;

		std::string debug_get_name() const;
		void debug_set_name(std::string name);

		using NativeType = VkImage;
		NativeType native() const;
		static Image null();
		bool is_null() const;

		bool operator==(const Image& rhs) const;

		friend class hardware::Queue;
	private:
		Image();
		void set_layout(ImageLayout layout);

		VkImage image;
		ImageFormat format;
		ImageLayout layout;
		ImageTiling tiling;
		MemoryResidency residency;
		Vec2ui dimensions;
		const LogicalDevice* device;
		bool destroy_on_destructor;
		std::optional<VmaAllocation> vma_alloc;
		VmaAllocationInfo vma_alloc_info;
		std::string debug_name = "";
	};
}

#endif // TZ_VULKAN
#endif // TOPAZ_GL_IMPL_BACKEND_VK2_IMAGE_HPP
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * 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 <dsn/ports.h>
# include <dsn/service_api_c.h>
# include <dsn/cpp/address.h>
# include <dsn/internal/task.h>
# include "group_address.h"

# ifdef _WIN32

# else
# include <sys/socket.h>
# include <netdb.h>
# include <ifaddrs.h>
# include <netinet/in.h>
# include <arpa/inet.h>

# if defined(__FreeBSD__)
# include <netinet/in.h>
# endif

# endif

static void net_init()
{
    static std::once_flag flag;
    static bool flag_inited = false;
    if (!flag_inited)
    {
        std::call_once(flag, [&]()
        {
#ifdef _WIN32
            WSADATA wsaData;
            WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
            flag_inited = true;
        });
    }
}

// name to ip etc.
DSN_API uint32_t dsn_ipv4_from_host(const char* name)
{
    sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    if ((addr.sin_addr.s_addr = inet_addr(name)) == (unsigned int)(-1))
    {
        hostent* hp = ::gethostbyname(name);
        int err =
# ifdef _WIN32
            (int)::WSAGetLastError()
# else
            h_errno
# endif
            ;
        dassert(hp != nullptr, "gethostbyname failed, name = %s, err = %d.", name, err);

        if (hp != nullptr)
        {
            memcpy(
                (void*)&(addr.sin_addr.s_addr),
                (const void*)hp->h_addr,
                (size_t)hp->h_length
                );
        }
    }

    // converts from network byte order to host byte order
    return (uint32_t)ntohl(addr.sin_addr.s_addr);
}

DSN_API uint32_t dsn_ipv4_local(const char* network_interface)
{
    uint32_t ret = 0;

# ifndef _WIN32
    struct ifaddrs* ifa = nullptr;
    if (getifaddrs(&ifa) == 0)
    {
        struct ifaddrs* i = ifa;
        while (i != nullptr)
        {
            if (i->ifa_addr->sa_family == AF_INET && strcmp(i->ifa_name, network_interface) == 0)
            {
                ret = (uint32_t)ntohl(((struct sockaddr_in *)i->ifa_addr)->sin_addr.s_addr);
                break;
            }
            i = i->ifa_next;
        }

        if (i == nullptr)
        {
            derror("get local ip from network interfaces failed, network_interface = %s\n", network_interface);
        }

        if (ifa != nullptr)
        {
            // remember to free it
            if (freeifaddrs(ifa) != 0)
            {
                derror("freeifaddrs failed, err = %s\n", strerror(errno));
            }
        }
    }
#endif

    return ret;
}

DSN_API const char*   dsn_address_to_string(dsn_address_t addr)
{
    char* p = dsn::tls_dsn.scatch_buffer;
    auto sz = sizeof(dsn::tls_dsn.scatch_buffer);

    switch (addr.u.v4.type)
    {
    case HOST_TYPE_IPV4:
        snprintf_p(
            p, sz,
            "%u.%u.%u.%u:%hu",
            addr.u.v4.ip & 0xff,
            ((uint32_t)addr.u.v4.ip >> 8) & 0xff,
            ((uint32_t)addr.u.v4.ip >> 16) & 0xff,
            ((uint32_t)addr.u.v4.ip >> 24) & 0xff,
            (uint16_t)addr.u.v4.port
            );
        break;
    case HOST_TYPE_URI:
        p = (char*)addr.u.uri.uri;
        break;
    case HOST_TYPE_GROUP:
        p = (char*)(((dsn::rpc_group_address*)(addr.u.group.group))->name());
        break;
    default:
        p = "invalid address";
        break;
    }

    return (const char*)p;
}

DSN_API dsn_address_t dsn_address_build(
    const char* host,
    uint16_t port
    )
{
    dsn::rpc_address addr(host, port);
    return addr.c_addr();
}

DSN_API dsn_address_t dsn_address_build_ipv4(
    uint32_t ipv4,
    uint16_t port
    )
{
    dsn::rpc_address addr(ipv4, port);
    return addr.c_addr();
}

DSN_API dsn_address_t dsn_address_build_group(
    dsn_group_t g
    )
{
    dsn::rpc_address addr;
    addr.assign_group(g);
    return addr.c_addr();
}

DSN_API dsn_address_t dsn_address_build_uri(
    dsn_uri_t uri
    )
{
    dsn::rpc_address addr;
    addr.assign_uri(uri);
    return addr.c_addr();
}

DSN_API dsn_uri_t dsn_uri_build(const char* url) // must be paired with destroy later
{
    return (dsn_uri_t)strdup(url);
}

DSN_API void dsn_uri_destroy(dsn_uri_t uri)
{
    free((void*)uri);
}

DSN_API dsn_group_t dsn_group_build(const char* name) // must be paired with release later
{
    auto g = new ::dsn::rpc_group_address(name);
    return g;
}

DSN_API bool dsn_group_add(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    ::dsn::rpc_address addr(ep);
    return grp->add(addr);
}

DSN_API void dsn_group_set_leader(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    ::dsn::rpc_address addr(ep);
    grp->set_leader(addr);
}

DSN_API dsn_address_t dsn_group_get_leader(dsn_group_t g)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    return grp->leader().c_addr();
}

DSN_API bool dsn_group_is_leader(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    return grp->leader() == ep;
}

DSN_API dsn_address_t dsn_group_next(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    ::dsn::rpc_address addr(ep);
    return grp->next(addr).c_addr();
}

DSN_API bool dsn_group_remove(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    ::dsn::rpc_address addr(ep);
    return grp->remove(addr);
}

DSN_API void dsn_group_destroy(dsn_group_t g)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    delete grp;
}<commit_msg>freeifaddrs does not have return value.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * 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 <dsn/ports.h>
# include <dsn/service_api_c.h>
# include <dsn/cpp/address.h>
# include <dsn/internal/task.h>
# include "group_address.h"

# ifdef _WIN32

# else
# include <sys/socket.h>
# include <netdb.h>
# include <ifaddrs.h>
# include <netinet/in.h>
# include <arpa/inet.h>

# if defined(__FreeBSD__)
# include <netinet/in.h>
# endif

# endif

static void net_init()
{
    static std::once_flag flag;
    static bool flag_inited = false;
    if (!flag_inited)
    {
        std::call_once(flag, [&]()
        {
#ifdef _WIN32
            WSADATA wsaData;
            WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
            flag_inited = true;
        });
    }
}

// name to ip etc.
DSN_API uint32_t dsn_ipv4_from_host(const char* name)
{
    sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));

    addr.sin_family = AF_INET;
    if ((addr.sin_addr.s_addr = inet_addr(name)) == (unsigned int)(-1))
    {
        hostent* hp = ::gethostbyname(name);
        int err =
# ifdef _WIN32
            (int)::WSAGetLastError()
# else
            h_errno
# endif
            ;
        dassert(hp != nullptr, "gethostbyname failed, name = %s, err = %d.", name, err);

        if (hp != nullptr)
        {
            memcpy(
                (void*)&(addr.sin_addr.s_addr),
                (const void*)hp->h_addr,
                (size_t)hp->h_length
                );
        }
    }

    // converts from network byte order to host byte order
    return (uint32_t)ntohl(addr.sin_addr.s_addr);
}

DSN_API uint32_t dsn_ipv4_local(const char* network_interface)
{
    uint32_t ret = 0;

# ifndef _WIN32
    struct ifaddrs* ifa = nullptr;
    if (getifaddrs(&ifa) == 0)
    {
        struct ifaddrs* i = ifa;
        while (i != nullptr)
        {
            if (i->ifa_addr->sa_family == AF_INET && strcmp(i->ifa_name, network_interface) == 0)
            {
                ret = (uint32_t)ntohl(((struct sockaddr_in *)i->ifa_addr)->sin_addr.s_addr);
                break;
            }
            i = i->ifa_next;
        }

        if (i == nullptr)
        {
            derror("get local ip from network interfaces failed, network_interface = %s\n", network_interface);
        }

        if (ifa != nullptr)
        {
            // remember to free it
            freeifaddrs(ifa);
        }
    }
#endif

    return ret;
}

DSN_API const char*   dsn_address_to_string(dsn_address_t addr)
{
    char* p = dsn::tls_dsn.scatch_buffer;
    auto sz = sizeof(dsn::tls_dsn.scatch_buffer);

    switch (addr.u.v4.type)
    {
    case HOST_TYPE_IPV4:
        snprintf_p(
            p, sz,
            "%u.%u.%u.%u:%hu",
            addr.u.v4.ip & 0xff,
            ((uint32_t)addr.u.v4.ip >> 8) & 0xff,
            ((uint32_t)addr.u.v4.ip >> 16) & 0xff,
            ((uint32_t)addr.u.v4.ip >> 24) & 0xff,
            (uint16_t)addr.u.v4.port
            );
        break;
    case HOST_TYPE_URI:
        p = (char*)addr.u.uri.uri;
        break;
    case HOST_TYPE_GROUP:
        p = (char*)(((dsn::rpc_group_address*)(addr.u.group.group))->name());
        break;
    default:
        p = "invalid address";
        break;
    }

    return (const char*)p;
}

DSN_API dsn_address_t dsn_address_build(
    const char* host,
    uint16_t port
    )
{
    dsn::rpc_address addr(host, port);
    return addr.c_addr();
}

DSN_API dsn_address_t dsn_address_build_ipv4(
    uint32_t ipv4,
    uint16_t port
    )
{
    dsn::rpc_address addr(ipv4, port);
    return addr.c_addr();
}

DSN_API dsn_address_t dsn_address_build_group(
    dsn_group_t g
    )
{
    dsn::rpc_address addr;
    addr.assign_group(g);
    return addr.c_addr();
}

DSN_API dsn_address_t dsn_address_build_uri(
    dsn_uri_t uri
    )
{
    dsn::rpc_address addr;
    addr.assign_uri(uri);
    return addr.c_addr();
}

DSN_API dsn_uri_t dsn_uri_build(const char* url) // must be paired with destroy later
{
    return (dsn_uri_t)strdup(url);
}

DSN_API void dsn_uri_destroy(dsn_uri_t uri)
{
    free((void*)uri);
}

DSN_API dsn_group_t dsn_group_build(const char* name) // must be paired with release later
{
    auto g = new ::dsn::rpc_group_address(name);
    return g;
}

DSN_API bool dsn_group_add(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    ::dsn::rpc_address addr(ep);
    return grp->add(addr);
}

DSN_API void dsn_group_set_leader(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    ::dsn::rpc_address addr(ep);
    grp->set_leader(addr);
}

DSN_API dsn_address_t dsn_group_get_leader(dsn_group_t g)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    return grp->leader().c_addr();
}

DSN_API bool dsn_group_is_leader(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    return grp->leader() == ep;
}

DSN_API dsn_address_t dsn_group_next(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    ::dsn::rpc_address addr(ep);
    return grp->next(addr).c_addr();
}

DSN_API bool dsn_group_remove(dsn_group_t g, dsn_address_t ep)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    ::dsn::rpc_address addr(ep);
    return grp->remove(addr);
}

DSN_API void dsn_group_destroy(dsn_group_t g)
{
    auto grp = (::dsn::rpc_group_address*)(g);
    delete grp;
}
<|endoftext|>


<commit_before>#include "easypr/core/plate_detect.h"
#include "easypr/util/util.h"

namespace easypr {

  CPlateDetect::CPlateDetect() {
    m_plateLocate = new CPlateLocate();

    m_maxPlates = 3;
    m_type = 0;

    m_showDetect = false;
  }

  CPlateDetect::~CPlateDetect() { SAFE_RELEASE(m_plateLocate); }

  int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec, int type,
    bool showDetectArea, int img_index) {

    std::vector<CPlate> color_Plates;
    std::vector<CPlate> sobel_Plates;
    std::vector<CPlate> mser_Plates;

    std::vector<CPlate> all_result_Plates;
    if ( !type || type & PR_DETECT_SOBEL) {
      m_plateLocate->plateSobelLocate(src, sobel_Plates, img_index);
      std::vector<CPlate>& sobel_result_Plates = sobel_Plates;

      for (size_t i = 0; i < sobel_result_Plates.size(); i++) {
        CPlate plate = sobel_result_Plates[i];
        plate.setPlateLocateType(SOBEL);

        all_result_Plates.push_back(plate);
      }
    }

    if ( !type || type & PR_DETECT_COLOR) {
      m_plateLocate->plateColorLocate(src, color_Plates, img_index);
      std::vector<CPlate>& color_result_Plates = color_Plates;

      for (size_t i = 0; i < color_result_Plates.size(); i++) {
        CPlate plate = color_result_Plates[i];

        plate.setPlateLocateType(COLOR);
        all_result_Plates.push_back(plate);
      }
    }

    if ( !type || type & PR_DETECT_CMSER) {
      m_plateLocate->plateMserLocate(src, mser_Plates, img_index);
      std::vector<CPlate>& mser_result_Plates = mser_Plates;

      for (size_t i = 0; i < mser_result_Plates.size(); i++) {
        CPlate plate = mser_result_Plates[i];

        plate.setPlateLocateType(CMSER);
        all_result_Plates.push_back(plate);
      }
    }

    // ʹ÷Ǽֵжϳ
    PlateJudge::instance()->plateJudgeUsingNMS(all_result_Plates, resultVec, m_maxPlates);

    if (showDetectArea || getDetectShow()) {
      int index = 0;

      Mat result;
      src.copyTo(result);

      for (size_t j = 0; j < resultVec.size(); j++) {
        CPlate item = resultVec[j];
        Mat plate = item.getPlateMat();

        int height = 36;
        int width = 136;
        if (height * index + height < result.rows) {
          Mat imageRoi = result(Rect(0, 0 + height * index, width, height));
          addWeighted(imageRoi, 0, plate, 1, 0, imageRoi);
        }
        index++;

        RotatedRect minRect = item.getPlatePos();
        Point2f rect_points[4];
        minRect.points(rect_points);

        Scalar lineColor = Scalar(255, 255, 255);

        if (item.getPlateLocateType() == SOBEL) lineColor = Scalar(255, 0, 0);
        if (item.getPlateLocateType() == COLOR) lineColor = Scalar(0, 255, 0);
        if (item.getPlateLocateType() == CMSER) lineColor = Scalar(0, 0, 255);

        for (int j = 0; j < 4; j++)
          line(result, rect_points[j], rect_points[(j + 1) % 4], lineColor, 2, 8);
      }

      //ʾλͼƬ
      showResult(result, img_index);
    }

    /*if (0) {
      Mat result = src.clone();
      for (size_t i = 0; i < all_result_Plates.size(); i++) {
        CPlate plate = all_result_Plates.at(i);

        Rect_<float> outputRect;
        calcSafeRect(plate.getPlatePos(), src, outputRect);
        cv::rectangle(result, outputRect, Scalar(0, 0, 255));

        if (0){
          std::stringstream ss(std::stringstream::in | std::stringstream::out);
          ss << "resources/image/tmp/plate_" << index << "_" << i << ".jpg";
          imwrite(ss.str(), src(outputRect));
        }
      }

      if (0) {
        imshow("result", result);
        waitKey(0);
        destroyWindow("result");
      }
    }*/

    return 0;
  }

  int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec, int img_index) {
    int result = plateDetect(src, resultVec, m_type, false, img_index);
    return result;
  }

  void CPlateDetect::LoadSVM(std::string path) {
    PlateJudge::instance()->LoadModel(path);
  }

  int CPlateDetect::showResult(const Mat &result, int img_index) {
    namedWindow("EasyPR", CV_WINDOW_AUTOSIZE);

    const int RESULTWIDTH = 640;   // 640 930
    const int RESULTHEIGHT = 540;  // 540 710

    Mat img_window;
    img_window.create(RESULTHEIGHT, RESULTWIDTH, CV_8UC3);

    int nRows = result.rows;
    int nCols = result.cols;

    Mat result_resize;
    if (nCols <= img_window.cols && nRows <= img_window.rows) {
      result_resize = result;

    }
    else if (nCols > img_window.cols && nRows <= img_window.rows) {
      float scale = float(img_window.cols) / float(nCols);
      resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA);

    }
    else if (nCols <= img_window.cols && nRows > img_window.rows) {
      float scale = float(img_window.rows) / float(nRows);
      resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA);

    }
    else if (nCols > img_window.cols && nRows > img_window.rows) {
      Mat result_middle;
      float scale = float(img_window.cols) / float(nCols);
      resize(result, result_middle, Size(), scale, scale, CV_INTER_AREA);

      if (result_middle.rows > img_window.rows) {
        float scale = float(img_window.rows) / float(result_middle.rows);
        resize(result_middle, result_resize, Size(), scale, scale, CV_INTER_AREA);
      }
      else {
        result_resize = result_middle;
      }
    }
    else {
      result_resize = result;
    }

    Mat imageRoi = img_window(Rect((RESULTWIDTH - result_resize.cols) / 2,
      (RESULTHEIGHT - result_resize.rows) / 2,
      result_resize.cols, result_resize.rows));
    addWeighted(imageRoi, 0, result_resize, 1, 0, imageRoi);

    if (1) {
      imshow("EasyPR", img_window);
      waitKey(0);
      destroyWindow("EasyPR");
    }

    if (1) {
      std::stringstream ss(std::stringstream::in | std::stringstream::out);
      ss << "resources/image/tmp/Result/plate_" << img_index << ".jpg";
      imwrite(ss.str(), img_window);
    }

    return 0;
  }
}<commit_msg>* fix #93.<commit_after>#include "easypr/core/plate_detect.h"
#include "easypr/util/util.h"

namespace easypr {

  CPlateDetect::CPlateDetect() {
    m_plateLocate = new CPlateLocate();

    m_maxPlates = 3;
    m_type = 0;

    m_showDetect = false;
  }

  CPlateDetect::~CPlateDetect() { SAFE_RELEASE(m_plateLocate); }

  int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec, int type,
    bool showDetectArea, int img_index) {

    std::vector<CPlate> color_Plates;
    std::vector<CPlate> sobel_Plates;
    std::vector<CPlate> mser_Plates;

    std::vector<CPlate> all_result_Plates;
    if ( !type || type & PR_DETECT_SOBEL) {
      m_plateLocate->plateSobelLocate(src, sobel_Plates, img_index);
      std::vector<CPlate>& sobel_result_Plates = sobel_Plates;

      for (size_t i = 0; i < sobel_result_Plates.size(); i++) {
        CPlate plate = sobel_result_Plates[i];
        plate.setPlateLocateType(SOBEL);

        all_result_Plates.push_back(plate);
      }
    }

    if ( !type || type & PR_DETECT_COLOR) {
      m_plateLocate->plateColorLocate(src, color_Plates, img_index);
      std::vector<CPlate>& color_result_Plates = color_Plates;

      for (size_t i = 0; i < color_result_Plates.size(); i++) {
        CPlate plate = color_result_Plates[i];

        plate.setPlateLocateType(COLOR);
        all_result_Plates.push_back(plate);
      }
    }

    if ( !type || type & PR_DETECT_CMSER) {
      m_plateLocate->plateMserLocate(src, mser_Plates, img_index);
      std::vector<CPlate>& mser_result_Plates = mser_Plates;

      for (size_t i = 0; i < mser_result_Plates.size(); i++) {
        CPlate plate = mser_result_Plates[i];

        plate.setPlateLocateType(CMSER);
        all_result_Plates.push_back(plate);
      }
    }

    // ʹ÷Ǽֵжϳ
    PlateJudge::instance()->plateJudgeUsingNMS(all_result_Plates, resultVec, m_maxPlates);

    if (showDetectArea || getDetectShow()) {
      int index = 0;

      Mat result;
      src.copyTo(result);

      for (size_t j = 0; j < resultVec.size(); j++) {
        CPlate item = resultVec[j];
        Mat plate = item.getPlateMat();

        int height = 36;
        int width = 136;
        if (height * index + height < result.rows) {
          Mat imageRoi = result(Rect(0, 0 + height * index, width, height));
          addWeighted(imageRoi, 0, plate, 1, 0, imageRoi);
        }
        index++;

        RotatedRect minRect = item.getPlatePos();
        Point2f rect_points[4];
        minRect.points(rect_points);

        Scalar lineColor = Scalar(255, 255, 255);

        if (item.getPlateLocateType() == SOBEL) lineColor = Scalar(255, 0, 0);
        if (item.getPlateLocateType() == COLOR) lineColor = Scalar(0, 255, 0);
        if (item.getPlateLocateType() == CMSER) lineColor = Scalar(0, 0, 255);

        for (int j = 0; j < 4; j++)
          line(result, rect_points[j], rect_points[(j + 1) % 4], lineColor, 2, 8);
      }

      //ʾλͼƬ
      showResult(result, img_index);
    }

    /*if (0) {
      Mat result = src.clone();
      for (size_t i = 0; i < all_result_Plates.size(); i++) {
        CPlate plate = all_result_Plates.at(i);

        Rect_<float> outputRect;
        calcSafeRect(plate.getPlatePos(), src, outputRect);
        cv::rectangle(result, outputRect, Scalar(0, 0, 255));

        if (0){
          std::stringstream ss(std::stringstream::in | std::stringstream::out);
          ss << "resources/image/tmp/plate_" << index << "_" << i << ".jpg";
          imwrite(ss.str(), src(outputRect));
        }
      }

      if (0) {
        imshow("result", result);
        waitKey(0);
        destroyWindow("result");
      }
    }*/

    return 0;
  }

  int CPlateDetect::plateDetect(Mat src, std::vector<CPlate> &resultVec, int img_index) {
    int result = plateDetect(src, resultVec, m_type, false, img_index);
    return result;
  }

  void CPlateDetect::LoadSVM(std::string path) {
    PlateJudge::instance()->LoadModel(path);
  }

  int CPlateDetect::showResult(const Mat &result, int img_index) {
    namedWindow("EasyPR", CV_WINDOW_AUTOSIZE);

    const int RESULTWIDTH = 640;   // 640 930
    const int RESULTHEIGHT = 540;  // 540 710

    Mat img_window;
    img_window.create(RESULTHEIGHT, RESULTWIDTH, CV_8UC3);

    int nRows = result.rows;
    int nCols = result.cols;

    Mat result_resize;
    if (nCols <= img_window.cols && nRows <= img_window.rows) {
      result_resize = result;

    }
    else if (nCols > img_window.cols && nRows <= img_window.rows) {
      float scale = float(img_window.cols) / float(nCols);
      resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA);

    }
    else if (nCols <= img_window.cols && nRows > img_window.rows) {
      float scale = float(img_window.rows) / float(nRows);
      resize(result, result_resize, Size(), scale, scale, CV_INTER_AREA);

    }
    else if (nCols > img_window.cols && nRows > img_window.rows) {
      Mat result_middle;
      float scale = float(img_window.cols) / float(nCols);
      resize(result, result_middle, Size(), scale, scale, CV_INTER_AREA);

      if (result_middle.rows > img_window.rows) {
        float scale = float(img_window.rows) / float(result_middle.rows);
        resize(result_middle, result_resize, Size(), scale, scale, CV_INTER_AREA);
      }
      else {
        result_resize = result_middle;
      }
    }
    else {
      result_resize = result;
    }

    Mat imageRoi = img_window(Rect((RESULTWIDTH - result_resize.cols) / 2,
      (RESULTHEIGHT - result_resize.rows) / 2,
      result_resize.cols, result_resize.rows));
    addWeighted(imageRoi, 0, result_resize, 1, 0, imageRoi);

    if (1) {
      imshow("EasyPR", img_window);
      waitKey(0);
      destroyWindow("EasyPR");
    }

    if (1) {
      std::stringstream ss(std::stringstream::in | std::stringstream::out);
      ss << "resources/image/tmp/Result/plate_" << img_index << ".jpg";
      imwrite(ss.str(), img_window);
    }

    return 0;
  }
}<|endoftext|>


<commit_before>/*
* Copyright (c) 2012-2015 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* VapourSynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "vscore.h"
#include <cassert>
#ifdef VS_TARGET_CPU_X86
#include "x86utils.h"
#endif

bool VSThreadPool::taskCmp(const PFrameContext &a, const PFrameContext &b) {
    return (a->reqOrder < b->reqOrder) || (a->reqOrder == b->reqOrder && a->n < b->n);
}

void VSThreadPool::runTasks(VSThreadPool *owner, std::atomic<bool> &stop) {
#ifdef VS_TARGET_CPU_X86
    if (!vs_isMMXStateOk())
        vsFatal("Bad MMX state detected after creating new thread");
#endif
#ifdef VS_TARGET_OS_WINDOWS
    if (!vs_isFPUStateOk())
        vsWarning("Bad FPU state detected after creating new thread");
    if (!vs_isSSEStateOk())
        vsFatal("Bad SSE state detected after creating new thread");
#endif

    std::unique_lock<std::mutex> lock(owner->lock);

    while (true) {
        bool ranTask = false;

/////////////////////////////////////////////////////////////////////////////////////////////
// Go through all tasks from the top (oldest) and process the first one possible
        owner->tasks.sort(taskCmp);

        for (auto iter = owner->tasks.begin(); iter != owner->tasks.end(); ++iter) {
            FrameContext *mainContext = iter->get();
            FrameContext *leafContext = nullptr;

/////////////////////////////////////////////////////////////////////////////////////////////
// Handle the output tasks
            if (mainContext->frameDone && mainContext->returnedFrame) {
                PFrameContext mainContextRef(*iter);
                owner->tasks.erase(iter);
                owner->returnFrame(mainContextRef, mainContext->returnedFrame);
                ranTask = true;
                break;
            }

            if (mainContext->frameDone && mainContext->hasError()) {
                PFrameContext mainContextRef(*iter);
                owner->tasks.erase(iter);
                owner->returnFrame(mainContextRef, mainContext->getErrorMessage());
                ranTask = true;
                break;
            }

            bool hasLeafContext = mainContext->returnedFrame || mainContext->hasError();
            if (hasLeafContext)
            {
                leafContext = mainContext;
                mainContext = mainContext->upstreamContext.get();
            }

            VSNode *clip = mainContext->clip;
            int filterMode = clip->filterMode;

/////////////////////////////////////////////////////////////////////////////////////////////
// This part handles the locking for the different filter modes

            bool parallelRequestsNeedsUnlock = false;
            if (filterMode == fmUnordered) {
                // already busy?
                if (!clip->serialMutex.try_lock())
                    continue;
            } else if (filterMode == fmSerial) {
                // already busy?
                if (!clip->serialMutex.try_lock())
                    continue;
                // no frame in progress?
                if (clip->serialFrame == -1) {
                    clip->serialFrame = mainContext->n;
                //
                } else if (clip->serialFrame != mainContext->n) {
                    clip->serialMutex.unlock();
                    continue;
                }
                // continue processing the already started frame
            } else if (filterMode == fmParallel) {
                std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex);
                // is the filter already processing another call for this frame? if so move along
                if (clip->concurrentFrames.count(mainContext->n)) {
                    continue;
                } else {
                    clip->concurrentFrames.insert(mainContext->n);
                }
            } else if (filterMode == fmParallelRequests) {
                std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex);
                // is the filter already processing another call for this frame? if so move along
                if (clip->concurrentFrames.count(mainContext->n)) {
                    continue;
                } else {
                    // do we need the serial lock since all frames will be ready this time?
                    // check if we're in the arAllFramesReady state so we need additional locking
                    if (mainContext->numFrameRequests == 1) {
                        if (!clip->serialMutex.try_lock())
                            continue;
                        parallelRequestsNeedsUnlock = true;
                        clip->concurrentFrames.insert(mainContext->n);
                    }
                }
            }

/////////////////////////////////////////////////////////////////////////////////////////////
// Remove the context from the task list

            PFrameContext mainContextRef;
            PFrameContext leafContextRef;
            if (hasLeafContext) {
                leafContextRef = *iter;
                mainContextRef = leafContextRef->upstreamContext;
            } else {
                mainContextRef = *iter;
            }

            owner->tasks.erase(iter);

/////////////////////////////////////////////////////////////////////////////////////////////
// Figure out the activation reason

            VSActivationReason ar = arInitial;
            bool skipCall = false; // Used to avoid multiple error calls for the same frame request going into a filter
            if ((hasLeafContext && leafContext->hasError()) || mainContext->hasError()) {
                ar = arError;
                skipCall = mainContext->setError(leafContext->getErrorMessage());
                --mainContext->numFrameRequests;
            } else if (hasLeafContext && leafContext->returnedFrame) {
                if (--mainContext->numFrameRequests > 0)
                    ar = arFrameReady;
                else
                    ar = arAllFramesReady;

                mainContext->availableFrames.insert(std::make_pair(NodeOutputKey(leafContext->clip, leafContext->n, leafContext->index), leafContext->returnedFrame));
                mainContext->lastCompletedN = leafContext->n;
                mainContext->lastCompletedNode = leafContext->node;
            }

            assert(mainContext->numFrameRequests >= 0);

            bool hasExistingRequests = !!mainContext->numFrameRequests;

/////////////////////////////////////////////////////////////////////////////////////////////
// Do the actual processing

            lock.unlock();

            VSFrameContext externalFrameCtx(mainContextRef);
            assert(ar == arError || !mainContext->hasError());
#ifdef VS_FRAME_REQ_DEBUG
                vsWarning("Entering: %s Frame: %d Index: %d AR: %d", mainContext->clip->name.c_str(), mainContext->n, mainContext->index, (int)ar);
#endif
            PVideoFrame f;
            if (!skipCall)
                f = clip->getFrameInternal(mainContext->n, ar, externalFrameCtx);
            ranTask = true;
#ifdef VS_FRAME_REQ_DEBUG
            vsWarning("Exiting: %s Frame: %d Index: %d AR: %d", mainContext->clip->name.c_str(), mainContext->n, mainContext->index, (int)ar);
#endif
            bool frameProcessingDone = f || mainContext->hasError();
            if (mainContext->hasError() && f)
                vsFatal("A frame was returned by %s but an error was also set, this is not allowed", clip->name.c_str());

/////////////////////////////////////////////////////////////////////////////////////////////
// Unlock so the next job can run on the context
            if (filterMode == fmUnordered) {
                clip->serialMutex.unlock();
            } else if (filterMode == fmSerial) {
                if (frameProcessingDone)
                    clip->serialFrame = -1;
                clip->serialMutex.unlock();
            } else if (filterMode == fmParallel) {
                std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex);
                clip->concurrentFrames.erase(mainContext->n);
            } else if (filterMode == fmParallelRequests) {
                std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex);
                clip->concurrentFrames.erase(mainContext->n);
                if (parallelRequestsNeedsUnlock)
                    clip->serialMutex.unlock();
            }

/////////////////////////////////////////////////////////////////////////////////////////////
// Handle frames that were requested
            bool requestedFrames = !externalFrameCtx.reqList.empty() && !frameProcessingDone;

            lock.lock();

            if (requestedFrames) {
                for (auto &reqIter : externalFrameCtx.reqList)
                    owner->startInternal(reqIter);
                externalFrameCtx.reqList.clear();
            }

            if (frameProcessingDone)
                owner->allContexts.erase(NodeOutputKey(mainContext->clip, mainContext->n, mainContext->index));

/////////////////////////////////////////////////////////////////////////////////////////////
// Propagate status to other linked contexts
// CHANGES mainContextRef!!!

            if (mainContext->hasError() && !hasExistingRequests && !requestedFrames) {
                PFrameContext n;
                do {
                    n = mainContextRef->notificationChain;

                    if (n) {
                        mainContextRef->notificationChain.reset();
                        n->setError(mainContextRef->getErrorMessage());
                    }

                    if (mainContextRef->upstreamContext) {
                        owner->startInternal(mainContextRef);
                    }

                    if (mainContextRef->frameDone) {
                        owner->returnFrame(mainContextRef, mainContextRef->getErrorMessage());
                    }
                } while ((mainContextRef = n));
            } else if (f) {
                if (hasExistingRequests || requestedFrames)
                    vsFatal("A frame was returned at the end of processing by %s but there are still outstanding requests", clip->name.c_str());
                PFrameContext n;

                do {
                    n = mainContextRef->notificationChain;

                    if (n)
                        mainContextRef->notificationChain.reset();

                    if (mainContextRef->upstreamContext) {
                        mainContextRef->returnedFrame = f;
                        owner->startInternal(mainContextRef);
                    }

                    if (mainContextRef->frameDone)
                        owner->returnFrame(mainContextRef, f);
                } while ((mainContextRef = n));
            } else if (hasExistingRequests || requestedFrames) {
                // already scheduled, do nothing
            } else {
                vsFatal("No frame returned at the end of processing by %s", clip->name.c_str());
            }
            break;
        }

        if (!ranTask || owner->activeThreadCount() > owner->threadCount()) {
            --owner->activeThreads;
            if (stop) {
                lock.unlock();
                break;
            }
            ++owner->idleThreads;
            owner->newWork.wait(lock);
            --owner->idleThreads;
            ++owner->activeThreads;
        }
    }
}

VSThreadPool::VSThreadPool(VSCore *core, int threads) : core(core), activeThreads(0), idleThreads(0), reqCounter(0), stopThreads(false), ticks(0) {
    setThreadCount(threads);
}

int VSThreadPool::activeThreadCount() const {
    return activeThreads;
}

int VSThreadPool::threadCount() const {
    return maxThreads;
}

void VSThreadPool::spawnThread() {
    std::thread *thread = new std::thread(runTasks, this, std::ref(stopThreads));
    allThreads.insert(std::make_pair(thread->get_id(), thread));
    ++activeThreads;
}

void VSThreadPool::setThreadCount(int threads) {
    maxThreads = threads > 0 ? threads : std::thread::hardware_concurrency();
    if (maxThreads == 0) {
        maxThreads = 1;
        vsWarning("Couldn't detect optimal number of threads. Thread count set to 1.");
    }
}

void VSThreadPool::wakeThread() {
    if (activeThreads < maxThreads) {
        if (idleThreads == 0) // newly spawned threads are active so no need to notify an additional thread
            spawnThread();
        else
            newWork.notify_one();
    }
}

void VSThreadPool::releaseThread() {
    --activeThreads;
}

void VSThreadPool::reserveThread() {
    ++activeThreads;
}

void VSThreadPool::notifyCaches(bool needMemory) {
    std::lock_guard<std::mutex> lock(core->cacheLock);
    for (auto &cache : core->caches)
        cache->notifyCache(needMemory);
}

void VSThreadPool::start(const PFrameContext &context) {
    assert(context);
    std::lock_guard<std::mutex> l(lock);
    context->reqOrder = ++reqCounter;
    startInternal(context);
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const PVideoFrame &f) {
    assert(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    VSFrameRef *ref = new VSFrameRef(f);
    callbackLock.lock();
    rCtx->frameDone(rCtx->userData, ref, rCtx->n, rCtx->node, nullptr);
    callbackLock.unlock();
    lock.lock();
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const std::string &errMsg) {
    assert(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    callbackLock.lock();
    rCtx->frameDone(rCtx->userData, nullptr, rCtx->n, rCtx->node, errMsg.c_str());
    callbackLock.unlock();
    lock.lock();
}

void VSThreadPool::startInternal(const PFrameContext &context) {
    //technically this could be done by walking up the context chain and add a new notification to the correct one
    //unfortunately this would probably be quite slow for deep scripts so just hope the cache catches it

    if (context->n < 0)
        vsFatal("Negative frame request by: %s", context->clip->getName().c_str());

    // check to see if it's time to reevaluate cache sizes
    if (core->memory->isOverLimit()) {
        ticks = 0;
        notifyCaches(true);
    }

    // a normal tick for caches to adjust their sizes based on recent history
    if (!context->upstreamContext && ++ticks == 500) {
        ticks = 0;
        notifyCaches(false);
    }

    // add it immediately if the task is to return a completed frame or report an error since it never has an existing context
    if (context->returnedFrame || context->hasError()) {
        tasks.push_back(context);
    } else {
        if (context->upstreamContext)
            ++context->upstreamContext->numFrameRequests;

        NodeOutputKey p(context->clip, context->n, context->index);

        if (allContexts.count(p)) {
            PFrameContext &ctx = allContexts[p];
            assert(context->clip == ctx->clip && context->n == ctx->n && context->index == ctx->index);

            if (ctx->returnedFrame) {
                // special case where the requested frame is encountered "by accident"
                context->returnedFrame = ctx->returnedFrame;
                tasks.push_back(context);
            } else {
                // add it to the list of contexts to notify when it's available
                context->notificationChain = ctx->notificationChain;
                ctx->notificationChain = context;
                ctx->reqOrder = std::min(ctx->reqOrder, context->reqOrder);
            }
        } else {
            // create a new context and append it to the tasks
            allContexts[p] = context;
            tasks.push_back(context);
        }
    }
    wakeThread();
}

bool VSThreadPool::isWorkerThread() {
    std::lock_guard<std::mutex> m(lock);
    return allThreads.count(std::this_thread::get_id()) > 0;
}

void VSThreadPool::waitForDone() {
    // todo
}

VSThreadPool::~VSThreadPool() {
    std::unique_lock<std::mutex> m(lock);
    stopThreads = true;

    while (!allThreads.empty()) {
        auto iter = allThreads.begin();
        auto thread = iter->second;
        newWork.notify_all();
        m.unlock();
        thread->join();
        m.lock();
        allThreads.erase(iter);
        delete thread;
        newWork.notify_all();
    }

    assert(activeThreads == 0);
    assert(idleThreads == 0);
};
<commit_msg>Add comment<commit_after>/*
* Copyright (c) 2012-2015 Fredrik Mellbin
*
* This file is part of VapourSynth.
*
* VapourSynth is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* VapourSynth is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with VapourSynth; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "vscore.h"
#include <cassert>
#ifdef VS_TARGET_CPU_X86
#include "x86utils.h"
#endif

bool VSThreadPool::taskCmp(const PFrameContext &a, const PFrameContext &b) {
    return (a->reqOrder < b->reqOrder) || (a->reqOrder == b->reqOrder && a->n < b->n);
}

void VSThreadPool::runTasks(VSThreadPool *owner, std::atomic<bool> &stop) {
#ifdef VS_TARGET_CPU_X86
    if (!vs_isMMXStateOk())
        vsFatal("Bad MMX state detected after creating new thread");
#endif
#ifdef VS_TARGET_OS_WINDOWS
    if (!vs_isFPUStateOk())
        vsWarning("Bad FPU state detected after creating new thread");
    if (!vs_isSSEStateOk())
        vsFatal("Bad SSE state detected after creating new thread");
#endif

    std::unique_lock<std::mutex> lock(owner->lock);

    while (true) {
        bool ranTask = false;

/////////////////////////////////////////////////////////////////////////////////////////////
// Go through all tasks from the top (oldest) and process the first one possible
        owner->tasks.sort(taskCmp);

        for (auto iter = owner->tasks.begin(); iter != owner->tasks.end(); ++iter) {
            FrameContext *mainContext = iter->get();
            FrameContext *leafContext = nullptr;

/////////////////////////////////////////////////////////////////////////////////////////////
// Handle the output tasks
            if (mainContext->frameDone && mainContext->returnedFrame) {
                PFrameContext mainContextRef(*iter);
                owner->tasks.erase(iter);
                owner->returnFrame(mainContextRef, mainContext->returnedFrame);
                ranTask = true;
                break;
            }

            if (mainContext->frameDone && mainContext->hasError()) {
                PFrameContext mainContextRef(*iter);
                owner->tasks.erase(iter);
                owner->returnFrame(mainContextRef, mainContext->getErrorMessage());
                ranTask = true;
                break;
            }

            bool hasLeafContext = mainContext->returnedFrame || mainContext->hasError();
            if (hasLeafContext)
            {
                leafContext = mainContext;
                mainContext = mainContext->upstreamContext.get();
            }

            VSNode *clip = mainContext->clip;
            int filterMode = clip->filterMode;

/////////////////////////////////////////////////////////////////////////////////////////////
// This part handles the locking for the different filter modes

            bool parallelRequestsNeedsUnlock = false;
            if (filterMode == fmUnordered) {
                // already busy?
                if (!clip->serialMutex.try_lock())
                    continue;
            } else if (filterMode == fmSerial) {
                // already busy?
                if (!clip->serialMutex.try_lock())
                    continue;
                // no frame in progress?
                if (clip->serialFrame == -1) {
                    clip->serialFrame = mainContext->n;
                // another frame already in progress?
                } else if (clip->serialFrame != mainContext->n) {
                    clip->serialMutex.unlock();
                    continue;
                }
                // continue processing the already started frame
            } else if (filterMode == fmParallel) {
                std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex);
                // is the filter already processing another call for this frame? if so move along
                if (clip->concurrentFrames.count(mainContext->n)) {
                    continue;
                } else {
                    clip->concurrentFrames.insert(mainContext->n);
                }
            } else if (filterMode == fmParallelRequests) {
                std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex);
                // is the filter already processing another call for this frame? if so move along
                if (clip->concurrentFrames.count(mainContext->n)) {
                    continue;
                } else {
                    // do we need the serial lock since all frames will be ready this time?
                    // check if we're in the arAllFramesReady state so we need additional locking
                    if (mainContext->numFrameRequests == 1) {
                        if (!clip->serialMutex.try_lock())
                            continue;
                        parallelRequestsNeedsUnlock = true;
                        clip->concurrentFrames.insert(mainContext->n);
                    }
                }
            }

/////////////////////////////////////////////////////////////////////////////////////////////
// Remove the context from the task list

            PFrameContext mainContextRef;
            PFrameContext leafContextRef;
            if (hasLeafContext) {
                leafContextRef = *iter;
                mainContextRef = leafContextRef->upstreamContext;
            } else {
                mainContextRef = *iter;
            }

            owner->tasks.erase(iter);

/////////////////////////////////////////////////////////////////////////////////////////////
// Figure out the activation reason

            VSActivationReason ar = arInitial;
            bool skipCall = false; // Used to avoid multiple error calls for the same frame request going into a filter
            if ((hasLeafContext && leafContext->hasError()) || mainContext->hasError()) {
                ar = arError;
                skipCall = mainContext->setError(leafContext->getErrorMessage());
                --mainContext->numFrameRequests;
            } else if (hasLeafContext && leafContext->returnedFrame) {
                if (--mainContext->numFrameRequests > 0)
                    ar = arFrameReady;
                else
                    ar = arAllFramesReady;

                mainContext->availableFrames.insert(std::make_pair(NodeOutputKey(leafContext->clip, leafContext->n, leafContext->index), leafContext->returnedFrame));
                mainContext->lastCompletedN = leafContext->n;
                mainContext->lastCompletedNode = leafContext->node;
            }

            assert(mainContext->numFrameRequests >= 0);

            bool hasExistingRequests = !!mainContext->numFrameRequests;

/////////////////////////////////////////////////////////////////////////////////////////////
// Do the actual processing

            lock.unlock();

            VSFrameContext externalFrameCtx(mainContextRef);
            assert(ar == arError || !mainContext->hasError());
#ifdef VS_FRAME_REQ_DEBUG
            vsWarning("Entering: %s Frame: %d Index: %d AR: %d", mainContext->clip->name.c_str(), mainContext->n, mainContext->index, (int)ar);
#endif
            PVideoFrame f;
            if (!skipCall)
                f = clip->getFrameInternal(mainContext->n, ar, externalFrameCtx);
            ranTask = true;
#ifdef VS_FRAME_REQ_DEBUG
            vsWarning("Exiting: %s Frame: %d Index: %d AR: %d", mainContext->clip->name.c_str(), mainContext->n, mainContext->index, (int)ar);
#endif
            bool frameProcessingDone = f || mainContext->hasError();
            if (mainContext->hasError() && f)
                vsFatal("A frame was returned by %s but an error was also set, this is not allowed", clip->name.c_str());

/////////////////////////////////////////////////////////////////////////////////////////////
// Unlock so the next job can run on the context
            if (filterMode == fmUnordered) {
                clip->serialMutex.unlock();
            } else if (filterMode == fmSerial) {
                if (frameProcessingDone)
                    clip->serialFrame = -1;
                clip->serialMutex.unlock();
            } else if (filterMode == fmParallel) {
                std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex);
                clip->concurrentFrames.erase(mainContext->n);
            } else if (filterMode == fmParallelRequests) {
                std::lock_guard<std::mutex> lock(clip->concurrentFramesMutex);
                clip->concurrentFrames.erase(mainContext->n);
                if (parallelRequestsNeedsUnlock)
                    clip->serialMutex.unlock();
            }

/////////////////////////////////////////////////////////////////////////////////////////////
// Handle frames that were requested
            bool requestedFrames = !externalFrameCtx.reqList.empty() && !frameProcessingDone;

            lock.lock();

            if (requestedFrames) {
                for (auto &reqIter : externalFrameCtx.reqList)
                    owner->startInternal(reqIter);
                externalFrameCtx.reqList.clear();
            }

            if (frameProcessingDone)
                owner->allContexts.erase(NodeOutputKey(mainContext->clip, mainContext->n, mainContext->index));

/////////////////////////////////////////////////////////////////////////////////////////////
// Propagate status to other linked contexts
// CHANGES mainContextRef!!!

            if (mainContext->hasError() && !hasExistingRequests && !requestedFrames) {
                PFrameContext n;
                do {
                    n = mainContextRef->notificationChain;

                    if (n) {
                        mainContextRef->notificationChain.reset();
                        n->setError(mainContextRef->getErrorMessage());
                    }

                    if (mainContextRef->upstreamContext) {
                        owner->startInternal(mainContextRef);
                    }

                    if (mainContextRef->frameDone) {
                        owner->returnFrame(mainContextRef, mainContextRef->getErrorMessage());
                    }
                } while ((mainContextRef = n));
            } else if (f) {
                if (hasExistingRequests || requestedFrames)
                    vsFatal("A frame was returned at the end of processing by %s but there are still outstanding requests", clip->name.c_str());
                PFrameContext n;

                do {
                    n = mainContextRef->notificationChain;

                    if (n)
                        mainContextRef->notificationChain.reset();

                    if (mainContextRef->upstreamContext) {
                        mainContextRef->returnedFrame = f;
                        owner->startInternal(mainContextRef);
                    }

                    if (mainContextRef->frameDone)
                        owner->returnFrame(mainContextRef, f);
                } while ((mainContextRef = n));
            } else if (hasExistingRequests || requestedFrames) {
                // already scheduled, do nothing
            } else {
                vsFatal("No frame returned at the end of processing by %s", clip->name.c_str());
            }
            break;
        }

        if (!ranTask || owner->activeThreadCount() > owner->threadCount()) {
            --owner->activeThreads;
            if (stop) {
                lock.unlock();
                break;
            }
            ++owner->idleThreads;
            owner->newWork.wait(lock);
            --owner->idleThreads;
            ++owner->activeThreads;
        }
    }
}

VSThreadPool::VSThreadPool(VSCore *core, int threads) : core(core), activeThreads(0), idleThreads(0), reqCounter(0), stopThreads(false), ticks(0) {
    setThreadCount(threads);
}

int VSThreadPool::activeThreadCount() const {
    return activeThreads;
}

int VSThreadPool::threadCount() const {
    return maxThreads;
}

void VSThreadPool::spawnThread() {
    std::thread *thread = new std::thread(runTasks, this, std::ref(stopThreads));
    allThreads.insert(std::make_pair(thread->get_id(), thread));
    ++activeThreads;
}

void VSThreadPool::setThreadCount(int threads) {
    maxThreads = threads > 0 ? threads : std::thread::hardware_concurrency();
    if (maxThreads == 0) {
        maxThreads = 1;
        vsWarning("Couldn't detect optimal number of threads. Thread count set to 1.");
    }
}

void VSThreadPool::wakeThread() {
    if (activeThreads < maxThreads) {
        if (idleThreads == 0) // newly spawned threads are active so no need to notify an additional thread
            spawnThread();
        else
            newWork.notify_one();
    }
}

void VSThreadPool::releaseThread() {
    --activeThreads;
}

void VSThreadPool::reserveThread() {
    ++activeThreads;
}

void VSThreadPool::notifyCaches(bool needMemory) {
    std::lock_guard<std::mutex> lock(core->cacheLock);
    for (auto &cache : core->caches)
        cache->notifyCache(needMemory);
}

void VSThreadPool::start(const PFrameContext &context) {
    assert(context);
    std::lock_guard<std::mutex> l(lock);
    context->reqOrder = ++reqCounter;
    startInternal(context);
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const PVideoFrame &f) {
    assert(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    VSFrameRef *ref = new VSFrameRef(f);
    callbackLock.lock();
    rCtx->frameDone(rCtx->userData, ref, rCtx->n, rCtx->node, nullptr);
    callbackLock.unlock();
    lock.lock();
}

void VSThreadPool::returnFrame(const PFrameContext &rCtx, const std::string &errMsg) {
    assert(rCtx->frameDone);
    // we need to unlock here so the callback may request more frames without causing a deadlock
    // AND so that slow callbacks will only block operations in this thread, not all the others
    lock.unlock();
    callbackLock.lock();
    rCtx->frameDone(rCtx->userData, nullptr, rCtx->n, rCtx->node, errMsg.c_str());
    callbackLock.unlock();
    lock.lock();
}

void VSThreadPool::startInternal(const PFrameContext &context) {
    //technically this could be done by walking up the context chain and add a new notification to the correct one
    //unfortunately this would probably be quite slow for deep scripts so just hope the cache catches it

    if (context->n < 0)
        vsFatal("Negative frame request by: %s", context->clip->getName().c_str());

    // check to see if it's time to reevaluate cache sizes
    if (core->memory->isOverLimit()) {
        ticks = 0;
        notifyCaches(true);
    }

    // a normal tick for caches to adjust their sizes based on recent history
    if (!context->upstreamContext && ++ticks == 500) {
        ticks = 0;
        notifyCaches(false);
    }

    // add it immediately if the task is to return a completed frame or report an error since it never has an existing context
    if (context->returnedFrame || context->hasError()) {
        tasks.push_back(context);
    } else {
        if (context->upstreamContext)
            ++context->upstreamContext->numFrameRequests;

        NodeOutputKey p(context->clip, context->n, context->index);

        if (allContexts.count(p)) {
            PFrameContext &ctx = allContexts[p];
            assert(context->clip == ctx->clip && context->n == ctx->n && context->index == ctx->index);

            if (ctx->returnedFrame) {
                // special case where the requested frame is encountered "by accident"
                context->returnedFrame = ctx->returnedFrame;
                tasks.push_back(context);
            } else {
                // add it to the list of contexts to notify when it's available
                context->notificationChain = ctx->notificationChain;
                ctx->notificationChain = context;
                ctx->reqOrder = std::min(ctx->reqOrder, context->reqOrder);
            }
        } else {
            // create a new context and append it to the tasks
            allContexts[p] = context;
            tasks.push_back(context);
        }
    }
    wakeThread();
}

bool VSThreadPool::isWorkerThread() {
    std::lock_guard<std::mutex> m(lock);
    return allThreads.count(std::this_thread::get_id()) > 0;
}

void VSThreadPool::waitForDone() {
    // todo
}

VSThreadPool::~VSThreadPool() {
    std::unique_lock<std::mutex> m(lock);
    stopThreads = true;

    while (!allThreads.empty()) {
        auto iter = allThreads.begin();
        auto thread = iter->second;
        newWork.notify_all();
        m.unlock();
        thread->join();
        m.lock();
        allThreads.erase(iter);
        delete thread;
        newWork.notify_all();
    }

    assert(activeThreads == 0);
    assert(idleThreads == 0);
};
<|endoftext|>


<commit_before>#include <iostream>
using namespace std;

<commit_msg>amend<commit_after>#include <iostream>
using namespace std;

int main() {
	return 0;
}
<|endoftext|>


<commit_before>#include <QFileInfo>
#include "torrentcontentmodelitem.h"
#include <QDebug>

#include <algorithm>
#include <iterator>

void TorrentContentModelItem::init(QString name, const qlonglong size)
{
    // Do not display incomplete extensions
    if (name.endsWith(".!qB"))
    {
        name.chop(4);
    }
    m_itemData << name
               << size
               << QVariant()
               << 0.
               << prio::NORMAL;

    /* Update parent */
    m_parentItem->appendChild(this);
}

TorrentContentModelItem::TorrentContentModelItem(
    const libtorrent::file_entry& f,
    TorrentContentModelItem* parent)
    : m_parentItem(parent), m_type(TFILE), m_totalDone(0)
{
    Q_ASSERT(parent);
    m_path = QString::fromStdString(f.path);
    QString name = QFileInfo(m_path).fileName();

    init(name, f.size);

    m_parentItem->updateSize();
}

TorrentContentModelItem::TorrentContentModelItem(const QString& name, TorrentContentModelItem* parent)
    : m_parentItem(parent), m_type(FOLDER), m_totalDone(0)
{
    m_path = name;
    if (parent != nullptr && parent->isFolder())
    {
        m_path = parent->getPath() + '/' + m_path;
    }

    init(name, 0);
}

TorrentContentModelItem::TorrentContentModelItem(const QList<QVariant>& data)
    : m_parentItem(nullptr), m_type(ROOT), m_itemData(data), m_totalDone(0)
{
    Q_ASSERT(data.size() == NB_COL);
}

TorrentContentModelItem::~TorrentContentModelItem()
{
    qDeleteAll(m_childItems);
}

TorrentContentModelItem::FileType TorrentContentModelItem::getType() const
{
    return m_type;
}

void TorrentContentModelItem::deleteAllChildren()
{
    Q_ASSERT(m_type == ROOT);
    qDeleteAll(m_childItems);
    m_childItems.clear();
}

const QList<TorrentContentModelItem*>& TorrentContentModelItem::children() const
{
    return m_childItems;
}

QString TorrentContentModelItem::getPath() const
{
    return m_path;
}

QString TorrentContentModelItem::getName() const
{
    return m_itemData.at(COL_NAME).toString();
}

void TorrentContentModelItem::setName(const QString& name)
{
    Q_ASSERT(m_type != ROOT);
    m_itemData.replace(COL_NAME, name);
}

qulonglong TorrentContentModelItem::getSize() const
{
    return m_itemData.value(COL_SIZE).toULongLong();
}

void TorrentContentModelItem::setSize(qulonglong size)
{
    Q_ASSERT(m_type != ROOT);
    if (getSize() == size)
    {
        return;
    }
    m_itemData.replace(COL_SIZE, (qulonglong)size);
    m_parentItem->updateSize();
}

void TorrentContentModelItem::updateSize()
{
    if (m_type == ROOT)
    {
        return;
    }
    Q_ASSERT(m_type == FOLDER);
    const qulonglong size = std::accumulate(m_childItems.constBegin(), m_childItems.constEnd(), 0ull,
        [](qulonglong sum, TorrentContentModelItem* child)
        {
            return (child->getPriority() != prio::IGNORED) ? sum + child->getSize() : sum;
        });
    setSize(size);
}

void TorrentContentModelItem::setProgress(qulonglong done)
{
    Q_ASSERT(m_type != ROOT);
    if (getPriority() == 0) {
        return;
    }
    if (m_totalDone == done) {
        return;
    }
    const auto diff = done - m_totalDone;
    m_totalDone = done;
    const qulonglong size = getSize();
    Q_ASSERT(m_totalDone <= size);
    const qreal progress = (size > 0) ? m_totalDone / (qreal)size : 1.;
    Q_ASSERT(progress >= 0. && progress <= 1.);
    m_itemData.replace(COL_PROGRESS, progress);
    m_parentItem->setProgress(m_parentItem->getTotalDone() + diff);

    // STATUS COLUMN
    if ((done == size || progress == 1.) && m_type == TFILE)
    {
        setStatus(ItemDC::eFINISHED);
    }
}

qulonglong TorrentContentModelItem::getTotalDone() const
{
    return m_totalDone;
}

ItemDC::eSTATUSDC TorrentContentModelItem::getStatus() const
{
    QVariant v = m_itemData.value(COL_STATUS, ItemDC::eUNKNOWN);
    return (!v.isValid() || v.isNull())
        ? m_parentItem->getStatus()
        : (ItemDC::eSTATUSDC)v.toInt();
}

void TorrentContentModelItem::setStatus(ItemDC::eSTATUSDC status)
{
    m_itemData.replace(COL_STATUS, status);
}

void TorrentContentModelItem::updateStatus()
{
    ItemDC::eSTATUSDC status = ItemDC::eUNKNOWN;
    if (m_totalDone == getSize())
    {
        status = ItemDC::eFINISHED;
    }
    else if (getPriority() == prio::IGNORED)
    {
        status = ItemDC::ePAUSED;
    }
    else if (m_parentItem != nullptr)
    {
        status = m_parentItem->getStatus();
    }
    setStatus(status);
}

float TorrentContentModelItem::getProgress() const
{
    Q_ASSERT(m_type != ROOT);
    if (getPriority() == 0)
    {
        return -1;
    }
    qulonglong size = getSize();
    if (size > 0)
    {
        return m_totalDone / (float) getSize();
    }
    return 1.;
}

void TorrentContentModelItem::updateProgress()
{
    if (m_type == ROOT) { return; }
    Q_ASSERT(m_type == FOLDER);
    const auto totalDone = std::accumulate(m_childItems.constBegin(), m_childItems.constEnd(), 0ull,
        [](qulonglong sum, TorrentContentModelItem* child)
        {
            return (child->getPriority() != prio::IGNORED) ? sum + child->getTotalDone() : sum;
        });
    Q_ASSERT(totalDone <= getSize());
    setProgress(totalDone);
}

int TorrentContentModelItem::getPriority() const
{
    return m_itemData.value(COL_PRIO).toInt();
}

void TorrentContentModelItem::setPriority(int new_prio, bool update_parent)
{
    Q_ASSERT(new_prio != prio::PARTIAL || m_type == FOLDER); // PARTIAL only applies to folders
    if (getPriority() == new_prio)
    {
        return;
    }
    qDebug("setPriority(%s, %d)", qPrintable(getName()), new_prio);

    m_itemData.replace(COL_PRIO, new_prio);

    // Update parent
    if (update_parent && m_parentItem)
    {
        qDebug("Updating parent item");
        m_parentItem->updateSize();
        m_parentItem->updateProgress();
        m_parentItem->updatePriority();
    }

    // Update children
    if (new_prio != prio::PARTIAL && !m_childItems.empty())
    {
        qDebug("Updating children items");
        for (TorrentContentModelItem* child : qAsConst(m_childItems))
        {
            // Do not update the parent since
            // the parent is causing the update
            child->setPriority(new_prio, false);
        }
    }
    if (m_type == FOLDER)
    {
        updateSize();
        updateProgress();
    }
    else
    {
        updateStatus();
    }
}

// Only non-root folders use this function
void TorrentContentModelItem::updatePriority()
{
    if (m_type == ROOT) { return; }
    Q_ASSERT(m_type == FOLDER);
    if (m_childItems.isEmpty()) { return; }
    // If all children have the same priority
    // then the folder should have the same priority
    const int priority = m_childItems.first()->getPriority();
    const bool same_priorities = std::all_of(std::next(m_childItems.begin()), m_childItems.end(),
        [priority](auto item) { return item->getPriority() == priority; });
    setPriority(same_priorities? priority : prio::PARTIAL);
}

TorrentContentModelItem* TorrentContentModelItem::childWithName(const QString& name) const
{
    for (TorrentContentModelItem* child : qAsConst(m_childItems))
    {
        if (child->getName() == name)
        {
            return child;
        }
    }
    return 0;
}

bool TorrentContentModelItem::isFolder() const
{
    return (m_type == FOLDER);
}

void TorrentContentModelItem::appendChild(TorrentContentModelItem* item)
{
    Q_ASSERT(item);
    Q_ASSERT(m_type != TFILE);
    m_childItems.append(item);
}

TorrentContentModelItem* TorrentContentModelItem::child(int row) const
{
    return m_childItems.value(row, 0);
}

int TorrentContentModelItem::childCount() const
{
    return m_childItems.count();
}

int TorrentContentModelItem::columnCount() const
{
    return m_itemData.count();
}

QVariant TorrentContentModelItem::data(int column) const
{
    if (m_type != ROOT)
    {
        if (column == COL_PROGRESS)
        {
            return getProgress();
        }
        if (column == COL_STATUS)
        {
            return itemDCStatusToString(getStatus());
        }
    }

    return m_itemData.value(column);
}

int TorrentContentModelItem::row() const
{
    return m_parentItem ? m_parentItem->children().indexOf(const_cast<TorrentContentModelItem*>(this)) : 0;
}

TorrentContentModelItem* TorrentContentModelItem::parent() const
{
    return m_parentItem;
}
<commit_msg>debug configuration fixed<commit_after>#include <QFileInfo>
#include "torrentcontentmodelitem.h"
#include <QDebug>

#include <algorithm>
#include <iterator>

void TorrentContentModelItem::init(QString name, const qlonglong size)
{
    // Do not display incomplete extensions
    if (name.endsWith(".!qB"))
    {
        name.chop(4);
    }
    m_itemData << name
               << size
               << QVariant()
               << 0.
               << prio::NORMAL;

    /* Update parent */
    m_parentItem->appendChild(this);
}

TorrentContentModelItem::TorrentContentModelItem(
    const libtorrent::file_entry& f,
    TorrentContentModelItem* parent)
    : m_parentItem(parent), m_type(TFILE), m_totalDone(0)
{
    Q_ASSERT(parent);
    m_path = QString::fromStdString(f.path);
    QString name = QFileInfo(m_path).fileName();

    init(name, f.size);

    m_parentItem->updateSize();
}

TorrentContentModelItem::TorrentContentModelItem(const QString& name, TorrentContentModelItem* parent)
    : m_parentItem(parent), m_type(FOLDER), m_totalDone(0)
{
    m_path = name;
    if (parent != nullptr && parent->isFolder())
    {
        m_path = parent->getPath() + '/' + m_path;
    }

    init(name, 0);
}

TorrentContentModelItem::TorrentContentModelItem(const QList<QVariant>& data)
    : m_parentItem(nullptr), m_type(ROOT), m_itemData(data), m_totalDone(0)
{
    Q_ASSERT(data.size() == NB_COL);
}

TorrentContentModelItem::~TorrentContentModelItem()
{
    qDeleteAll(m_childItems);
}

TorrentContentModelItem::FileType TorrentContentModelItem::getType() const
{
    return m_type;
}

void TorrentContentModelItem::deleteAllChildren()
{
    Q_ASSERT(m_type == ROOT);
    qDeleteAll(m_childItems);
    m_childItems.clear();
}

const QList<TorrentContentModelItem*>& TorrentContentModelItem::children() const
{
    return m_childItems;
}

QString TorrentContentModelItem::getPath() const
{
    return m_path;
}

QString TorrentContentModelItem::getName() const
{
    return m_itemData.at(COL_NAME).toString();
}

void TorrentContentModelItem::setName(const QString& name)
{
    Q_ASSERT(m_type != ROOT);
    m_itemData.replace(COL_NAME, name);
}

qulonglong TorrentContentModelItem::getSize() const
{
    return m_itemData.value(COL_SIZE).toULongLong();
}

void TorrentContentModelItem::setSize(qulonglong size)
{
    Q_ASSERT(m_type != ROOT);
    if (getSize() == size)
    {
        return;
    }
    m_itemData.replace(COL_SIZE, (qulonglong)size);
    m_parentItem->updateSize();
}

void TorrentContentModelItem::updateSize()
{
    if (m_type == ROOT)
    {
        return;
    }
    Q_ASSERT(m_type == FOLDER);
    const qulonglong size = std::accumulate(m_childItems.constBegin(), m_childItems.constEnd(), 0ull,
        [](qulonglong sum, TorrentContentModelItem* child)
        {
            return (child->getPriority() != prio::IGNORED) ? sum + child->getSize() : sum;
        });
    setSize(size);
}

void TorrentContentModelItem::setProgress(qulonglong done)
{
    Q_ASSERT(m_type != ROOT);
    if (getPriority() == 0) {
        return;
    }
    if (m_totalDone == done) {
        return;
    }
    const auto diff = done - m_totalDone;
    m_totalDone = done;
    const qulonglong size = getSize();
    Q_ASSERT(m_totalDone <= size);
    const qreal progress = (size > 0) ? m_totalDone / (qreal)size : 1.;
    Q_ASSERT(progress >= 0. && progress <= 1.);
    m_itemData.replace(COL_PROGRESS, progress);
    if (m_parentItem->m_type != ROOT) {
        m_parentItem->setProgress(m_parentItem->getTotalDone() + diff);
    }

    // STATUS COLUMN
    if ((done == size || progress == 1.) && m_type == TFILE)
    {
        setStatus(ItemDC::eFINISHED);
    }
}

qulonglong TorrentContentModelItem::getTotalDone() const
{
    return m_totalDone;
}

ItemDC::eSTATUSDC TorrentContentModelItem::getStatus() const
{
    QVariant v = m_itemData.value(COL_STATUS, ItemDC::eUNKNOWN);
    return (!v.isValid() || v.isNull())
        ? m_parentItem->getStatus()
        : (ItemDC::eSTATUSDC)v.toInt();
}

void TorrentContentModelItem::setStatus(ItemDC::eSTATUSDC status)
{
    m_itemData.replace(COL_STATUS, status);
}

void TorrentContentModelItem::updateStatus()
{
    ItemDC::eSTATUSDC status = ItemDC::eUNKNOWN;
    if (m_totalDone == getSize())
    {
        status = ItemDC::eFINISHED;
    }
    else if (getPriority() == prio::IGNORED)
    {
        status = ItemDC::ePAUSED;
    }
    else if (m_parentItem != nullptr)
    {
        status = m_parentItem->getStatus();
    }
    setStatus(status);
}

float TorrentContentModelItem::getProgress() const
{
    Q_ASSERT(m_type != ROOT);
    if (getPriority() == 0)
    {
        return -1;
    }
    qulonglong size = getSize();
    if (size > 0)
    {
        return m_totalDone / (float) getSize();
    }
    return 1.;
}

void TorrentContentModelItem::updateProgress()
{
    if (m_type == ROOT) { return; }
    Q_ASSERT(m_type == FOLDER);
    const auto totalDone = std::accumulate(m_childItems.constBegin(), m_childItems.constEnd(), 0ull,
        [](qulonglong sum, TorrentContentModelItem* child)
        {
            return (child->getPriority() != prio::IGNORED) ? sum + child->getTotalDone() : sum;
        });
    Q_ASSERT(totalDone <= getSize());
    setProgress(totalDone);
}

int TorrentContentModelItem::getPriority() const
{
    return m_itemData.value(COL_PRIO).toInt();
}

void TorrentContentModelItem::setPriority(int new_prio, bool update_parent)
{
    Q_ASSERT(new_prio != prio::PARTIAL || m_type == FOLDER); // PARTIAL only applies to folders
    if (getPriority() == new_prio)
    {
        return;
    }
    qDebug("setPriority(%s, %d)", qPrintable(getName()), new_prio);

    m_itemData.replace(COL_PRIO, new_prio);

    // Update parent
    if (update_parent && m_parentItem)
    {
        qDebug("Updating parent item");
        m_parentItem->updateSize();
        m_parentItem->updateProgress();
        m_parentItem->updatePriority();
    }

    // Update children
    if (new_prio != prio::PARTIAL && !m_childItems.empty())
    {
        qDebug("Updating children items");
        for (TorrentContentModelItem* child : qAsConst(m_childItems))
        {
            // Do not update the parent since
            // the parent is causing the update
            child->setPriority(new_prio, false);
        }
    }
    if (m_type == FOLDER)
    {
        updateSize();
        updateProgress();
    }
    else
    {
        updateStatus();
    }
}

// Only non-root folders use this function
void TorrentContentModelItem::updatePriority()
{
    if (m_type == ROOT) { return; }
    Q_ASSERT(m_type == FOLDER);
    if (m_childItems.isEmpty()) { return; }
    // If all children have the same priority
    // then the folder should have the same priority
    const int priority = m_childItems.first()->getPriority();
    const bool same_priorities = std::all_of(std::next(m_childItems.begin()), m_childItems.end(),
        [priority](auto item) { return item->getPriority() == priority; });
    setPriority(same_priorities? priority : prio::PARTIAL);
}

TorrentContentModelItem* TorrentContentModelItem::childWithName(const QString& name) const
{
    for (TorrentContentModelItem* child : qAsConst(m_childItems))
    {
        if (child->getName() == name)
        {
            return child;
        }
    }
    return 0;
}

bool TorrentContentModelItem::isFolder() const
{
    return (m_type == FOLDER);
}

void TorrentContentModelItem::appendChild(TorrentContentModelItem* item)
{
    Q_ASSERT(item);
    Q_ASSERT(m_type != TFILE);
    m_childItems.append(item);
}

TorrentContentModelItem* TorrentContentModelItem::child(int row) const
{
    return m_childItems.value(row, 0);
}

int TorrentContentModelItem::childCount() const
{
    return m_childItems.count();
}

int TorrentContentModelItem::columnCount() const
{
    return m_itemData.count();
}

QVariant TorrentContentModelItem::data(int column) const
{
    if (m_type != ROOT)
    {
        if (column == COL_PROGRESS)
        {
            return getProgress();
        }
        if (column == COL_STATUS)
        {
            return itemDCStatusToString(getStatus());
        }
    }

    return m_itemData.value(column);
}

int TorrentContentModelItem::row() const
{
    return m_parentItem ? m_parentItem->children().indexOf(const_cast<TorrentContentModelItem*>(this)) : 0;
}

TorrentContentModelItem* TorrentContentModelItem::parent() const
{
    return m_parentItem;
}
<|endoftext|>


<commit_before>// Part of Arac Neural Network Composition Library.
// (c) 2008 by Justin S Bayer, <bayer.justin@googlemail.com>

#include <iostream>
#include <cstring>

#include "buffer.h"

using arac::common::Buffer;

Buffer::Buffer(size_t rowsize, bool owner) :
    _rowsize(rowsize),
    _owner(owner),
    _contmemory(true)
{
    expand();
}

Buffer::~Buffer()
{
    free_memory();
}

void Buffer::add(double* addend_p, int index)
{
    double* current_p = index == -1 ? _content.back() : _content[index];
    for(size_t i = 0; i < _rowsize; i++)
    {
        current_p[i] += addend_p[i];
    }
}

void
Buffer::append(double* row)
{
    _owner = false;
    if ((_contmemory) && (size() > 0))
    {
        if (row != _content.back() + sizeof(double) * _rowsize)
        {
            _contmemory = false;
        }
    }
    _content.push_back(row);
}

void Buffer::expand()
{
    if (!owner())
    {
        return;
    }
    double* new_chunk = new double[_rowsize];
    memset((void*) new_chunk, 0, sizeof(double) * _rowsize);
    _content.push_back(new_chunk);
    _contmemory = false;
}

void Buffer::clear()
{
    if (_contmemory)
    {
        // std::cout << "Big memory reset..." << std::endl;
        memset((void*) _content[0], 0, sizeof(double) * _rowsize * size());
    }
    else
    {
        // std::cout << "Small memory reset..." << std::endl;
        for(size_t i = 0; i < size(); i++)
        {
            clear_at(i);
        }
    }
}

void
Buffer::clear_at(size_t index)
{
    memset((void*) _content[index], 0, sizeof(double) * _rowsize);
}

void Buffer::free_memory()
{
    if (owner())
    {
        DoublePtrVec::iterator iter;
        for(iter = _content.begin(); iter != _content.end(); iter++)
        {
            delete[] *iter;
        }
    }
    _content.clear();
    _contmemory = true;
}
<commit_msg>Aesthetical.<commit_after>// Part of Arac Neural Network Composition Library.
// (c) 2008 by Justin S Bayer, <bayer.justin@googlemail.com>

#include <iostream>
#include <cstring>

#include "buffer.h"

using arac::common::Buffer;

Buffer::Buffer(size_t rowsize, bool owner) :
    _rowsize(rowsize),
    _owner(owner),
    _contmemory(true)
{
    expand();
}

Buffer::~Buffer()
{
    free_memory();
}

void Buffer::add(double* addend_p, int index)
{
    double* current_p = index == -1 ? _content.back() : _content[index];
    for(size_t i = 0; i < _rowsize; i++)
    {
        current_p[i] += addend_p[i];
    }
}

void
Buffer::append(double* row)
{
    _owner = false;
    if ((_contmemory) && (size() > 0))
    {
        if (row != _content.back() + sizeof(double) * _rowsize)
        {
            _contmemory = false;
        }
    }
    _content.push_back(row);
}

void Buffer::expand()
{
    if (!owner())
    {
        return;
    }
    double* new_chunk = new double[_rowsize];
    memset((void*) new_chunk, 0, sizeof(double) * _rowsize);
    _content.push_back(new_chunk);
    _contmemory = false;
}

void Buffer::clear()
{
    if (_contmemory)
    {
        // std::cout << "Big memory reset..." << std::endl;
        memset((void*) _content[0], 0, sizeof(double) * _rowsize * size());
    }
    else
    {
        // std::cout << "Small memory reset..." << std::endl;
        for(size_t i = 0; i < size(); i++)
        {
            clear_at(i);
        }
    }
}

void
Buffer::clear_at(size_t index)
{
    memset((void*) _content[index], 0, sizeof(double) * _rowsize);
}

void Buffer::free_memory()
{
    if (owner())
    {
        DoublePtrVec::iterator iter;
        for(iter = _content.begin(); iter != _content.end(); iter++)
        {
            delete[] *iter;
        }
    }
    _content.clear();
    _contmemory = true;
}
<|endoftext|>


<commit_before><commit_msg>SomaticQC: minor improvements in the calculation<commit_after><|endoftext|>