This commit is contained in:
Ara Ayvazyan 2017-04-25 10:52:34 -07:00
Родитель 0b4a694632
Коммит f3050e22c5
216 изменённых файлов: 17003 добавлений и 247 удалений

280
.gitignore поставляемый
Просмотреть файл

@ -1,252 +1,42 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
/ipch
*.gch
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Fortran module files
*.mod
*.smod
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# TFS 2012 Local Workspace
$tf/
# Executables
*.exe
*.out
*.app
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# Nuget packages
/packages
# Visual Studio files
/.vs
/IPC.VC.VC.opendb
/IPC.VC.db
*.vcxproj.user
# Build folders
**/x64

64
IPC.sln Normal file
Просмотреть файл

@ -0,0 +1,64 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Native", "Native\Build\Native.vcxproj", "{2030ED0D-4667-4299-87CD-ACE298BDF56D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "UnitTests\Build\UnitTests.vcxproj", "{2A3B9657-1D10-4A71-AA21-62AD4106CED4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Managed", "Managed\Build\Managed.vcxproj", "{705098F7-93DC-4954-8165-FDDCD66231F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestsManaged", "UnitTestsManaged\UnitTestsManaged.csproj", "{BDFCB3AD-21A8-446E-840B-68718C49E7D4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestManagedTransport", "TestManagedTransport\Build\TestManagedTransport.vcxproj", "{AA58310A-0C12-4F55-A04F-F9151828F64B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Interop", "Interop\Build\Interop.vcxproj", "{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transport", "Transport\Transport.csproj", "{BDCCCCCD-21A8-446E-840B-68718C49E7D4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Object", "Object\Object.csproj", "{2A0B90AC-9117-48DA-B04C-FF194498D95D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2030ED0D-4667-4299-87CD-ACE298BDF56D}.Debug|x64.ActiveCfg = Debug|x64
{2030ED0D-4667-4299-87CD-ACE298BDF56D}.Debug|x64.Build.0 = Debug|x64
{2030ED0D-4667-4299-87CD-ACE298BDF56D}.Release|x64.ActiveCfg = Release|x64
{2030ED0D-4667-4299-87CD-ACE298BDF56D}.Release|x64.Build.0 = Release|x64
{2A3B9657-1D10-4A71-AA21-62AD4106CED4}.Debug|x64.ActiveCfg = Debug|x64
{2A3B9657-1D10-4A71-AA21-62AD4106CED4}.Debug|x64.Build.0 = Debug|x64
{2A3B9657-1D10-4A71-AA21-62AD4106CED4}.Release|x64.ActiveCfg = Release|x64
{2A3B9657-1D10-4A71-AA21-62AD4106CED4}.Release|x64.Build.0 = Release|x64
{705098F7-93DC-4954-8165-FDDCD66231F0}.Debug|x64.ActiveCfg = Debug|x64
{705098F7-93DC-4954-8165-FDDCD66231F0}.Debug|x64.Build.0 = Debug|x64
{705098F7-93DC-4954-8165-FDDCD66231F0}.Release|x64.ActiveCfg = Release|x64
{705098F7-93DC-4954-8165-FDDCD66231F0}.Release|x64.Build.0 = Release|x64
{BDFCB3AD-21A8-446E-840B-68718C49E7D4}.Debug|x64.ActiveCfg = Debug|x64
{BDFCB3AD-21A8-446E-840B-68718C49E7D4}.Debug|x64.Build.0 = Debug|x64
{BDFCB3AD-21A8-446E-840B-68718C49E7D4}.Release|x64.ActiveCfg = Release|x64
{BDFCB3AD-21A8-446E-840B-68718C49E7D4}.Release|x64.Build.0 = Release|x64
{AA58310A-0C12-4F55-A04F-F9151828F64B}.Debug|x64.ActiveCfg = Debug|x64
{AA58310A-0C12-4F55-A04F-F9151828F64B}.Debug|x64.Build.0 = Debug|x64
{AA58310A-0C12-4F55-A04F-F9151828F64B}.Release|x64.ActiveCfg = Release|x64
{AA58310A-0C12-4F55-A04F-F9151828F64B}.Release|x64.Build.0 = Release|x64
{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}.Debug|x64.ActiveCfg = Debug|x64
{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}.Debug|x64.Build.0 = Debug|x64
{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}.Release|x64.ActiveCfg = Release|x64
{A13012C1-76DE-4D1D-A58B-2361D2BE8F65}.Release|x64.Build.0 = Release|x64
{BDCCCCCD-21A8-446E-840B-68718C49E7D4}.Debug|x64.ActiveCfg = Debug|x64
{BDCCCCCD-21A8-446E-840B-68718C49E7D4}.Debug|x64.Build.0 = Debug|x64
{BDCCCCCD-21A8-446E-840B-68718C49E7D4}.Release|x64.ActiveCfg = Release|x64
{BDCCCCCD-21A8-446E-840B-68718C49E7D4}.Release|x64.Build.0 = Release|x64
{2A0B90AC-9117-48DA-B04C-FF194498D95D}.Debug|x64.ActiveCfg = Debug|x64
{2A0B90AC-9117-48DA-B04C-FF194498D95D}.Debug|x64.Build.0 = Debug|x64
{2A0B90AC-9117-48DA-B04C-FF194498D95D}.Release|x64.ActiveCfg = Release|x64
{2A0B90AC-9117-48DA-B04C-FF194498D95D}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

Двоичные данные
IPC.snk Normal file

Двоичный файл не отображается.

64
Inc/IPC/Accept.h Normal file
Просмотреть файл

@ -0,0 +1,64 @@
#pragma once
#include "detail/Accept.h"
#include "Acceptor.h"
#include "ComponentCollection.h"
#include "Client.h"
#include "Server.h"
#include <memory>
namespace IPC
{
template <typename Request, typename Response, typename Traits = DefaultTraits, typename HandlerFactory, typename ErrorHandler = typename Traits::ErrorHandler>
auto AcceptServers(
const char* name,
HandlerFactory&& handlerFactory,
ChannelSettings<Traits> channelSettings = {},
std::size_t hostInfoMemorySize = 0,
ErrorHandler&& errorHandler = {})
{
using Server = Server<Request, Response, Traits>;
return detail::Accept<ServerAcceptor<Request, Response, Traits>>(
name,
std::make_shared<ServerCollection<Server>>(),
[handlerFactory = std::forward<HandlerFactory>(handlerFactory)](auto&& connection, auto&& closeHandler) mutable
{
auto handler = handlerFactory(*connection);
return std::make_unique<Server>(
std::move(connection), std::forward<decltype(handler)>(handler), std::forward<decltype(closeHandler)>(closeHandler));
},
std::forward<ErrorHandler>(errorHandler),
std::move(channelSettings),
hostInfoMemorySize);
}
template <typename Request, typename Response, typename Traits = DefaultTraits, typename ErrorHandler = typename Traits::ErrorHandler>
auto AcceptClients(
const char* name,
ChannelSettings<Traits> channelSettings = {},
std::size_t hostInfoMemorySize = 0,
ErrorHandler&& errorHandler = {},
typename Traits::TransactionManagerFactory transactionManagerFactory = {})
{
using Client = Client<Request, Response, Traits>;
return detail::Accept<ClientAcceptor<Request, Response, Traits>>(
name,
std::make_shared<ClientCollection<Client>>(),
[transactionManagerFactory = std::move(transactionManagerFactory)](auto&& connection, auto&& closeHandler)
{
return std::make_unique<Client>(
std::move(connection),
std::forward<decltype(closeHandler)>(closeHandler),
transactionManagerFactory(detail::Identity<typename Client::TransactionManager>{}));
},
std::forward<ErrorHandler>(errorHandler),
std::move(channelSettings),
hostInfoMemorySize);
}
} // IPC

299
Inc/IPC/Acceptor.h Normal file
Просмотреть файл

@ -0,0 +1,299 @@
#pragma once
#include "AcceptorFwd.h"
#include "Server.h"
#include "DefaultTraits.h"
#include "ComponentCollection.h"
#include "detail/ChannelFactory.h"
#include "detail/Info.h"
#include "detail/KernelEvent.h"
#include "detail/KernelProcess.h"
#include "detail/RandomString.h"
#include "detail/Apply.h"
#include "ComponentCollection.h"
#include "Exception.h"
#include <memory>
#include <future>
namespace IPC
{
template <typename Input, typename Output, typename Traits>
class Acceptor
{
using ChannelFactory = detail::ChannelFactory<Traits>;
public:
using Connection = Connection<Input, Output, Traits>;
template <typename Handler>
Acceptor(const char* name, Handler&& handler, ChannelSettings<Traits> channelSettings = {}, std::size_t hostInfoMemorySize = 0)
: m_acceptorHostInfo{ std::make_shared<AcceptorHostInfoMemory>(name, channelSettings, hostInfoMemorySize) },
m_pingChannel{ create_only, nullptr, m_acceptorHostInfo, channelSettings.GetWaitHandleFactory(), channelSettings.GetReceiverFactory() },
m_closeEvent{ create_only, true, false, (*m_acceptorHostInfo)->m_acceptorCloseEventName.c_str() },
m_handler{ std::forward<Handler>(handler) },
m_channelFactory{ std::move(channelSettings) }
{
if (!m_pingChannel.RegisterReceiver(
[this](detail::ConnectorPingInfo&& pingInfo)
{
try
{
HostServer(std::move(pingInfo));
}
catch (...)
{
std::promise<std::unique_ptr<Connection>> promise; // TODO: Use custom Maybe<T> in such places to avoid allocation costs in future (?).
promise.set_exception(std::current_exception());
m_handler(promise.get_future());
}
}))
{
throw Exception{ "Failed to register a receiver." };
}
}
Acceptor(const Acceptor& other) = delete;
Acceptor& operator=(const Acceptor& other) = delete;
Acceptor(Acceptor&& other) = default;
Acceptor& operator=(Acceptor&& other) = default;
~Acceptor()
{
m_closeEvent.Signal();
m_pingChannel.UnregisterReceiver();
}
private:
class AcceptorHostInfoMemory : public SharedMemory
{
public:
AcceptorHostInfoMemory(const char* name, const ChannelSettings<Traits>& channelSettings, std::size_t hostInfoMemorySize)
: SharedMemory{ create_only, name, hostInfoMemorySize != 0 ? hostInfoMemorySize : (1 * 1024 * 1024) }
{
InvokeAtomic(
[&]
{
auto allocator = GetAllocator<char>();
m_info = &Construct<detail::AcceptorHostInfo>(unique_instance, allocator);
m_info->m_processId = detail::KernelProcess::GetCurrentProcessId();
m_info->m_acceptorCloseEventName.assign(detail::GenerateRandomString().c_str());
if (const auto& memory = channelSettings.GetCommonInput())
{
m_info->m_settings.m_commonInputMemoryName.emplace(memory->GetName().c_str(), allocator);
}
if (const auto& memory = channelSettings.GetCommonOutput())
{
m_info->m_settings.m_commonOutputMemoryName.emplace(memory->GetName().c_str(), allocator);
}
});
}
const detail::AcceptorHostInfo* operator->() const
{
return m_info;
}
private:
detail::AcceptorHostInfo* m_info{ nullptr };
};
using Server = Server<detail::ConnectorInfo, detail::AcceptorInfo, Traits>;
class AcceptorServer : public Server
{
public:
template <typename Handler, typename CloseHandler>
AcceptorServer(
ChannelFactory channelFactory,
const detail::ConnectorPingInfo& pingInfo,
Handler&& handler,
CloseHandler&& closeHandler)
: AcceptorServer{
std::move(channelFactory),
pingInfo,
detail::KernelEvent{ open_only, pingInfo.m_connectorCloseEventName.c_str() },
std::forward<Handler>(handler),
std::forward<CloseHandler>(closeHandler) }
{}
private:
template <typename Handler, typename CloseHandler>
AcceptorServer(
ChannelFactory&& channelFactory,
const detail::ConnectorPingInfo& pingInfo,
detail::KernelEvent&& connectorCloseEvent,
Handler&& handler,
CloseHandler&& closeHandler)
try
: AcceptorServer{
std::move(channelFactory),
OpenChannelsOrdered(pingInfo, channelFactory),
connectorCloseEvent,
detail::KernelProcess{ pingInfo.m_processId },
std::forward<Handler>(handler),
std::forward<CloseHandler>(closeHandler) }
{}
catch (...)
{
connectorCloseEvent.Signal();
throw;
}
template <typename Channels, typename Handler, typename CloseHandler>
AcceptorServer(
ChannelFactory&& channelFactory,
Channels&& channels,
detail::KernelEvent connectorCloseEvent,
detail::KernelProcess&& process,
Handler&& handler,
CloseHandler&& closeHandler)
: Server{
std::make_unique<typename Server::Connection>(
nullptr,
std::move(connectorCloseEvent),
process,
channelFactory.GetWaitHandleFactory(),
std::move(channels.first),
std::move(channels.second)),
[this, process, channelFactory, handler = std::forward<Handler>(handler)](
detail::ConnectorInfo&& connectorInfo, auto&& callback) mutable
{
handler(
this->GetConnection(),
process,
std::move(connectorInfo),
channelFactory.MakeInstance(),
std::forward<decltype(callback)>(callback));
},
std::forward<CloseHandler>(closeHandler) }
{}
static auto OpenChannelsOrdered(const detail::ConnectorPingInfo& pingInfo, const ChannelFactory& channelFactory)
{
auto channelFactoryInstance = channelFactory.MakeInstance();
// Open in a strict order. Must be the reverse of the connector.
auto input = channelFactoryInstance.template OpenInput<typename Server::InputPacket>(pingInfo.m_connectorInfoChannelName.c_str());
auto output = channelFactoryInstance.template OpenOutput<typename Server::OutputPacket>(pingInfo.m_acceptorInfoChannelName.c_str());
return std::make_pair(std::move(input), std::move(output));
}
};
void HostServer(detail::ConnectorPingInfo&& pingInfo)
{
m_servers.Accept(
[&](auto&& closeHandler)
{
return std::make_unique<AcceptorServer>(
m_channelFactory.Override( // Input and output must be flipped.
pingInfo.m_settings.m_commonOutputMemoryName
? pingInfo.m_settings.m_commonOutputMemoryName->c_str()
: nullptr,
pingInfo.m_settings.m_commonInputMemoryName
? pingInfo.m_settings.m_commonInputMemoryName->c_str()
: nullptr),
pingInfo,
[this](
const typename AcceptorServer::Connection& connection,
const detail::KernelProcess& process,
detail::ConnectorInfo&& connectorInfo,
typename ChannelFactory::Instance&& channelFactoryInstance,
auto&& callback)
{
std::packaged_task<std::unique_ptr<Connection>()> task{
[&]
{
return Accept(
process,
detail::AcceptorInfo{ connection.GetOutputChannel().GetMemory()->GetAllocator<char>() },
std::move(connectorInfo),
std::move(channelFactoryInstance),
callback);
} };
auto result = task.get_future();
task();
m_handler(std::move(result));
},
std::forward<decltype(closeHandler)>(closeHandler));
});
}
template <typename Callback>
std::unique_ptr<Connection> Accept(
detail::KernelProcess process,
detail::AcceptorInfo&& acceptorInfo,
detail::ConnectorInfo&& connectorInfo,
typename ChannelFactory::Instance&& channelFactoryInstance,
Callback& callback)
{
acceptorInfo.m_closeEventName.assign(detail::GenerateRandomString().c_str());
return detail::ApplyTuple(
[&](auto&&... channels)
{
detail::KernelEvent closeEvent{ create_only, false, false, acceptorInfo.m_closeEventName.c_str() };
auto connection = std::make_unique<Connection>(
closeEvent,
closeEvent,
std::move(process),
channelFactoryInstance.GetWaitHandleFactory(),
std::forward<decltype(channels)>(channels)...);
callback(std::move(acceptorInfo));
return connection;
},
CollectChannels(connectorInfo, acceptorInfo, channelFactoryInstance));
}
template <typename I = Input, typename O = Output, std::enable_if_t<!std::is_void<I>::value && !std::is_void<O>::value>* = nullptr>
auto CollectChannels(detail::ConnectorInfo& connectorInfo, detail::AcceptorInfo& acceptorInfo, typename ChannelFactory::Instance& channelFactoryInstance)
{
// Make sure opening input channel happens before creating output (important when both must use the same memory).
auto input = CollectChannels<Input, void>(connectorInfo, acceptorInfo, channelFactoryInstance);
auto output = CollectChannels<void, Output>(connectorInfo, acceptorInfo, channelFactoryInstance);
return std::tuple_cat(std::move(input), std::move(output));
}
template <typename I = Input, typename O = Output, std::enable_if_t<!std::is_void<I>::value && std::is_void<O>::value>* = nullptr>
auto CollectChannels(detail::ConnectorInfo& connectorInfo, detail::AcceptorInfo& /*acceptorInfo*/, typename ChannelFactory::Instance& channelFactoryInstance)
{
return std::make_tuple(channelFactoryInstance.template OpenInput<Input>(connectorInfo.m_channelName.c_str()));
}
template <typename I = Input, typename O = Output, std::enable_if_t<std::is_void<I>::value && !std::is_void<O>::value>* = nullptr>
auto CollectChannels(detail::ConnectorInfo& /*connectorInfo*/, detail::AcceptorInfo& acceptorInfo, typename ChannelFactory::Instance& channelFactoryInstance)
{
acceptorInfo.m_channelName.assign(detail::GenerateRandomString().c_str());
return std::make_tuple(channelFactoryInstance.template CreateOutput<Output>(acceptorInfo.m_channelName.c_str()));
}
std::shared_ptr<AcceptorHostInfoMemory> m_acceptorHostInfo;
InputChannel<detail::ConnectorPingInfo, Traits> m_pingChannel;
detail::KernelEvent m_closeEvent;
detail::Callback<void(std::future<std::unique_ptr<Connection>>)> m_handler;
ChannelFactory m_channelFactory;
ComponentCollection<std::list<std::unique_ptr<AcceptorServer>>> m_servers; // Must be the last member.
};
template <typename PacketInfoT> // TODO: Remove the T suffix when VC14 bugs are fixed. Confuses with global PacketInfo struct.
class PacketAcceptor
: public Acceptor<typename PacketInfoT::InputPacket, typename PacketInfoT::OutputPacket, typename PacketInfoT::Traits>,
public PacketInfoT
{
using Acceptor<typename PacketInfoT::InputPacket, typename PacketInfoT::OutputPacket, typename PacketInfoT::Traits>::Acceptor;
};
} // IPC

22
Inc/IPC/AcceptorFwd.h Normal file
Просмотреть файл

@ -0,0 +1,22 @@
#pragma once
#include "detail/PacketFwd.h"
namespace IPC
{
struct DefaultTraits;
template <typename Input, typename Output, typename Traits = DefaultTraits>
class Acceptor;
template <typename PacketInfo>
class PacketAcceptor;
template <typename Request, typename Response, typename Traits = DefaultTraits>
using ClientAcceptor = PacketAcceptor<detail::ClientPacketInfo<Request, Response, Traits>>;
template <typename Request, typename Response, typename Traits = DefaultTraits>
using ServerAcceptor = PacketAcceptor<detail::ServerPacketInfo<Request, Response, Traits>>;
} // IPC

46
Inc/IPC/ChannelSettings.h Normal file
Просмотреть файл

@ -0,0 +1,46 @@
#pragma once
#include "detail/ChannelSettingsBase.h"
namespace IPC
{
struct DefaultTraits;
template <typename Traits>
class ChannelSettings : public detail::ChannelSettingsBase
{
public:
ChannelSettings()
: ChannelSettings{ {} }
{}
explicit ChannelSettings(
typename Traits::WaitHandleFactory waitHandleFactory,
typename Traits::ReceiverFactory receiverFactory = {},
std::shared_ptr<SharedMemoryCache> cache = {})
: ChannelSettingsBase{ std::move(cache) },
m_waitHandleFactory{ std::move(waitHandleFactory) },
m_receiverFactory{ std::move(receiverFactory) }
{}
const auto& GetWaitHandleFactory() const
{
return m_waitHandleFactory;
}
const auto& GetReceiverFactory() const
{
return m_receiverFactory;
}
private:
typename Traits::WaitHandleFactory m_waitHandleFactory;
typename Traits::ReceiverFactory m_receiverFactory;
};
using DefaultChannelSettings = ChannelSettings<DefaultTraits>;
} // IPC

124
Inc/IPC/Client.h Normal file
Просмотреть файл

@ -0,0 +1,124 @@
#pragma once
#include "ClientFwd.h"
#include "detail/PacketConnectionHolder.h"
#include "detail/Packet.h"
#include "detail/Callback.h"
#include <future>
namespace IPC
{
namespace detail
{
template <typename Request, typename Response, typename Traits>
class ClientTraits : public ClientPacketInfo<Request, Response, Traits>
{
struct NullTransactionManager
{};
public:
using Context = std::conditional_t<
std::is_void<Response>::value,
NullTransactionManager,
typename Traits::template TransactionManager<Callback<void(Response)>>>;
};
} // detail
template <typename Request, typename Response, typename Traits>
class Client : public detail::PacketConnectionHolder<detail::ClientTraits<Request, Response, Traits>>
{
using PacketConnectionHolder = detail::PacketConnectionHolder<detail::ClientTraits<Request, Response, Traits>>;
protected:
using typename PacketConnectionHolder::InputPacket;
using typename PacketConnectionHolder::OutputPacket;
public:
static_assert(!std::is_void<Request>::value, "Request cannot be void.");
static_assert(std::is_same<Connection, ClientConnection<Request, Response, Traits>>::value, "Connection definitions must be the same.");
using typename PacketConnectionHolder::Connection;
using TransactionManager = typename PacketConnectionHolder::Context;
template <typename CloseHandler, typename U = Response, std::enable_if_t<!std::is_void<U>::value>* = nullptr>
Client(std::unique_ptr<Connection> connection, CloseHandler&& closeHandler, TransactionManager transactionManager = {})
: PacketConnectionHolder{
std::move(connection),
std::move(transactionManager),
[this](InputPacket&& packet) { ResponseHandler(std::move(packet)); },
[this, closeHandler = std::forward<CloseHandler>(closeHandler)]() mutable
{
GetTransactionManager().TerminateTransactions();
closeHandler();
} }
{}
template <typename CloseHandler, typename U = Response, std::enable_if_t<std::is_void<U>::value>* = nullptr>
Client(std::unique_ptr<Connection> connection, CloseHandler&& closeHandler)
: PacketConnectionHolder{ std::move(connection), {}, std::forward<CloseHandler>(closeHandler) }
{}
template <typename OtherRequest, typename Callback, typename... TransactionArgs, typename U = Response, std::enable_if_t<!std::is_void<U>::value>* = nullptr,
decltype(std::declval<Callback>()(std::declval<U>()))* = nullptr>
void operator()(OtherRequest&& request, Callback&& callback, TransactionArgs&&... transactionArgs)
{
auto&& id = GetTransactionManager().BeginTransaction(std::forward<Callback>(callback), std::forward<TransactionArgs>(transactionArgs)...);
try
{
SendRequest(std::forward<OtherRequest>(request), id);
}
catch (...)
{
GetTransactionManager().EndTransaction(id);
throw;
}
}
template <typename OtherRequest, typename U = Response, std::enable_if_t<std::is_void<U>::value>* = nullptr>
void operator()(OtherRequest&& request)
{
SendRequest(std::forward<OtherRequest>(request));
}
template <typename OtherRequest, typename... TransactionArgs, typename U = Response, std::enable_if_t<!std::is_void<U>::value>* = nullptr>
std::future<Response> operator()(OtherRequest&& request, TransactionArgs&&... transactionArgs)
{
std::packaged_task<Response(Response&&)> callback{ [](Response&& response) { return response; } };
auto result = callback.get_future();
operator()(std::forward<OtherRequest>(request), std::move(callback), std::forward<TransactionArgs>(transactionArgs)...);
return result;
}
private:
auto& GetTransactionManager()
{
return this->GetContext();
}
template <typename OtherRequest, typename... Args>
void SendRequest(OtherRequest&& request, Args&&... args)
{
this->GetConnection().GetOutputChannel().Send(OutputPacket{ std::forward<Args>(args)..., std::forward<OtherRequest>(request) });
}
template <typename Packet = InputPacket, typename U = Response, std::enable_if_t<!std::is_void<U>::value>* = nullptr>
void ResponseHandler(Packet&& packet)
{
if (auto callback = GetTransactionManager().EndTransaction(packet.GetId()))
{
if (packet.IsValid())
{
(*callback)(std::move(packet.GetPayload()));
}
}
}
};
} // IPC

17
Inc/IPC/ClientFwd.h Normal file
Просмотреть файл

@ -0,0 +1,17 @@
#pragma once
#include "detail/PacketConnectionFwd.h"
#include "detail/PacketFwd.h"
namespace IPC
{
struct DefaultTraits;
template <typename Request, typename Response, typename Traits = DefaultTraits>
class Client;
template <typename Request, typename Response, typename Traits = DefaultTraits>
using ClientConnection = detail::PacketConnection<detail::ClientPacketInfo<Request, Response, Traits>>;
} // IPC

Просмотреть файл

@ -0,0 +1,215 @@
#pragma once
#include <vector>
#include <list>
#include <memory>
#include <shared_mutex>
namespace IPC
{
namespace detail
{
template <typename T>
struct IsStable : std::false_type
{};
template <typename T, typename Enable = void>
class ComponentCollectionInserter;
template <typename T>
class ComponentCollectionInserter<T, std::enable_if_t<!IsStable<T>::value>>
{
static_assert(IsStable<T>::value, "Only stable containers are allowed.");
};
} // detail
template <typename Container>
class ComponentCollection
{
public:
ComponentCollection()
: m_components{ std::make_shared<LockableContainer>() },
m_inserter{ *m_components }
{}
ComponentCollection(const ComponentCollection& other) = delete;
ComponentCollection& operator=(const ComponentCollection& other) = delete;
ComponentCollection(ComponentCollection&& other) = default;
ComponentCollection& operator=(ComponentCollection&& other) = default;
~ComponentCollection()
{
if (m_components)
{
std::vector<typename Container::value_type> components;
auto newContainer = std::make_shared<LockableContainer>();
{
std::lock_guard<std::shared_timed_mutex> guard{ *m_components };
std::copy(
std::make_move_iterator(std::begin(*m_components)),
std::make_move_iterator(std::end(*m_components)),
std::back_inserter(components));
m_components.swap(newContainer);
}
}
}
explicit operator bool() const
{
return static_cast<bool>(m_components);
}
template <typename ComponentFactory>
bool Accept(ComponentFactory&& componentFactory)
{
struct Deleter
{
void operator()(void*) const
{
if (auto components = m_components.lock())
{
std::lock_guard<std::shared_timed_mutex> guard{ *components };
components->erase(m_location);
}
}
std::weak_ptr<LockableContainer> m_components;
typename Container::iterator m_location{};
};
std::shared_ptr<void> eraser{ nullptr, Deleter{} };
auto component = componentFactory([eraser]() mutable { eraser.reset(); });
if (!eraser.unique())
{
auto deleter = std::get_deleter<Deleter>(eraser);
assert(deleter);
{
std::lock_guard<std::shared_timed_mutex> guard{ *m_components };
auto location = m_inserter(std::move(component));
if (location != m_components->end()) // Otherwise leave the deleter->m_components empty so that Deleter becomes a no-op.
{
deleter->m_location = location;
deleter->m_components = m_components;
return true;
}
}
}
return false;
}
auto GetComponents() const
{
class Accessor : private std::shared_lock<std::shared_timed_mutex>
{
public:
explicit Accessor(std::shared_ptr<LockableContainer> components)
: shared_lock{ *components },
m_components{ std::move(components) }
{}
const Container& operator*() const
{
return *m_components;
}
const Container* operator->() const
{
return m_components.get();
}
private:
std::shared_ptr<LockableContainer> m_components;
};
return Accessor{ m_components };
}
private:
struct LockableContainer : Container, std::shared_timed_mutex // TODO: Use std::shared_mutex when available in VC14.
{
LockableContainer() = default;
};
std::shared_ptr<LockableContainer> m_components;
detail::ComponentCollectionInserter<Container> m_inserter;
};
namespace detail
{
template <typename T, typename Allocator>
struct IsStable<std::list<T, Allocator>> : std::true_type
{};
template <typename T, typename Allocator>
class ComponentCollectionInserter<std::list<T, Allocator>>
{
public:
explicit ComponentCollectionInserter(std::list<T, Allocator>& container)
: m_container{ &container }
{}
template <typename U>
auto operator()(U&& value)
{
return m_container->insert(m_container->end(), std::forward<U>(value));
}
private:
std::list<T, Allocator>* m_container;
};
} // detail
template <typename Server>
class ServerCollection : public ComponentCollection<std::list<std::shared_ptr<Server>>>
{
using Base = ComponentCollection<std::list<std::shared_ptr<Server>>>;
public:
using Base::Accept;
template <typename Handler>
bool Accept(std::unique_ptr<typename Server::Connection> connection, Handler&& handler)
{
return Base::Accept(
[&](auto&& closeHandler)
{
return std::make_shared<Server>(std::move(connection), std::forward<Handler>(handler), std::forward<decltype(closeHandler)>(closeHandler));
});
}
};
template <typename Client>
class ClientCollection : public ComponentCollection<std::list<std::shared_ptr<Client>>>
{
using Base = ComponentCollection<std::list<std::shared_ptr<Client>>>;
public:
using Base::Accept;
bool Accept(std::unique_ptr<typename Client::Connection> connection)
{
return Base::Accept(
[&](auto&& closeHandler)
{
return std::make_shared<Client>(std::move(connection), std::forward<decltype(closeHandler)>(closeHandler));
});
}
};
} // IPC

78
Inc/IPC/Connect.h Normal file
Просмотреть файл

@ -0,0 +1,78 @@
#pragma once
#include "detail/Connect.h"
#include "DefaultTraits.h"
#include "Client.h"
#include "Server.h"
#include <memory>
#include <chrono>
namespace IPC
{
template <
typename PacketConnector,
typename TimeoutFactory = typename PacketConnector::Traits::TimeoutFactory,
typename ErrorHandler = typename PacketConnector::Traits::ErrorHandler,
typename... TransactionArgs>
auto ConnectClient(
const char* acceptorName,
std::shared_ptr<PacketConnector> connector,
bool async,
TimeoutFactory&& timeoutFactory = {},
ErrorHandler&& errorHandler = {},
typename PacketConnector::Traits::TransactionManagerFactory transactionManagerFactory = {},
TransactionArgs&&... transactionArgs)
{
return detail::Connect(
acceptorName,
std::move(connector),
async,
std::forward<TimeoutFactory>(timeoutFactory),
std::forward<ErrorHandler>(errorHandler),
[transactionManagerFactory = std::move(transactionManagerFactory)](auto&& connection, auto&& closeHandler) mutable
{
using Client = Client<typename PacketConnector::Request, typename PacketConnector::Response, typename PacketConnector::Traits>;
return std::make_unique<Client>(
std::move(connection),
std::forward<decltype(closeHandler)>(closeHandler),
transactionManagerFactory(detail::Identity<typename Client::TransactionManager>{}));
},
std::forward<TransactionArgs>(transactionArgs)...);
}
template <
typename PacketConnector,
typename HandlerFactory,
typename TimeoutFactory = typename PacketConnector::Traits::TimeoutFactory,
typename ErrorHandler = typename PacketConnector::Traits::ErrorHandler,
typename... TransactionArgs>
auto ConnectServer(
const char* acceptorName,
std::shared_ptr<PacketConnector> connector,
HandlerFactory&& handlerFactory,
bool async,
TimeoutFactory&& timeoutFactory = {},
ErrorHandler&& errorHandler = {},
TransactionArgs&&... transactionArgs)
{
return detail::Connect(
acceptorName,
std::move(connector),
async,
std::forward<TimeoutFactory>(timeoutFactory),
std::forward<ErrorHandler>(errorHandler),
[handlerFactory = std::forward<HandlerFactory>(handlerFactory)](auto&& connection, auto&& closeHandler) mutable
{
using Server = Server<typename PacketConnector::Request, typename PacketConnector::Response, typename PacketConnector::Traits>;
auto handler = handlerFactory(*connection);
return std::make_unique<Server>(std::move(connection), std::move(handler), std::forward<decltype(closeHandler)>(closeHandler));
},
std::forward<TransactionArgs>(transactionArgs)...);
}
} // IPC

144
Inc/IPC/Connection.h Normal file
Просмотреть файл

@ -0,0 +1,144 @@
#pragma once
#include "ConnectionFwd.h"
#include "detail/ConnectionBase.h"
#include <memory>
#include <type_traits>
namespace IPC
{
struct DefaultTraits;
template <typename Input, typename Output, typename Traits>
class Connection : public detail::ConnectionBase
{
using ChannelHolder = detail::ChannelHolderOf<Input, Output, Traits>;
public:
template <typename... Channels>
Connection(
detail::KernelEvent localCloseEvent,
detail::KernelEvent remoteCloseEvent,
detail::KernelObject process,
typename Traits::WaitHandleFactory waitHandleFactory,
Channels&&... channels)
: Connection{
std::move(localCloseEvent),
false,
std::move(remoteCloseEvent),
std::move(process),
std::move(waitHandleFactory),
std::forward<Channels>(channels)... }
{}
template <typename... Channels>
Connection(
detail::KernelEvent localCloseEvent,
bool monitorLocalCloseEvent,
detail::KernelEvent remoteCloseEvent,
detail::KernelObject process,
typename Traits::WaitHandleFactory waitHandleFactory,
Channels&&... channels)
: Connection{
ChannelHolder{ std::forward<Channels>(channels)... },
std::move(localCloseEvent),
monitorLocalCloseEvent,
std::move(remoteCloseEvent),
std::move(process),
std::move(waitHandleFactory) }
{}
~Connection()
{
Close();
}
template <typename = std::enable_if_t<!std::is_void<Input>::value>>
auto GetInputChannel(const std::nothrow_t&)
{
return IsClosed() ? nullptr : &std::get<InputChannel<Input, Traits>>(m_channels);
}
template <typename = std::enable_if_t<!std::is_void<Input>::value>>
decltype(auto) GetInputChannel()
{
ThrowIfClosed();
return std::get<InputChannel<Input, Traits>>(m_channels);
}
template <typename = std::enable_if_t<!std::is_void<Input>::value>>
decltype(auto) GetInputChannel() const
{
ThrowIfClosed();
return std::get<InputChannel<Input, Traits>>(m_channels);
}
template <typename = std::enable_if_t<!std::is_void<Output>::value>>
auto GetOutputChannel(const std::nothrow_t&)
{
return IsClosed() ? nullptr : &std::get<OutputChannel<Output, Traits>>(m_channels);
}
template <typename = std::enable_if_t<!std::is_void<Output>::value>>
decltype(auto) GetOutputChannel()
{
ThrowIfClosed();
return std::get<OutputChannel<Output, Traits>>(m_channels);
}
template <typename = std::enable_if_t<!std::is_void<Output>::value>>
decltype(auto) GetOutputChannel() const
{
ThrowIfClosed();
return std::get<OutputChannel<Output, Traits>>(m_channels);
}
template <typename I = Input, std::enable_if_t<!std::is_void<I>::value>* = nullptr>
void Close()
{
std::get<InputChannel<Input, Traits>>(m_channels).UnregisterReceiver();
Close<void>();
}
template <typename I = Input, std::enable_if_t<std::is_void<I>::value>* = nullptr>
void Close()
{
m_localCloseMonitor = {};
m_remoteCloseMonitor = {};
m_processMonitor = {};
ConnectionBase::Close();
}
private:
Connection(
ChannelHolder&& channels,
detail::KernelEvent&& localCloseEvent,
bool monitorLocalCloseEvent,
detail::KernelEvent&& remoteCloseEvent,
detail::KernelObject&& process,
typename Traits::WaitHandleFactory&& waitHandleFactory)
: ConnectionBase{ localCloseEvent },
m_channels{ std::move(channels) },
m_waitHandleFactory{ std::move(waitHandleFactory) },
m_localCloseMonitor{
monitorLocalCloseEvent && localCloseEvent
? m_waitHandleFactory(std::move(localCloseEvent), [this] { CloseHandler(); return false; })
: WaitHandle{} },
m_remoteCloseMonitor{ m_waitHandleFactory(std::move(remoteCloseEvent), [this] { CloseHandler(); return false; }) },
m_processMonitor{ m_waitHandleFactory(std::move(process), [this] { CloseHandler(); return false; }) }
{}
using WaitHandle = decltype(std::declval<typename Traits::WaitHandleFactory>()(
std::declval<detail::KernelObject>(), std::declval<bool(*)()>()));
ChannelHolder m_channels;
typename Traits::WaitHandleFactory m_waitHandleFactory;
WaitHandle m_localCloseMonitor;
WaitHandle m_remoteCloseMonitor;
WaitHandle m_processMonitor;
};
} // IPC

11
Inc/IPC/ConnectionFwd.h Normal file
Просмотреть файл

@ -0,0 +1,11 @@
#pragma once
namespace IPC
{
struct DefaultTraits;
template <typename Input, typename Output, typename Traits = DefaultTraits>
class Connection;
} // IPC

432
Inc/IPC/Connector.h Normal file
Просмотреть файл

@ -0,0 +1,432 @@
#pragma once
#include "ConnectorFwd.h"
#include "Client.h"
#include "DefaultTraits.h"
#include "ComponentCollection.h"
#include "detail/ChannelFactory.h"
#include "detail/Info.h"
#include "detail/KernelProcess.h"
#include "detail/RandomString.h"
#include "detail/Apply.h"
#include <memory>
#include <string>
#include <map>
#include <type_traits>
#include <future>
namespace IPC
{
namespace detail
{
template <typename K, typename V, typename Comparer, typename Allocator>
struct IsStable<std::map<K, V, Comparer, Allocator>> : std::true_type
{};
template <typename K, typename V, typename Comparer, typename Allocator>
class ComponentCollectionInserter<std::map<K, V, Comparer, Allocator>>
{
public:
explicit ComponentCollectionInserter(std::map<K, V, Comparer, Allocator>& container)
: m_container{ &container }
{}
template <typename U>
auto operator()(U&& value)
{
auto result = m_container->insert(std::forward<U>(value));
return result.second ? result.first : m_container->end();
}
private:
std::map<K, V, Comparer, Allocator>* m_container;
};
} // detail
template <typename Input, typename Output, typename Traits>
class Connector
{
using ChannelFactory = detail::ChannelFactory<Traits>;
public:
using Connection = Connection<Input, Output, Traits>;
Connector()
: Connector{ {} }
{}
explicit Connector(ChannelSettings<Traits> channelSettings, typename Traits::TransactionManagerFactory transactionManagerFactory = {})
: m_transactionManagerFactory{ std::move(transactionManagerFactory) },
m_channelFactory{ std::move(channelSettings) }
{}
Connector(const Connector& other) = delete;
Connector& operator=(const Connector& other) = delete;
Connector(Connector&& other) = default;
Connector& operator=(Connector&& other) = default;
#ifdef NDEBUG
~Connector() = default;
#else
~Connector()
{
if (m_clients)
{
auto clients = m_clients.GetComponents();
for (auto& entry : *clients)
{
assert(entry.second.unique()); // No one must be holding an extra ref on clients.
}
}
}
#endif
template <typename Callback, typename... TransactionArgs, typename U = std::future<std::unique_ptr<Connection>>,
decltype(std::declval<Callback>()(std::declval<U>()))* = nullptr>
void Connect(const char* acceptorName, Callback&& callback, TransactionArgs&&... transactionArgs)
{
class GuaranteedCallback
{
public:
explicit GuaranteedCallback(Callback&& callback)
: m_callback{ std::forward<Callback>(callback) }
{}
~GuaranteedCallback()
{
if (!m_invoked)
{
operator()(std::make_exception_ptr(Exception{ "Acceptor is closed." }));
}
}
void operator()(std::exception_ptr error)
{
std::promise<std::unique_ptr<Connection>> promise;
promise.set_exception(error);
operator()(promise.get_future());
}
void operator()(std::future<std::unique_ptr<Connection>>&& connection)
{
m_invoked = true;
m_callback(std::move(connection));
}
private:
std::decay_t<Callback> m_callback;
bool m_invoked{ false };
};
auto guaranteedCallback = std::make_shared<GuaranteedCallback>(std::forward<Callback>(callback));
try
{
InvokeAcceptor(
BeginConnect(acceptorName),
[this, guaranteedCallback](auto&& state) mutable
{
std::packaged_task<std::unique_ptr<Connection>()> task{ [&] { return EndConnect(state); } };
auto result = task.get_future();
task();
(*guaranteedCallback)(std::move(result));
},
std::forward<TransactionArgs>(transactionArgs)...);
}
catch (...)
{
(*guaranteedCallback)(std::current_exception());
}
}
template <typename... TransactionArgs>
std::future<std::unique_ptr<Connection>> Connect(const char* acceptorName, TransactionArgs&&... transactionArgs)
{
using ConnectionPtr = std::unique_ptr<Connection>;
std::packaged_task<ConnectionPtr(std::future<ConnectionPtr>)> callback{ [](auto&& connection) { return connection.get(); } };
auto result = callback.get_future();
Connect(acceptorName, std::move(callback), std::forward<TransactionArgs>(transactionArgs)...);
return result;
}
private:
using Client = Client<detail::ConnectorInfo, detail::AcceptorInfo, Traits>;
class ConnectorClient : public Client
{
public:
ConnectorClient(
ChannelFactory channelFactory,
const detail::AcceptorHostInfo& acceptorHostInfo,
const detail::ConnectorPingInfo& pingInfo,
typename Traits::TransactionManagerFactory transactionManagerFactory,
detail::Callback<void()> closeHandler)
: ConnectorClient{
std::move(channelFactory),
CreateChannelsOrdered(pingInfo, channelFactory),
acceptorHostInfo,
pingInfo,
std::move(transactionManagerFactory),
std::move(closeHandler),
detail::KernelProcess{ acceptorHostInfo.m_processId } }
{}
const detail::KernelProcess& GetProcess() const
{
return m_process;
}
const auto& GetChannelFactory() const
{
return m_channelFactory;
}
private:
template <typename Channels>
ConnectorClient(
ChannelFactory&& channelFactory,
Channels&& channels,
const detail::AcceptorHostInfo& acceptorHostInfo,
const detail::ConnectorPingInfo& pingInfo,
typename Traits::TransactionManagerFactory&& transactionManagerFactory,
detail::Callback<void()>&& closeHandler,
detail::KernelProcess process)
: Client{
std::make_unique<Connection>(
detail::KernelEvent{ create_only, false, false, pingInfo.m_connectorCloseEventName.c_str() },
true,
detail::KernelEvent{ open_only, acceptorHostInfo.m_acceptorCloseEventName.c_str() },
process,
channelFactory.GetWaitHandleFactory(),
std::move(channels.first),
std::move(channels.second)),
std::move(closeHandler),
std::move(transactionManagerFactory)(detail::Identity<typename Client::TransactionManager>{}) },
m_process{ std::move(process) },
m_channelFactory{ std::move(channelFactory) }
{}
static auto CreateChannelsOrdered(const detail::ConnectorPingInfo& pingInfo, const ChannelFactory& channelFactory)
{
auto channelFactoryInstance = channelFactory.MakeInstance();
// Create in a strict order. Must be the reverse of the acceptor.
auto output = channelFactoryInstance.template CreateOutput<typename Client::OutputPacket>(pingInfo.m_connectorInfoChannelName.c_str());
auto input = channelFactoryInstance.template CreateInput<typename Client::InputPacket>(pingInfo.m_acceptorInfoChannelName.c_str());
return std::make_pair(std::move(input), std::move(output));
}
detail::KernelProcess m_process;
ChannelFactory m_channelFactory;
};
std::shared_ptr<ConnectorClient> TryCreateClient(const char* acceptorName)
{
auto acceptorInfoMemory = std::make_shared<SharedMemory>(open_only, acceptorName);
const auto& acceptorHostInfo = acceptorInfoMemory->Find<detail::AcceptorHostInfo>(unique_instance);
OutputChannel<detail::ConnectorPingInfo, Traits> pingChannel{ open_only, nullptr, acceptorInfoMemory };
auto pingInfo = MakePingInfo(pingChannel.GetMemory()->GetAllocator<char>());
std::shared_ptr<ConnectorClient> client;
auto result = m_clients.Accept(
[&](auto&& closeHandler)
{
client = std::make_shared<ConnectorClient>(
m_channelFactory.Override( // Input and output must be flipped.
acceptorHostInfo.m_settings.m_commonOutputMemoryName
? acceptorHostInfo.m_settings.m_commonOutputMemoryName->c_str()
: nullptr,
acceptorHostInfo.m_settings.m_commonInputMemoryName
? acceptorHostInfo.m_settings.m_commonInputMemoryName->c_str()
: nullptr),
acceptorHostInfo,
pingInfo,
m_transactionManagerFactory,
std::forward<decltype(closeHandler)>(closeHandler));
return std::make_pair(acceptorName, client);
});
if (result)
{
pingChannel.Send(pingInfo);
return client;
}
return{};
}
std::shared_ptr<ConnectorClient> TryGetClient(const char* acceptorName)
{
{
auto clients = m_clients.GetComponents();
auto it = clients->find(acceptorName);
if (it != clients->end())
{
return it->second;
}
}
return TryCreateClient(acceptorName);
}
detail::ConnectorPingInfo MakePingInfo(const SharedMemory::Allocator<char>& allocator)
{
detail::ConnectorPingInfo pingInfo{ allocator };
pingInfo.m_processId = detail::KernelProcess::GetCurrentProcessId();
pingInfo.m_connectorCloseEventName.assign(detail::GenerateRandomString().c_str());
pingInfo.m_acceptorInfoChannelName.assign(detail::GenerateRandomString().c_str());
pingInfo.m_connectorInfoChannelName.assign(detail::GenerateRandomString().c_str());
if (const auto& memory = m_channelFactory.GetCommonInput())
{
pingInfo.m_settings.m_commonInputMemoryName.emplace(memory->GetName().c_str(), allocator);
}
if (const auto& memory = m_channelFactory.GetCommonOutput())
{
pingInfo.m_settings.m_commonOutputMemoryName.emplace(memory->GetName().c_str(), allocator);
}
return pingInfo;
}
std::shared_ptr<ConnectorClient> GetClient(const char* acceptorName)
{
std::shared_ptr<ConnectorClient> client;
do
{
client = TryGetClient(acceptorName);
} while (!client);
return client;
}
template <typename O = Output, std::enable_if_t<std::is_void<O>::value>* = nullptr>
auto BeginConnect(const char* acceptorName)
{
auto client = GetClient(acceptorName);
auto memory = client->GetConnection().GetOutputChannel().GetMemory();
auto process = client->GetProcess();
auto channelFactoryInstance = client->GetChannelFactory().MakeInstance();
return std::make_tuple(
std::move(client),
std::move(process),
detail::ConnectorInfo{ memory->GetAllocator<char>() },
std::move(channelFactoryInstance));
}
template <typename O = Output, std::enable_if_t<!std::is_void<O>::value>* = nullptr>
auto BeginConnect(const char* acceptorName)
{
auto state = BeginConnect<void>(acceptorName);
auto& connectorInfo = std::get<detail::ConnectorInfo>(state);
connectorInfo.m_channelName.assign(detail::GenerateRandomString().c_str());
auto& transaction = std::get<typename ChannelFactory::Instance>(state);
return std::tuple_cat(std::move(state), std::make_tuple(transaction.template CreateOutput<Output>(connectorInfo.m_channelName.c_str())));
}
template <typename State, typename Callback, typename... TransactionArgs>
void InvokeAcceptor(State&& state, Callback&& callback, TransactionArgs&&... transactionArgs)
{
auto client = std::move(std::get<std::shared_ptr<ConnectorClient>>(state)); // Move the client out of the state!
auto connectorInfo = std::move(std::get<detail::ConnectorInfo>(state));
(*client)(
std::move(connectorInfo),
[state = std::forward<State>(state), callback = std::forward<Callback>(callback)](detail::AcceptorInfo&& acceptorInfo) mutable
{
callback(std::tuple_cat(std::forward<State>(state), std::make_tuple(std::move(acceptorInfo))));
},
std::forward<TransactionArgs>(transactionArgs)...);
}
template <typename State>
std::unique_ptr<Connection> EndConnect(State& state)
{
detail::KernelEvent closeEvent{ open_only, std::get<detail::AcceptorInfo>(state).m_closeEventName.c_str() };
try
{
return detail::ApplyTuple(
[&](auto&&... channels)
{
return std::make_unique<Connection>(
closeEvent,
closeEvent,
std::move(std::get<detail::KernelProcess>(state)),
std::get<typename ChannelFactory::Instance>(state).GetWaitHandleFactory(),
std::forward<decltype(channels)>(channels)...);
},
CollectChannels(state));
}
catch (...)
{
closeEvent.Signal();
throw;
}
}
template <typename I = Input, typename O = Output, typename State, std::enable_if_t<!std::is_void<I>::value && !std::is_void<O>::value>* = nullptr>
auto CollectChannels(State& state)
{
return std::tuple_cat(CollectChannels<Input, void>(state), CollectChannels<void, Output>(state));
}
template <typename I = Input, typename O = Output, typename State, std::enable_if_t<!std::is_void<I>::value && std::is_void<O>::value>* = nullptr>
auto CollectChannels(State& state)
{
return std::make_tuple(std::get<typename ChannelFactory::Instance>(state).template OpenInput<Input>(
std::get<detail::AcceptorInfo>(state).m_channelName.c_str()));
}
template <typename I = Input, typename O = Output, typename State, std::enable_if_t<std::is_void<I>::value && !std::is_void<O>::value>* = nullptr>
auto CollectChannels(State& state)
{
return std::make_tuple(std::move(std::get<OutputChannel<Output, Traits>>(state)));
}
typename Traits::TransactionManagerFactory m_transactionManagerFactory;
ChannelFactory m_channelFactory;
ComponentCollection<std::map<std::string, std::shared_ptr<ConnectorClient>>> m_clients; // Must be the last member.
};
template <typename PacketInfoT> // TODO: Remove the T suffix when VC14 bugs are fixed. Confuses with global PacketInfo struct.
class PacketConnector
: public Connector<typename PacketInfoT::InputPacket, typename PacketInfoT::OutputPacket, typename PacketInfoT::Traits>,
public PacketInfoT
{
public:
template <typename... Args>
PacketConnector(Args&&... args) // TODO: Inherit constructors instead when VC14 bugs are fixed.
: Connector{ std::forward<Args>(args)... }
{}
};
} // IPC

22
Inc/IPC/ConnectorFwd.h Normal file
Просмотреть файл

@ -0,0 +1,22 @@
#pragma once
#include "detail/PacketFwd.h"
namespace IPC
{
struct DefaultTraits;
template <typename Input, typename Output, typename Traits = DefaultTraits>
class Connector;
template <typename PacketInfoT>
class PacketConnector;
template <typename Request, typename Response, typename Traits = DefaultTraits>
using ClientConnector = PacketConnector<detail::ClientPacketInfo<Request, Response, Traits>>;
template <typename Request, typename Response, typename Traits = DefaultTraits>
using ServerConnector = PacketConnector<detail::ServerPacketInfo<Request, Response, Traits>>;
} // IPC

10
Inc/IPC/DefaultTraits.h Normal file
Просмотреть файл

@ -0,0 +1,10 @@
#pragma once
#include "DefaultTraitsFwd.h"
#include "detail/LockFree/Queue.h"
#include "Policies/WaitHandleFactory.h"
#include "Policies/ReceiverFactory.h"
#include "Policies/TransactionManager.h"
#include "Policies/TimeoutFactory.h"
#include "Policies/TransactionManagerFactory.h"
#include "Policies/ErrorHandler.h"

Просмотреть файл

@ -0,0 +1,57 @@
#pragma once
#include "detail/LockFree/QueueFwd.h"
#include "Policies/ReceiverFactoryFwd.h"
#include "Policies/TransactionManagerFwd.h"
#include <cstdint>
#include <cstddef>
namespace IPC
{
namespace detail
{
template <typename T>
struct Identity
{
using type = T;
};
} // detail
namespace Policies
{
class WaitHandleFactory;
class TimeoutFactory;
class TransactionManagerFactory;
class ErrorHandler;
} // Policies
struct DefaultTraits
{
template <typename T, typename Allocator>
using Queue = detail::LockFree::Queue<T, Allocator>;
using WaitHandleFactory = Policies::WaitHandleFactory;
using ReceiverFactory = Policies::ReceiverFactory;
using TimeoutFactory = Policies::TimeoutFactory;
template <typename Context>
using TransactionManager = Policies::TransactionManager<Context, TimeoutFactory>;
using TransactionManagerFactory = Policies::TransactionManagerFactory;
using PacketId = std::uint32_t;
using ErrorHandler = Policies::ErrorHandler;
};
} // IPC

14
Inc/IPC/Exception.h Normal file
Просмотреть файл

@ -0,0 +1,14 @@
#pragma once
#include <stdexcept>
namespace IPC
{
class Exception : public std::runtime_error
{
public:
using runtime_error::runtime_error;
};
} // IPC

104
Inc/IPC/InputChannel.h Normal file
Просмотреть файл

@ -0,0 +1,104 @@
#pragma once
#include "detail/ChannelBase.h"
#include <memory>
#include <stdexcept>
namespace IPC
{
struct DefaultTraits;
template <typename T, typename Traits = DefaultTraits>
class InputChannel : public detail::ChannelBase<T, Traits::template Queue>
{
using ChannelBase = detail::ChannelBase<T, Traits::template Queue>;
using WaitHandleFactory = typename Traits::WaitHandleFactory;
using ReceiverFactory = typename Traits::ReceiverFactory;
public:
InputChannel(create_only_t, const char* name, std::shared_ptr<SharedMemory> memory, WaitHandleFactory waitHandleFactory = {}, ReceiverFactory receiverFactory = {})
: InputChannel{ ChannelBase{ create_only, name, std::move(memory) }, std::move(waitHandleFactory), std::move(receiverFactory) }
{}
InputChannel(open_only_t, const char* name, std::shared_ptr<SharedMemory> memory, WaitHandleFactory waitHandleFactory = {}, ReceiverFactory receiverFactory = {})
: InputChannel{ ChannelBase{ open_only, name, std::move(memory) }, std::move(waitHandleFactory), std::move(receiverFactory) }
{}
template <typename Handler>
std::size_t ReceiveAll(Handler&& handler)
{
return this->IsEmpty() ? 0 : ConsumeAll([&] { return this->GetQueue().ConsumeAll(handler); });
}
template <typename Handler>
bool RegisterReceiver(Handler&& handler)
{
if (m_notEmptyMonitor)
{
return false;
}
m_notEmptyMonitor = m_waitHandleFactory(
this->GetNotEmptyEvent(),
[this, receiver = m_receiverFactory(this->GetQueue(), std::forward<Handler>(handler))]() mutable
{
if (!this->IsEmpty())
{
ConsumeAll(receiver);
}
return true;
});
return true;
}
bool UnregisterReceiver()
{
if (!m_notEmptyMonitor)
{
return false;
}
m_notEmptyMonitor = {};
return true;
}
private:
InputChannel(ChannelBase&& base, WaitHandleFactory&& waitHandleFactory, ReceiverFactory&& receiverFactory)
: ChannelBase{ std::move(base) },
m_waitHandleFactory{ std::move(waitHandleFactory) },
m_receiverFactory{ std::move(receiverFactory) }
{}
template <typename Receiver>
std::size_t ConsumeAll(Receiver&& receiver)
{
assert(!this->IsEmpty());
auto memory = this->GetMemory(); // Make sure memory does not die if receiver deletes this.
auto& counter = this->GetCounter();
std::size_t n, count{ 0 };
do
{
count += (n = receiver()); // Receiver may delete this.
} while ((counter -= n) != 0);
return count;
}
using WaitHandle = decltype(std::declval<WaitHandleFactory>()(
std::declval<detail::KernelObject>(), std::declval<bool(*)()>()));
WaitHandleFactory m_waitHandleFactory;
WaitHandle m_notEmptyMonitor;
ReceiverFactory m_receiverFactory;
};
} // IPC

Просмотреть файл

@ -0,0 +1,99 @@
#pragma once
#include "Transport.h"
#include "NativeObject.h"
#include "ComponentHolder.h"
#include "ManagedCallback.h"
#include "AccessorBase.h"
#pragma managed(push, off)
#include <boost/optional.hpp>
#pragma managed(pop)
#include <msclr/marshal.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename Request, typename Response>
ref class Transport<Request, Response>::ServersAccessor : AccessorBase<IServer^>, IServersAccessor
{
public:
ServersAccessor(NativeTransport& transport, System::String^ name, HandlerFactory^ handlerFactory)
try
: m_accessor{ transport.AcceptServers(
msclr::interop::marshal_context().marshal_as<const char*>(name),
MakeManagedCallback(gcnew ServerFactoryLambda{ handlerFactory, transport.GetConfig(), this }),
MakeErrorHander(this)) }
{}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
property System::Collections::Generic::IReadOnlyCollection<IServer^>^ Servers
{
virtual System::Collections::Generic::IReadOnlyCollection<IServer^>^ get()
{
auto servers = gcnew System::Collections::Generic::List<IServer^>{};
try
{
for (auto&& item : (*m_accessor)()())
{
auto holder = std::get_deleter<ComponentHolder<Server^>>(item);
assert(holder);
servers->Add(*holder);
}
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
return gcnew System::Collections::ObjectModel::ReadOnlyCollection<IServer^>{ servers };
}
}
internal:
ref class ServerFactoryLambda : ComponentFactoryLambdaBase<NativeServer, NativeConfig, typename Transport::Server, IServer>
{
public:
ServerFactoryLambda(HandlerFactory^ handlerFactory, const NativeConfig& config, ServersAccessor^ accessor)
: ComponentFactoryLambdaBase{ config, accessor },
m_handlerFactory{ handlerFactory }
{}
protected:
typename Transport::Server^ MakeComponent(
typename NativeServer::ConnectionPtr&& connection,
Interop::Callback<void()>&& closeHandler,
const NativeConfig& config) override
{
return gcnew Transport::Server{ std::move(connection), m_handlerFactory, std::move(closeHandler), config };
}
private:
HandlerFactory^ m_handlerFactory;
};
private:
NativeObject<Interop::Callback<Interop::Callback<const NativeServerCollection&()>()>> m_accessor;
};
template <typename Request, typename Response>
auto Transport<Request, Response>::AcceptServers(System::String^ name, HandlerFactory^ handlerFactory)
-> IServersAccessor^
{
return gcnew ServersAccessor{ *m_transport, name, handlerFactory };
}
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,112 @@
#pragma once
#include "NativeObject.h"
#include "ComponentHolder.h"
#include "ErrorHandler.h"
#include "Interop/Callback.h"
#include <msclr/auto_handle.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename T>
ref class AccessorBase : IAccessor<T>
{
public:
~AccessorBase()
{}
virtual event System::EventHandler<ComponentEventArgs<T>^>^ Connected;
virtual event System::EventHandler<ComponentEventArgs<T>^>^ Disconnected;
virtual event System::EventHandler<ErrorEventArgs^>^ Error;
protected:
AccessorBase()
{}
internal:
void TriggerConnected(T component)
{
Connected(this, gcnew ComponentEventArgs<T>{ component });
}
void TriggerDisconnected(T component)
{
Disconnected(this, gcnew ComponentEventArgs<T>{ component });
}
void TriggerError(System::Exception^ e)
{
Error(this, gcnew ErrorEventArgs{ e });
}
};
template <typename NativeComponent, typename NativeConfig, typename Component, typename Interface>
ref class ComponentFactoryLambdaBase abstract
{
public:
std::shared_ptr<void> operator()(typename NativeComponent::ConnectionPtr&& connection, Interop::Callback<void()>&& closeHandler)
{
msclr::auto_handle<Component> component;
try
{
component.reset(MakeComponent(std::move(connection), std::move(closeHandler), *m_config));
if (auto accessor = m_errorHandler.Component)
{
accessor->TriggerConnected(component.get());
}
component->Closed += gcnew System::EventHandler(this, &ComponentFactoryLambdaBase::operator());
return{ (void*)true, ComponentHolder<Component^>{ component.release() } };
}
catch (const std::exception& /*e*/)
{
m_errorHandler(std::current_exception());
return{};
}
catch (System::Exception^ e)
{
m_errorHandler(e);
return{};
}
}
void operator()(System::Object^ sender, System::EventArgs^ /*args*/)
{
if (auto accessor = m_errorHandler.Component)
{
accessor->TriggerDisconnected(safe_cast<Interface^>(sender));
}
}
protected:
ComponentFactoryLambdaBase(const NativeConfig& config, AccessorBase<Interface^>^ accessor)
: m_config{ config },
m_errorHandler{ accessor }
{}
virtual Component^ MakeComponent(
typename NativeComponent::ConnectionPtr&& connection,
Interop::Callback<void()>&& closeHandler,
const NativeConfig& config) = 0;
private:
NativeObject<NativeConfig> m_config;
ErrorHandler<AccessorBase<Interface^>> m_errorHandler;
};
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,30 @@
#pragma once
#include "Cast.h"
#pragma managed(push, off)
#include <type_traits>
#pragma managed(pop)
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename T>
struct Convert<T, std::enable_if_t<std::is_arithmetic<T>::value>>
{
using type = T;
static type From(T% from)
{
return{ from };
}
};
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,38 @@
#pragma once
#pragma managed(push, off)
#include <type_traits>
#pragma managed(pop)
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename T, typename Enable = void>
struct Convert
{
static_assert(!std::is_same<T, T>::value, "No conversion available for this type.");
};
template <typename T>
using CastType = typename Convert<T>::type;
template <typename T, typename = std::enable_if_t<!__is_class(T)>>
decltype(auto) Cast(T% value)
{
return Convert<CastType<T>>::From(value);
}
template <typename T, typename = std::enable_if_t<__is_class(T)>>
CastType<std::decay_t<T>> Cast(const T& value)
{
return Convert<CastType<std::decay_t<T>>>::From(value);
}
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,94 @@
#pragma once
#include "Transport.h"
#include "Component.h"
#include "ManagedCallback.h"
#include "Throw.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
#pragma warning(push)
#pragma warning(disable : 4564)
template <typename Request, typename Response>
ref class Transport<Request, Response>::Client : Component<NativeClient, IComponent>, IClient
{
public:
virtual System::Threading::Tasks::Task<Response>^ InvokeAsync(Request request, System::TimeSpan timeout)
{
auto promise = gcnew System::Threading::Tasks::TaskCompletionSource<Response>{};
try
{
try
{
(*m_component)(
Cast(request),
MakeManagedCallback(gcnew InvokeLambda{ promise }),
std::chrono::milliseconds{ static_cast<std::chrono::milliseconds::rep>(timeout.TotalMilliseconds) });
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
}
catch (System::Exception^ e)
{
promise->TrySetException(e);
}
return promise->Task;
}
internal:
Client(typename NativeClient::ConnectionPtr&& connection, const NativeConfig& config)
: Component{ std::move(connection), nullptr, *config }
{}
Client(typename NativeClient::ConnectionPtr&& connection, Interop::Callback<void()>&& closeHandler, const NativeConfig& config)
: Component{ std::move(connection), std::move(closeHandler), *config }
{}
ref struct InvokeLambda
{
InvokeLambda(System::Threading::Tasks::TaskCompletionSource<Response>^ promise)
: m_promise{ promise }
{}
~InvokeLambda()
{
m_promise->TrySetCanceled();
}
void operator()(NativeResponse&& response)
{
try
{
try
{
m_promise->SetResult(Cast(response));
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
}
catch (System::Exception^ e)
{
m_promise->SetException(e);
}
}
System::Threading::Tasks::TaskCompletionSource<Response>^ m_promise;
};
};
#pragma warning(pop)
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,102 @@
#pragma once
#include "Transport.h"
#include "Client.h"
#include "NativeObject.h"
#include "ManagedCallback.h"
#include <msclr/marshal.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
#pragma warning(push)
#pragma warning(disable : 4564)
template <typename Request, typename Response>
ref class Transport<Request, Response>::ClientConnector : IClientConnector
{
public:
virtual System::Threading::Tasks::Task<IClient^>^ ConnectAsync(System::String^ acceptorName, System::TimeSpan timeout)
{
auto promise = gcnew System::Threading::Tasks::TaskCompletionSource<IClient^>{};
try
{
try
{
m_connector->Connect(
msclr::interop::marshal_context().marshal_as<const char*>(acceptorName),
MakeManagedCallback(gcnew ConnectLambda{ promise, *m_config }),
std::chrono::milliseconds{ static_cast<std::chrono::milliseconds::rep>(timeout.TotalMilliseconds) });
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
}
catch (System::Exception^ e)
{
promise->TrySetException(e);
}
return promise->Task;
}
internal:
ClientConnector(const NativeConfig& config)
: m_connector{ *config },
m_config{ config }
{}
ref struct ConnectLambda
{
ConnectLambda(System::Threading::Tasks::TaskCompletionSource<IClient^>^ promise, const NativeConfig& config)
: m_promise{ promise },
m_config{ config }
{}
~ConnectLambda()
{
m_promise->TrySetCanceled();
}
void operator()(Interop::Callback<typename NativeClient::ConnectionPtr()>&& getConnection)
{
try
{
Client^ client = nullptr;
try
{
client = gcnew Client{ getConnection(), *m_config };
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
m_promise->SetResult(client);
}
catch (System::Exception^ e)
{
m_promise->SetException(e);
}
}
System::Threading::Tasks::TaskCompletionSource<IClient^>^ m_promise;
NativeObject<NativeConfig> m_config;
};
NativeObject<NativeClientConnector> m_connector;
NativeObject<NativeConfig> m_config;
};
#pragma warning(pop)
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,44 @@
#pragma once
#include "ManagedCallback.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename T>
ref class CloseHandler
{
public:
CloseHandler(T^ component)
: m_component{ component }
{}
void operator()()
{
T^ component = nullptr;
if (m_component.TryGetTarget(component))
{
component->TriggerClosed();
}
}
private:
System::WeakReference<T^> m_component;
};
template <typename T>
auto MakeCloseHander(T^ component)
{
return MakeManagedCallback(gcnew CloseHandler<T>{ component });
}
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,77 @@
#pragma once
#include "NativeObject.h"
#include "CloseHandler.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename T, typename IComponent>
ref class Component : IComponent
{
public:
property SharedMemory^ InputMemory
{
virtual SharedMemory^ get() { return %m_inputMemory; }
}
property SharedMemory^ OutputMemory
{
virtual SharedMemory^ get() { return %m_outputMemory; }
}
property System::Boolean IsClosed
{
virtual System::Boolean get() { return m_component->IsClosed(); }
}
virtual void Close()
{
m_component->Close();
}
virtual event System::EventHandler^ Closed;
internal:
void TriggerClosed()
{
Closed(this, System::EventArgs::Empty);
}
protected:
template <typename... Args>
Component(typename T::ConnectionPtr&& connection, std::nullptr_t, Args&&... args)
: Component{ std::move(connection), MakeCloseHander(this), false, std::forward<Args>(args)... }
{}
template <typename... Args>
Component(typename T::ConnectionPtr&& connection, Interop::Callback<void()>&& closeHandler, Args&&... args)
: Component{ std::move(connection), std::move(closeHandler), true, std::forward<Args>(args)... }
{}
SharedMemory m_inputMemory;
SharedMemory m_outputMemory;
NativeObject<T> m_component;
private:
template <typename... Args>
Component(typename T::ConnectionPtr&& connection, Interop::Callback<void()>&& closeHandler, bool registerClose, Args&&... args)
: m_inputMemory{ connection.GetInputMemory() },
m_outputMemory{ connection.GetOutputMemory() },
m_component{ std::move(connection), std::move(closeHandler), std::forward<Args>(args)... }
{
if (registerClose)
{
m_component->RegisterCloseHandler(MakeCloseHander(this));
}
}
};
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,65 @@
#pragma once
#include "NativeObject.h"
#include "ManagedCallback.h"
#pragma managed(push, off)
#include "IPC/Exception.h"
#include <memory>
#include <functional>
#pragma managed(pop)
#include <msclr/gcroot.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename T>
ref struct SelfDisposer : NativeObject<T>
{
explicit SelfDisposer(const T& obj)
: NativeObject{ obj }
{}
void operator()(void*)
{
delete this;
}
};
template <typename T>
class ComponentHolder : public std::function<void(void*)>
{
public:
ComponentHolder(T obj)
: ComponentHolder{ std::shared_ptr<msclr::gcroot<T>>{ new msclr::gcroot<T>{ obj } } }
{}
operator T()
{
if (auto obj = m_obj.lock())
{
return *obj;
}
throw IPC::Exception{ "Connection is not available." };
}
private:
ComponentHolder(const std::shared_ptr<msclr::gcroot<T>>& obj)
: function{ MakeManagedCallback(gcnew SelfDisposer<std::shared_ptr<msclr::gcroot<T>>>{ obj }) },
m_obj{ obj }
{}
std::weak_ptr<msclr::gcroot<T>> m_obj;
};
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,87 @@
#pragma once
#include "Transport.h"
#include "NativeObject.h"
#include "ComponentHolder.h"
#include "ManagedCallback.h"
#include "AccessorBase.h"
#include <msclr/marshal.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename Request, typename Response>
ref class Transport<Request, Response>::ClientAccessor : AccessorBase<IClient^>, IClientAccessor
{
public:
ClientAccessor(NativeTransport& transport, System::String^ acceptorName, System::Boolean async, System::TimeSpan timeout, IClientConnector^ connector)
try
: m_accessor{ transport.ConnectClient(
msclr::interop::marshal_context().marshal_as<const char*>(acceptorName),
*safe_cast<ClientConnector^>(connector)->m_connector,
async,
MakeManagedCallback(gcnew ClientFactoryLambda{ transport.GetConfig(), this }),
MakeErrorHander(this),
std::chrono::milliseconds{ static_cast<std::size_t>(timeout.TotalMilliseconds) }) }
{}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
property IClient^ Client
{
virtual IClient^ get()
{
try
{
auto holder = std::get_deleter<ComponentHolder<Transport::Client^>>((*m_accessor)());
assert(holder);
return *holder;
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
}
}
internal:
ref class ClientFactoryLambda : ComponentFactoryLambdaBase<NativeClient, NativeConfig, typename Transport::Client, IClient>
{
public:
ClientFactoryLambda(const NativeConfig& config, ClientAccessor^ accessor)
: ComponentFactoryLambdaBase{ config, accessor }
{}
protected:
typename Transport::Client^ MakeComponent(
typename NativeClient::ConnectionPtr&& connection,
Interop::Callback<void()>&& closeHandler,
const NativeConfig& config) override
{
return gcnew Transport::Client{ std::move(connection), std::move(closeHandler), config };
}
};
private:
NativeObject<Interop::Callback<std::shared_ptr<void>()>> m_accessor;
};
template <typename Request, typename Response>
auto Transport<Request, Response>::ConnectClient(System::String^ acceptorName, System::Boolean async, System::TimeSpan timeout, IClientConnector^ connector)
-> IClientAccessor^
{
return gcnew ClientAccessor{ *m_transport, acceptorName, async, timeout, connector ? connector : m_clientConnector.Value };
}
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,64 @@
#pragma once
#include "ManagedCallback.h"
#include "Throw.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename T>
ref class ErrorHandler
{
public:
ErrorHandler(T^ component)
: m_component{ component }
{}
void operator()(std::exception_ptr&& error)
{
try
{
ThrowManagedException(error);
}
catch (System::Exception^ e)
{
operator()(e);
}
}
void operator()(System::Exception^ e)
{
if (auto component = this->Component)
{
component->TriggerError(e);
}
}
property T^ Component
{
T^ get()
{
T^ component;
return m_component.TryGetTarget(component) ? component : nullptr;
}
}
private:
System::WeakReference<T^> m_component;
};
template <typename T>
auto MakeErrorHander(T^ component)
{
return MakeManagedCallback(gcnew ErrorHandler<T>{ component });
}
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,7 @@
#pragma once
#ifdef IPC_MANAGED_EXPORT
#define IPC_MANAGED_DLL __declspec(dllexport)
#else
#define IPC_MANAGED_DLL __declspec(dllimport)
#endif

Просмотреть файл

@ -0,0 +1,48 @@
#pragma once
#include "Cast.h"
#pragma managed(push, off)
#include <guiddef.h>
#include <memory>
#pragma managed(pop)
namespace IPC
{
namespace Managed
{
namespace detail
{
template <>
struct Convert<::GUID>
{
using type = System::Guid;
static ::GUID From(type% from)
{
::GUID guid;
{
pin_ptr<unsigned char> bytes = &from.ToByteArray()[0];
std::memcpy(&guid, bytes, sizeof(guid));
}
return guid;
}
};
template <>
struct Convert<System::Guid>
{
using type = ::GUID;
static System::Guid From(const type& from)
{
return System::Guid{ from.Data1, from.Data2, from.Data3, from.Data4[0], from.Data4[1], from.Data4[2], from.Data4[3], from.Data4[4], from.Data4[5], from.Data4[6], from.Data4[7] };
}
};
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,28 @@
#pragma once
#ifdef __cplusplus_cli
#pragma managed(push, off)
#endif
#include "IPC/detail/Callback.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
using IPC::detail::Callback;
} // Interop
} // detail
} // Managed
} // IPC
#ifdef __cplusplus_cli
#pragma managed(pop)
#endif

Просмотреть файл

@ -0,0 +1,47 @@
#pragma once
#include "Transport.h"
#include "IPC/Connector.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
template <typename Request, typename Response>
Transport<Request, Response>::ClientConnector::ClientConnector(const Config& config)
: shared_ptr{ std::make_shared<IPC::ClientConnector<Request, Response, Traits>>(
config.m_channelSettings,
Policies::TransactionManagerFactory{ config.m_timeoutFactory, config.m_defaultRequestTimeout }) }
{}
template <typename Request, typename Response>
Transport<Request, Response>::ClientConnector::~ClientConnector() = default;
template <typename Request, typename Response>
void Transport<Request, Response>::ClientConnector::Connect(const char* acceptorName, HandlerFactory&& handlerFactory, const std::chrono::milliseconds& timeout)
{
auto handler = [handlerFactory = std::move(handlerFactory)](auto&& futureConnection) mutable
{
handlerFactory([futureConnection = std::move(futureConnection)]() mutable { return futureConnection.get(); });
};
if (timeout == std::chrono::milliseconds::zero())
{
this->get()->Connect(acceptorName, std::move(handler));
}
else
{
this->get()->Connect(acceptorName, std::move(handler), timeout);
}
}
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,93 @@
#pragma once
#include "Transport.h"
#include "IPC/Client.h"
#include <chrono>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
template <typename Request, typename Response>
Transport<Request, Response>::Client::ConnectionPtr::ConnectionPtr(ConnectionPtr&& other) = default;
template <typename Request, typename Response>
Transport<Request, Response>::Client::ConnectionPtr::~ConnectionPtr() = default;
template <typename Request, typename Response>
SharedMemory Transport<Request, Response>::Client::ConnectionPtr::GetInputMemory() const
{
return this->get()->GetInputChannel().GetMemory();
}
template <typename Request, typename Response>
SharedMemory Transport<Request, Response>::Client::ConnectionPtr::GetOutputMemory() const
{
return this->get()->GetOutputChannel().GetMemory();
}
template <typename Request, typename Response>
Transport<Request, Response>::Client::Client(ConnectionPtr&& connection, CloseHandler&& closeHandler, const Config& config)
: unique_ptr{ std::make_unique<IPC::Client<Request, Response, Traits>>(
std::move(connection),
std::move(closeHandler),
typename IPC::Client<Request, Response, Traits>::TransactionManager{ config.m_timeoutFactory, config.m_defaultRequestTimeout }) }
{}
template <typename Request, typename Response>
Transport<Request, Response>::Client::~Client() = default;
template <typename Request, typename Response>
void Transport<Request, Response>::Client::operator()(const Request& request, Callback<void(Response&&)>&& callback, const std::chrono::milliseconds& timeout)
{
if (timeout == std::chrono::milliseconds::zero())
{
this->get()->operator()(request, std::move(callback));
}
else
{
this->get()->operator()(request, std::move(callback), timeout);
}
}
template <typename Request, typename Response>
bool Transport<Request, Response>::Client::IsClosed() const
{
return this->get()->GetConnection().IsClosed();
}
template <typename Request, typename Response>
void Transport<Request, Response>::Client::Close()
{
this->get()->GetConnection().Close();
}
template <typename Request, typename Response>
void Transport<Request, Response>::Client::RegisterCloseHandler(CloseHandler&& closeHandler)
{
this->get()->GetConnection().RegisterCloseHandler(std::move(closeHandler), true);
}
template <typename Request, typename Response>
SharedMemory Transport<Request, Response>::Client::GetInputMemory() const
{
return this->get()->GetConnection().GetInputChannel().GetMemory();
}
template <typename Request, typename Response>
SharedMemory Transport<Request, Response>::Client::GetOutputMemory() const
{
return this->get()->GetConnection().GetOutputChannel().GetMemory();
}
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,25 @@
#pragma once
#pragma managed(push, off)
#include <type_traits>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
template <typename T>
struct ExternalConstructor : std::false_type
{};
} // Interop
} // detail
} // Managed
} // IPC
#pragma managed(pop)

Просмотреть файл

@ -0,0 +1,52 @@
#pragma once
#include "IPC/Managed/detail/Interop/Callback.h"
#include <functional>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
namespace Policies
{
class ReceiverFactory
{
using WorkItemFunc = Callback<void()>;
using QueueWorkItemFunc = Callback<void(WorkItemFunc&&)>;
using QueueWorkItemFactoryFunc = std::function<QueueWorkItemFunc()>;
public:
ReceiverFactory(const QueueWorkItemFactoryFunc& queueWorkItemFactory);
template <typename Queue, typename Handler>
auto operator()(Queue& queue, Handler&& handler) const
{
return [queueWorkItem = m_queueWorkItemFactory(), &queue, handler = std::forward<Handler>(handler)]() mutable
{
queueWorkItem(
[&queue, &handler]
{
auto value = queue.Pop();
assert(value);
handler(std::move(*value));
});
return 1;
};
}
private:
QueueWorkItemFactoryFunc m_queueWorkItemFactory;
};
} // Policies
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,63 @@
#pragma once
#include "IPC/Managed/detail/Interop/Callback.h"
#include <functional>
#include <memory>
#include <chrono>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
namespace Policies
{
class TimeoutFactory
{
using TimeoutHandle = std::shared_ptr<void>;
using TimeoutChangeFunc = std::function<void(const std::chrono::milliseconds&)>;
using TimeoutFactoryFunc = std::function<std::pair<TimeoutHandle, TimeoutChangeFunc>(Callback<void()>&&)>;
class Scheduler
{
public:
Scheduler(Callback<void()> handler, const TimeoutFactoryFunc& timeoutFatory, const std::chrono::milliseconds& defaultTimeout);
/// Schedules a timeout to fire after predefined amount of time configured in TimeoutFactory.
/// Previously scheduled timeout is deactivated first.
void operator()();
/// Schedules a timeout to fire after specified amount of time.
/// Previously scheduled timeout is deactivated first.
void operator()(const std::chrono::milliseconds& timeout);
/// Deactivates already scheduled timeout.
/// Blocks until the running handler (if any) returns.
void operator()(std::nullptr_t);
private:
std::shared_ptr<Callback<void()>> m_handler;
TimeoutFactoryFunc m_timeoutFatory;
TimeoutHandle m_timeoutHandle;
std::chrono::milliseconds m_defaultTimeout;
};
public:
TimeoutFactory(const std::chrono::milliseconds& defaultTimeout, const TimeoutFactoryFunc& timeoutFatory);
Scheduler operator()(Callback<void()> handler) const;
private:
std::chrono::milliseconds m_defaultTimeout;
TimeoutFactoryFunc m_timeoutFatory;
};
} // Policies
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,37 @@
#pragma once
#include "TimeoutFactory.h"
#include <IPC/DefaultTraitsFwd.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
namespace Policies
{
class TransactionManagerFactory
{
public:
TransactionManagerFactory(TimeoutFactory timeoutFactory, const std::chrono::milliseconds& defaultRequestTimeout);
template <typename T>
auto operator()(IPC::detail::Identity<IPC::Policies::TransactionManager<T, TimeoutFactory>>) const
{
return IPC::Policies::TransactionManager<T, TimeoutFactory>{ m_timeoutFactory, m_defaultRequestTimeout };
}
private:
TimeoutFactory m_timeoutFactory;
std::chrono::milliseconds m_defaultRequestTimeout;
};
} // Policies
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,26 @@
#pragma once
#include "IPC/detail/KernelObject.h"
#include "IPC/Managed/detail/Interop/Callback.h"
#include <functional>
#include <memory>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
namespace Policies
{
using WaitHandleFactory = std::function<std::shared_ptr<void>(IPC::detail::KernelObject, Callback<bool()>)>;
} // Policies
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,33 @@
#pragma once
#include "Transport.h"
#include "IPC/Acceptor.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
template <typename Request, typename Response>
Transport<Request, Response>::ServerAcceptor::ServerAcceptor(const char* name, HandlerFactory&& handlerFactory, const Config& config)
: unique_ptr{ std::make_unique<IPC::ServerAcceptor<Request, Response, Traits>>(
name,
[handlerFactory = std::move(handlerFactory)](auto&& futureConnection) mutable
{
handlerFactory([futureConnection = std::move(futureConnection)]() mutable { return futureConnection.get(); });
},
config.m_channelSettings) }
{}
template <typename Request, typename Response>
Transport<Request, Response>::ServerAcceptor::~ServerAcceptor() = default;
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,76 @@
#pragma once
#include "Transport.h"
#include "IPC/Server.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
template <typename Request, typename Response>
Transport<Request, Response>::Server::ConnectionPtr::ConnectionPtr(ConnectionPtr&& other) = default;
template <typename Request, typename Response>
Transport<Request, Response>::Server::ConnectionPtr::~ConnectionPtr() = default;
template <typename Request, typename Response>
SharedMemory Transport<Request, Response>::Server::ConnectionPtr::GetInputMemory() const
{
return this->get()->GetInputChannel().GetMemory();
}
template <typename Request, typename Response>
SharedMemory Transport<Request, Response>::Server::ConnectionPtr::GetOutputMemory() const
{
return this->get()->GetOutputChannel().GetMemory();
}
template <typename Request, typename Response>
Transport<Request, Response>::Server::Server(ConnectionPtr&& connection, CloseHandler&& closeHandler, Handler&& handler, const Config& /*config*/)
: unique_ptr{ std::make_unique<IPC::Server<Request, Response, Traits>>(std::move(connection), std::move(handler), std::move(closeHandler)) }
{}
template <typename Request, typename Response>
Transport<Request, Response>::Server::~Server() = default;
template <typename Request, typename Response>
bool Transport<Request, Response>::Server::IsClosed() const
{
return this->get()->GetConnection().IsClosed();
}
template <typename Request, typename Response>
void Transport<Request, Response>::Server::Close()
{
this->get()->GetConnection().Close();
}
template <typename Request, typename Response>
void Transport<Request, Response>::Server::RegisterCloseHandler(CloseHandler&& closeHandler)
{
this->get()->GetConnection().RegisterCloseHandler(std::move(closeHandler), true);
}
template <typename Request, typename Response>
SharedMemory Transport<Request, Response>::Server::GetInputMemory() const
{
return this->get()->GetConnection().GetInputChannel().GetMemory();
}
template <typename Request, typename Response>
SharedMemory Transport<Request, Response>::Server::GetOutputMemory() const
{
return this->get()->GetConnection().GetOutputChannel().GetMemory();
}
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,42 @@
#pragma once
#pragma managed(push, off)
#include <memory>
namespace IPC
{
class SharedMemory;
namespace Managed
{
namespace detail
{
namespace Interop
{
class SharedMemory : public std::shared_ptr<IPC::SharedMemory>
{
public:
static SharedMemory Create(const char* name, std::size_t size);
static SharedMemory Open(const char* name);
SharedMemory(const std::shared_ptr<IPC::SharedMemory>& memory);
SharedMemory(std::shared_ptr<IPC::SharedMemory>&& memory);
~SharedMemory();
const char* GetName() const;
std::size_t GetFreeSize() const;
};
} // Interop
} // detail
} // Managed
} // IPC
#pragma managed(pop)

Просмотреть файл

@ -0,0 +1,192 @@
#pragma once
#pragma managed(push, off)
#include "IPC/ClientFwd.h"
#include "IPC/ServerFwd.h"
#include "IPC/AcceptorFwd.h"
#include "IPC/ConnectorFwd.h"
#include "Callback.h"
#include "SharedMemory.h"
#include "Policies/ReceiverFactory.h"
#include "Policies/TimeoutFactory.h"
#include "Policies/WaitHandleFactory.h"
#include "Policies/TransactionManagerFactory.h"
#include <memory>
#include <list>
#include <chrono>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
struct Traits : IPC::DefaultTraits
{
using WaitHandleFactory = Interop::Policies::WaitHandleFactory;
using ReceiverFactory = Interop::Policies::ReceiverFactory;
using TimeoutFactory = Interop::Policies::TimeoutFactory;
template <typename Context>
using TransactionManager = IPC::Policies::TransactionManager<Context, TimeoutFactory>;
using TransactionManagerFactory = Interop::Policies::TransactionManagerFactory;
};
struct Config;
template <typename Request, typename Response>
class Transport
{
public:
using CloseHandler = Callback<void()>;
class Server : public std::unique_ptr<IPC::Server<Request, Response, Traits>>
{
public:
struct ConnectionPtr : std::unique_ptr<IPC::ServerConnection<Request, Response, Traits>>
{
using unique_ptr::unique_ptr;
ConnectionPtr(ConnectionPtr&& other);
~ConnectionPtr();
SharedMemory GetInputMemory() const;
SharedMemory GetOutputMemory() const;
};
using Handler = Callback<void(Request&&, Callback<void(const Response&)>&&)>;
Server(ConnectionPtr&& connection, CloseHandler&& closeHandler, Handler&& handler, const Config& config);
~Server();
bool IsClosed() const;
void Close();
void RegisterCloseHandler(CloseHandler&& closeHandler);
SharedMemory GetInputMemory() const;
SharedMemory GetOutputMemory() const;
};
class Client : public std::unique_ptr<IPC::Client<Request, Response, Traits>>
{
public:
struct ConnectionPtr : std::unique_ptr<IPC::ClientConnection<Request, Response, Traits>>
{
using unique_ptr::unique_ptr;
ConnectionPtr(ConnectionPtr&& other);
~ConnectionPtr();
SharedMemory GetInputMemory() const;
SharedMemory GetOutputMemory() const;
};
Client(ConnectionPtr&& connection, CloseHandler&& closeHandler, const Config& config);
~Client();
void operator()(const Request& request, Callback<void(Response&&)>&& callback, const std::chrono::milliseconds& timeout);
bool IsClosed() const;
void Close();
void RegisterCloseHandler(CloseHandler&& closeHandler);
SharedMemory GetInputMemory() const;
SharedMemory GetOutputMemory() const;
};
class ServerAcceptor : public std::unique_ptr<IPC::ServerAcceptor<Request, Response, Traits>>
{
using ConnectionPtr = typename Server::ConnectionPtr;
public:
using Handler = Callback<ConnectionPtr()>;
using HandlerFactory = Callback<void(Handler&&)>;
explicit ServerAcceptor(const char* name, HandlerFactory&& callback, const Config& config);
~ServerAcceptor();
};
class ClientConnector : public std::shared_ptr<IPC::ClientConnector<Request, Response, Traits>>
{
using ConnectionPtr = typename Client::ConnectionPtr;
public:
using Handler = Callback<ConnectionPtr()>;
using HandlerFactory = Callback<void(Handler&&)>;
explicit ClientConnector(const Config& config);
~ClientConnector();
void Connect(const char* acceptorName, HandlerFactory&& handlerFactory, const std::chrono::milliseconds& timeout);
};
using ErrorHandler = Callback<void(std::exception_ptr)>;
template <typename T>
using ComponentFactory = Callback<std::shared_ptr<void>(typename T::ConnectionPtr&&, CloseHandler&&)>;
using ServerCollection = std::list<std::shared_ptr<void>>;
Transport(
std::size_t outputMemorySize,
const std::chrono::milliseconds& defaultRequestTimeout,
Traits::ReceiverFactory receiverFactory,
Traits::WaitHandleFactory waitHandleFactory,
Traits::TimeoutFactory timeoutFactory);
~Transport();
const std::shared_ptr<const Config>& GetConfig() const;
Callback<std::shared_ptr<void>()> ConnectClient(
const char* acceptorName,
const ClientConnector& connector,
bool async,
ComponentFactory<Client>&& componentFactory,
ErrorHandler&& errorHandler,
const std::chrono::milliseconds& timeout);
Callback<Callback<const ServerCollection&()>()> AcceptServers(
const char* name,
ComponentFactory<Server>&& componentFactory,
ErrorHandler&& errorHandler);
private:
std::shared_ptr<const Config> m_config;
};
} // Interop
} // detail
} // Managed
} // IPC
#pragma managed(pop)

Просмотреть файл

@ -0,0 +1,121 @@
#pragma once
#include "IPC/Connect.h"
#include "IPC/Accept.h"
#include "Transport.h"
#include "ClientImpl.h"
#include "ServerImpl.h"
#include "ClientConnectorImpl.h"
#include "ServerAcceptorImpl.h"
#include <chrono>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Interop
{
struct Config
{
Config(
ChannelSettings<Traits> channelSettings,
const std::chrono::milliseconds& defaultRequestTimeout,
Traits::TimeoutFactory timeoutFactory)
: m_channelSettings{ std::move(channelSettings) },
m_defaultRequestTimeout{ defaultRequestTimeout },
m_timeoutFactory{ std::move(timeoutFactory) }
{}
ChannelSettings<Traits> m_channelSettings;
std::chrono::milliseconds m_defaultRequestTimeout;
Traits::TimeoutFactory m_timeoutFactory;
};
auto MakeChannelSettings(std::size_t memorySize, Traits::ReceiverFactory receiverFactory, Traits::WaitHandleFactory waitHandleFactory)
{
ChannelSettings<Traits> settings{ std::move(waitHandleFactory), std::move(receiverFactory) };
if (memorySize != 0)
{
settings.SetInput(memorySize, true);
settings.SetOutput(memorySize, true);
}
return settings;
}
template <typename Request, typename Response>
Transport<Request, Response>::Transport(
std::size_t outputMemorySize,
const std::chrono::milliseconds& defaultRequestTimeout,
Traits::ReceiverFactory receiverFactory,
Traits::WaitHandleFactory waitHandleFactory,
Traits::TimeoutFactory timeoutFactory)
: m_config{ std::make_shared<Config>(
MakeChannelSettings(outputMemorySize, std::move(receiverFactory), std::move(waitHandleFactory)),
defaultRequestTimeout,
std::move(timeoutFactory)) }
{}
template <typename Request, typename Response>
Transport<Request, Response>::~Transport() = default;
template <typename Request, typename Response>
const std::shared_ptr<const Config>& Transport<Request, Response>::GetConfig() const
{
return m_config;
}
template <typename Request, typename Response>
Callback<std::shared_ptr<void>()> Transport<Request, Response>::ConnectClient(
const char* acceptorName,
const ClientConnector& connector,
bool async,
ComponentFactory<Client>&& componentFactory,
ErrorHandler&& errorHandler,
const std::chrono::milliseconds& timeout)
{
return IPC::detail::Connect(
acceptorName,
connector,
async,
m_config->m_timeoutFactory,
std::move(errorHandler),
std::move(componentFactory),
timeout);
}
template <typename Request, typename Response>
auto Transport<Request, Response>::AcceptServers(
const char* name,
ComponentFactory<Server>&& componentFactory,
ErrorHandler&& errorHandler)
-> Callback<Callback<const ServerCollection&()>()>
{
auto accessor = IPC::detail::Accept<IPC::ServerAcceptor<Request, Response, Traits>>(
name,
std::make_shared<IPC::ComponentCollection<ServerCollection>>(),
std::move(componentFactory),
std::move(errorHandler),
m_config->m_channelSettings);
return [accessor = std::move(accessor)]() mutable
{
return [servers = accessor()]() -> const ServerCollection& { return *servers; };
};
}
} // Interop
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,29 @@
#pragma once
#include <msclr/gcroot.h> // Needed to prevent vcclr.h to include the root gcroot.h without namespace.
#include <msclr/auto_gcroot.h>
#pragma managed(push, off)
#include <memory>
#pragma managed(pop)
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename Lambda>
auto MakeManagedCallback(Lambda^ lambda)
{
return [lambda = std::shared_ptr<msclr::auto_gcroot<Lambda^>>{ new msclr::auto_gcroot<Lambda^>{ lambda } }](auto&&... args)
{
return lambda->get()->operator()(std::forward<decltype(args)>(args)...);
};
}
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,105 @@
#pragma once
#include "Interop/ExternalConstructor.h"
#include "Throw.h"
#pragma managed(push, off)
#include <typeinfo>
#include <type_traits>
#pragma managed(pop)
namespace IPC
{
namespace Managed
{
namespace detail
{
#pragma managed(push, off)
template <typename T, typename... Args, std::enable_if_t<Interop::ExternalConstructor<T>::value>* = nullptr>
T* New(Args&&... args)
{
return Interop::ExternalConstructor<T>::New(std::forward<Args>(args)...);
}
template <typename T, typename... Args, std::enable_if_t<!Interop::ExternalConstructor<T>::value>* = nullptr>
T* New(Args&&... args)
{
return new T(std::forward<Args>(args)...);
}
template <typename T, std::enable_if_t<Interop::ExternalConstructor<T>::value>* = nullptr>
void Delete(T* obj)
{
Interop::ExternalConstructor<T>::Delete(obj);
}
template <typename T, std::enable_if_t<!Interop::ExternalConstructor<T>::value>* = nullptr>
void Delete(T* obj)
{
delete obj;
}
#pragma managed(pop)
template <typename T>
ref class NativeObject
{
public:
template <typename... Args>
explicit NativeObject(Args&&... args)
try
: m_obj{ New<T>(std::forward<Args>(args)...) }
{}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
~NativeObject()
{
this->!NativeObject();
}
!NativeObject()
{
Delete(m_obj);
m_obj = nullptr;
}
explicit operator bool()
{
return m_obj != nullptr;
}
static T& operator*(NativeObject% obj)
{
return obj.Object;
}
static T* operator->(NativeObject% obj)
{
return &obj.Object;
}
private:
property T& Object
{
T& get()
{
if (!m_obj)
{
throw gcnew System::ObjectDisposedException{ gcnew System::String{ typeid(T).name() } };
}
return *m_obj;
}
}
T* m_obj;
};
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,24 @@
#pragma once
#pragma managed(push, off)
#include "IPC/Managed/detail/Interop/Policies/ReceiverFactory.h"
#pragma managed(on)
#include "IPC/Managed/detail/Export.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Policies
{
IPC_MANAGED_DLL Interop::Policies::ReceiverFactory MakeReceiverFactory();
} // Policies
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,24 @@
#pragma once
#pragma managed(push, off)
#include "IPC/Managed/detail/Interop/Policies/TimeoutFactory.h"
#pragma managed(on)
#include "IPC/Managed/detail/Export.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Policies
{
IPC_MANAGED_DLL Interop::Policies::TimeoutFactory MakeTimeoutFactory(const std::chrono::milliseconds& timeout);
} // Policies
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,24 @@
#pragma once
#pragma managed(push, off)
#include "IPC/Managed/detail/Interop/Policies/WaitHandleFactory.h"
#pragma managed(on)
#include "IPC/Managed/detail/Export.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace Policies
{
IPC_MANAGED_DLL Interop::Policies::WaitHandleFactory MakeWaitHandleFactory();
} // Policies
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,139 @@
#pragma once
#include "Transport.h"
#include "Component.h"
#include <msclr/auto_handle.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
namespace
{
template <typename T>
msclr::auto_handle<T> MakeAutoHandle(T^ obj)
{
return msclr::auto_handle<T>{ obj };
}
template <typename T>
T% MakeAutoHandle(T% obj)
{
return obj;
}
} // anonymous
template <typename Request, typename Response>
ref class Transport<Request, Response>::Server : Component<NativeServer, IComponent>, IServer
{
public:
virtual event System::EventHandler<ErrorEventArgs^>^ Error;
internal:
Server(typename NativeServer::ConnectionPtr&& connection, HandlerFactory^ handlerFactory, const NativeConfig& config)
: Component{ std::move(connection), nullptr, MakeHandlerFactory(connection, handlerFactory), *config }
{}
Server(typename NativeServer::ConnectionPtr&& connection, HandlerFactory^ handlerFactory, Interop::Callback<void()>&& closeHandler, const NativeConfig& config)
: Component{ std::move(connection), std::move(closeHandler), MakeHandlerFactory(connection, handlerFactory), *config }
{}
~Server()
{
m_pendingTasks->Signal();
m_pendingTasks->Wait();
}
ref struct HandlerLambda
{
HandlerLambda(Handler^ handler, Server^ server)
: m_handler{ handler },
m_server{ server }
{}
void operator()(NativeRequest&& request, Interop::Callback<void(const NativeResponse&)>&& callback)
{
Server^ server = nullptr;
if (m_server.TryGetTarget(server) && server->m_pendingTasks->TryAddCount())
{
try
{
m_handler(Cast(request))->ContinueWith(
gcnew System::Action<System::Threading::Tasks::Task<Response>^>(
gcnew ResponseLambda{ std::move(callback), server }, &ResponseLambda::operator()),
System::Threading::Tasks::TaskContinuationOptions::ExecuteSynchronously);
}
catch (System::Exception^ e)
{
server->m_pendingTasks->Signal();
server->Error(server, gcnew ErrorEventArgs{ e });
}
}
}
Handler^ m_handler;
System::WeakReference<Server^> m_server;
};
ref struct ResponseLambda
{
ResponseLambda(Interop::Callback<void(const NativeResponse&)>&& callback, Server^ server)
: m_callback{ std::move(callback) },
m_server{ server }
{}
~ResponseLambda()
{
m_server->m_pendingTasks->Signal();
}
void operator()(System::Threading::Tasks::Task<Response>^ task)
{
msclr::auto_handle<ResponseLambda> selfDisposer{ this };
try
{
auto response = task->Result;
auto responseDisposer = MakeAutoHandle(response);
(void)responseDisposer;
try
{
(*m_callback)(Cast(response));
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
}
catch (System::Exception^ e)
{
m_server->Error(m_server, gcnew ErrorEventArgs{ e });
}
}
NativeObject<Interop::Callback<void(const NativeResponse&)>> m_callback;
Server^ m_server;
};
private:
auto MakeHandlerFactory(const typename NativeServer::ConnectionPtr& connection, HandlerFactory^ handlerFactory)
{
return MakeManagedCallback(gcnew HandlerLambda{
handlerFactory(gcnew SharedMemory{ connection.GetInputMemory() }, gcnew SharedMemory{ connection.GetOutputMemory() }),
this });
}
System::Threading::CountdownEvent^ m_pendingTasks{ gcnew System::Threading::CountdownEvent{ 1 } };
};
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,76 @@
#pragma once
#include "Transport.h"
#include "Server.h"
#include "NativeObject.h"
#include "ManagedCallback.h"
#include <msclr/marshal.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename Request, typename Response>
ref class Transport<Request, Response>::ServerAcceptor : IServerAcceptor
{
public:
ServerAcceptor(System::String^ name, HandlerFactory^ handlerFactory, const NativeConfig& config)
: m_acceptor{
msclr::interop::marshal_context().marshal_as<const char*>(name),
MakeManagedCallback(gcnew AcceptedLambda{ this, config }),
*config },
m_handlerFactory{ handlerFactory }
{}
virtual event System::EventHandler<ComponentEventArgs<IServer^>^>^ Accepted;
virtual event System::EventHandler<ErrorEventArgs^>^ Error;
internal:
ref struct AcceptedLambda
{
AcceptedLambda(ServerAcceptor^ acceptor, const NativeConfig& config)
: m_acceptor{ acceptor },
m_config{ config }
{}
void operator()(Interop::Callback<typename NativeServer::ConnectionPtr()>&& getConnection)
{
try
{
Server^ server = nullptr;
try
{
server = gcnew Server{ getConnection(), m_acceptor->m_handlerFactory, *m_config };
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
m_acceptor->Accepted(m_acceptor, gcnew ComponentEventArgs<IServer^>{ server });
}
catch (System::Exception^ e)
{
m_acceptor->Error(m_acceptor, gcnew ErrorEventArgs{ e });
}
}
ServerAcceptor^ m_acceptor;
NativeObject<NativeConfig> m_config;
};
private:
NativeObject<NativeServerAcceptor> m_acceptor;
HandlerFactory^ m_handlerFactory;
};
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,39 @@
#pragma once
#ifdef __cplusplus_cli
#pragma managed(push, off)
#endif
#include "Export.h"
#include <exception>
namespace IPC
{
namespace Managed
{
namespace detail
{
[[noreturn]] IPC_MANAGED_DLL void ThrowManagedException(std::exception_ptr error);
template <typename Function, typename... Args>
decltype(auto) InvokeThrow(Function&& func, Args&&... args)
{
try
{
return std::forward<Function>(func)(std::forward<Args>(args)...);
}
catch (const std::exception& /*e*/)
{
ThrowManagedException(std::current_exception());
}
}
} // detail
} // Managed
} // IPC
#ifdef __cplusplus_cli
#pragma managed(pop)
#endif

Просмотреть файл

@ -0,0 +1,80 @@
#pragma once
#include "Interop/Transport.h"
#include "NativeObject.h"
#include "Cast.h"
namespace IPC
{
namespace Managed
{
namespace detail
{
#pragma warning(push)
#pragma warning(disable : 4564)
template <typename Request, typename Response>
[IPC::Managed::Object]
ref class Transport : ITransport<Request, Response>
{
using IClient = Managed::IClient<Request, Response>;
using IServer = Managed::IServer<Request, Response>;
using IClientConnector = Managed::IClientConnector<Request, Response>;
using IServerAcceptor = Managed::IServerAcceptor<Request, Response>;
using IClientAccessor = Managed::IClientAccessor<Request, Response>;
using IServersAccessor = Managed::IServersAccessor<Request, Response>;
using Handler = Managed::Handler<Request, Response>;
using HandlerFactory = Managed::HandlerFactory<Request, Response>;
using NativeRequest = CastType<Request>;
using NativeResponse = CastType<Response>;
using NativeTransport = Interop::Transport<NativeRequest, NativeResponse>;
using NativeConfig = std::shared_ptr<const Interop::Config>;
using NativeClient = typename NativeTransport::Client;
using NativeServer = typename NativeTransport::Server;
using NativeClientConnector = typename NativeTransport::ClientConnector;
using NativeServerAcceptor = typename NativeTransport::ServerAcceptor;
using NativeServerCollection = typename NativeTransport::ServerCollection;
public:
explicit Transport(Config^ config);
virtual IServerAcceptor^ MakeServerAcceptor(System::String^ name, HandlerFactory^ handlerFactory);
virtual IClientConnector^ MakeClientConnector();
virtual IClientAccessor^ ConnectClient(System::String^ acceptorName, System::Boolean async, System::TimeSpan timeout, IClientConnector^ connector);
virtual IServersAccessor^ AcceptServers(System::String^ name, HandlerFactory^ handlerFactory);
internal:
ref class Server;
ref class Client;
ref class ServerAcceptor;
ref class ClientConnector;
ref class ClientAccessor;
ref class ServersAccessor;
private:
NativeObject<NativeTransport> m_transport;
NativeObject<NativeConfig> m_config;
System::Lazy<IClientConnector^> m_clientConnector;
};
#pragma warning(pop)
} // detail
} // Managed
} // IPC

Просмотреть файл

@ -0,0 +1,59 @@
#pragma once
#pragma managed(push, off)
#include "IPC/DefaultTraitsFwd.h"
#pragma managed(pop)
#include "Transport.h"
#include "Client.h"
#include "Server.h"
#include "ClientConnector.h"
#include "ServerAcceptor.h"
#include "Connect.h"
#include "Accept.h"
#include "Policies/ReceiverFactory.h"
#include "Policies/WaitHandleFactory.h"
#include "Policies/TimeoutFactory.h"
#include <msclr/gcroot.h>
namespace IPC
{
namespace Managed
{
namespace detail
{
template <typename Request, typename Response>
Transport<Request, Response>::Transport(Config^ config)
: m_transport{
config->OutputMemorySize,
std::chrono::milliseconds{ static_cast<std::chrono::milliseconds::rep>(config->DefaultRequestTimeout.TotalMilliseconds) },
Policies::MakeReceiverFactory(),
Policies::MakeWaitHandleFactory(),
Policies::MakeTimeoutFactory(std::chrono::milliseconds{ static_cast<std::chrono::milliseconds::rep>(config->ReconnectTimeout.TotalMilliseconds) }) },
m_config{ m_transport->GetConfig() },
m_clientConnector{
gcnew System::Func<IClientConnector^>(this, &Transport<Request, Response>::MakeClientConnector),
System::Threading::LazyThreadSafetyMode::PublicationOnly }
{}
template <typename Request, typename Response>
auto Transport<Request, Response>::MakeServerAcceptor(System::String^ name, HandlerFactory^ handlerFactory)
-> IServerAcceptor^
{
return gcnew ServerAcceptor{ name, handlerFactory, *m_config };
}
template <typename Request, typename Response>
auto Transport<Request, Response>::MakeClientConnector()
-> IClientConnector^
{
return gcnew ClientConnector{ *m_config };
}
} // detail
} // Managed
} // IPC

47
Inc/IPC/OutputChannel.h Normal file
Просмотреть файл

@ -0,0 +1,47 @@
#pragma once
#include "detail/ChannelBase.h"
#include "Exception.h"
#include <memory>
namespace IPC
{
struct DefaultTraits;
template <typename T, typename Traits = DefaultTraits>
class OutputChannel : public detail::ChannelBase<T, Traits::template Queue>
{
using ChannelBase = detail::ChannelBase<T, Traits::template Queue>;
public:
using ChannelBase::ChannelBase;
template <typename U>
bool TrySend(U&& value)
{
if (this->GetQueue().Push(std::forward<U>(value)))
{
if (++this->GetCounter() == 1)
{
this->GetNotEmptyEvent().Signal();
}
return true;
}
return false;
}
template <typename U>
void Send(U&& value)
{
if (!TrySend(std::forward<U>(value)))
{
throw Exception{ "Out of buffers." };
}
}
};
} // IPC

Просмотреть файл

@ -0,0 +1,59 @@
#pragma once
#include "ThreadPool.h"
#include "IPC/detail/Callback.h"
#include <memory>
#pragma warning(push)
#include <boost/optional.hpp>
#pragma warning(pop)
namespace IPC
{
namespace Policies
{
class AsyncReceiverFactory
{
public:
AsyncReceiverFactory() = default;
explicit AsyncReceiverFactory(boost::optional<ThreadPool> pool);
template <typename Queue, typename Handler>
auto operator()(Queue& queue, Handler&& handler) const
{
return Scheduler{
[&queue, handler = std::forward<Handler>(handler)]
{
auto&& value = queue.Pop();
assert(value);
handler(std::move(*value));
},
m_pool };
}
private:
class Scheduler
{
public:
Scheduler(detail::Callback<void()> handler, boost::optional<ThreadPool> pool);
Scheduler(Scheduler&& other);
~Scheduler();
std::size_t operator()() const;
private:
class Impl;
std::shared_ptr<Impl> m_impl;
};
boost::optional<ThreadPool> m_pool;
};
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,23 @@
#pragma once
#include <exception>
#include <iostream>
namespace IPC
{
namespace Policies
{
class ErrorHandler
{
public:
ErrorHandler(std::ostream& stream = std::cerr);
void operator()(std::exception_ptr error) const;
private:
std::ostream& m_stream;
};
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,21 @@
#pragma once
#include <utility>
namespace IPC
{
namespace Policies
{
class InlineReceiverFactory
{
public:
template <typename Queue, typename Handler>
auto operator()(Queue& queue, Handler&& handler) const
{
return [&queue, handler = std::forward<Handler>(handler)]() mutable { return queue.ConsumeAll(handler); };
}
};
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,25 @@
#pragma once
#include "IPC/detail/Callback.h"
#include <chrono>
namespace IPC
{
namespace Policies
{
/// Provides infinite timeout behavior. The provided handler is not preserved.
class NullTimeoutFactory
{
public:
NullTimeoutFactory(const std::chrono::milliseconds& /*defaultTimeout*/ = {})
{}
auto operator()(const detail::Callback<void()>& /*handler*/) const
{
return [](auto&&...) {};
}
};
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,51 @@
#pragma once
#include "ReceiverFactoryFwd.h"
#include "InlineReceiverFactory.h"
#include "AsyncReceiverFactory.h"
#include <type_traits>
#pragma warning(push)
#include <boost/variant.hpp>
#pragma warning(pop)
namespace IPC
{
namespace detail
{
template <typename... Factories>
class VariantReceiverFactory
{
public:
VariantReceiverFactory() = default;
template <typename Factory,
typename = std::enable_if_t<!std::is_base_of<VariantReceiverFactory, std::decay_t<Factory>>::value>>
VariantReceiverFactory(Factory&& factory)
: m_factory{ std::forward<Factory>(factory) }
{}
VariantReceiverFactory(const VariantReceiverFactory& other) = default; // TODO: Remove this when crashing bugs are fixed in VC14.
template <typename Queue, typename Handler>
auto operator()(Queue& queue, Handler&& handler) const
{
using Receiver = boost::variant<decltype(std::declval<Factories>()(queue, std::forward<Handler>(handler)))...>;
auto&& receiver = boost::apply_visitor(
[&](auto& factory) { return Receiver{ factory(queue, std::forward<Handler>(handler)) }; },
m_factory);
return [receiver = std::make_unique<Receiver>(std::move(receiver))] // TODO: Do not make_unique when VC14 bugs are fixed.
{
return boost::apply_visitor([](auto& r) { return r(); }, *receiver);
};
}
private:
boost::variant<Factories...> m_factory;
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,21 @@
#pragma once
namespace IPC
{
namespace detail
{
template <typename... Factories>
class VariantReceiverFactory;
} // detail
namespace Policies
{
class AsyncReceiverFactory;
class InlineReceiverFactory;
using ReceiverFactory = detail::VariantReceiverFactory<AsyncReceiverFactory, InlineReceiverFactory>;
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,34 @@
#pragma once
#include <memory>
namespace IPC
{
namespace Policies
{
class ThreadPool
{
public:
ThreadPool();
ThreadPool(std::uint32_t minThreadCount, std::uint32_t maxThreadCount);
ThreadPool(const ThreadPool& other) = default;
ThreadPool& operator=(const ThreadPool& other) = default;
ThreadPool(ThreadPool&& other) = default;
ThreadPool& operator=(ThreadPool&& other) = default;
~ThreadPool();
void* GetEnvironment();
private:
class Impl;
std::shared_ptr<Impl> m_impl;
};
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,55 @@
#pragma once
#include "ThreadPool.h"
#include "IPC/detail/Callback.h"
#include <memory>
#include <chrono>
#pragma warning(push)
#include <boost/optional.hpp>
#pragma warning(pop)
namespace IPC
{
namespace Policies
{
class TimeoutFactory
{
class Timeout;
class Scheduler
{
public:
explicit Scheduler(std::shared_ptr<Timeout> timeout);
~Scheduler();
/// Schedules a timeout to fire after predefined amount of time configured in TimeoutFactory.
/// Previously scheduled timeout is deactivated first.
void operator()() const;
/// Schedules a timeout to fire after specified amount of time.
/// Previously scheduled timeout is deactivated first.
void operator()(const std::chrono::milliseconds& timeout) const;
/// Deactivates already scheduled timeout.
/// Blocks until the running handler (if any) returns.
void operator()(nullptr_t) const;
private:
std::shared_ptr<Timeout> m_timeout;
};
public:
TimeoutFactory(const std::chrono::milliseconds& defaultTimeout = {}, boost::optional<ThreadPool> pool = {});
Scheduler operator()(detail::Callback<void()> handler) const;
private:
std::chrono::milliseconds m_defaultTimeout;
boost::optional<ThreadPool> m_pool;
};
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,125 @@
#pragma once
#include "TransactionManagerFwd.h"
#include "NullTimeoutFactory.h"
#include "IPC/detail/LockFree/IndexedObjectPool.h"
#include <chrono>
#pragma warning(push)
#include <boost/optional.hpp>
#pragma warning(pop)
namespace IPC
{
namespace Policies
{
template <typename Context, typename TimeoutFactory>
class TransactionManager
{
public:
using Id = std::uint32_t;
TransactionManager() = default;
explicit TransactionManager(TimeoutFactory timeoutFactory, const std::chrono::milliseconds& defaultTimeout = {})
: m_timeoutFactory{ std::move(timeoutFactory) },
m_defaultTimeout{ defaultTimeout }
{}
template <typename OtherContext>
Id BeginTransaction(OtherContext&& context)
{
return BeginTransaction(std::forward<OtherContext>(context), m_defaultTimeout);
}
template <typename OtherContext>
Id BeginTransaction(OtherContext&& context, const std::chrono::milliseconds& timeout)
{
auto result = m_transactions->Take(
[this](Id id)
{
return m_timeoutFactory([this, id] { EndTransaction(id); });
});
Transaction& transaction = result.first;
Id id = result.second;
try
{
transaction.Begin(
std::forward<OtherContext>(context),
timeout != std::chrono::milliseconds::zero() ? timeout : GetDefaultTimeout());
}
catch (...)
{
m_transactions->Return(id);
throw;
}
return id;
}
boost::optional<Context> EndTransaction(Id id)
{
boost::optional<Context> context;
m_transactions->Return(id, [&](auto& transaction) { context = transaction.End(); });
return context;
}
void TerminateTransactions()
{
m_transactions->ReturnAll([](auto& transaction) { transaction.End(); });
}
private:
class Transaction
{
public:
template <typename SchedulerFactory>
Transaction(Id id, SchedulerFactory&& schedulerFactory)
: m_timeoutScheduler{ std::forward<SchedulerFactory>(schedulerFactory)(id) }
{}
template <typename OtherContext>
void Begin(OtherContext&& context, const std::chrono::milliseconds& timeout)
{
m_context = std::forward<OtherContext>(context);
m_timeoutScheduler(timeout);
}
boost::optional<Context> End()
{
m_timeoutScheduler(nullptr); // This must wait for callback (which effectively runs End) to complete.
decltype(m_context) context;
context.swap(m_context);
return context;
}
private:
using TimeoutScheduler = decltype(std::declval<TimeoutFactory>()({}));
boost::optional<Context> m_context;
TimeoutScheduler m_timeoutScheduler;
};
using TransactionPool = detail::LockFree::IndexedObjectPool<Transaction, std::allocator<void>>;
static_assert(std::is_same<Id, typename TransactionPool::Index>::value, "Id and Index must have the same type.");
static constexpr auto GetDefaultTimeout()
{
return std::chrono::seconds{ 3 };
}
std::unique_ptr<TransactionPool> m_transactions{ std::make_unique<TransactionPool>() };
TimeoutFactory m_timeoutFactory;
std::chrono::milliseconds m_defaultTimeout{};
};
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,19 @@
#pragma once
namespace IPC
{
namespace Policies
{
class TransactionManagerFactory
{
public:
template <typename T>
auto operator()(T&&) const
{
return typename std::decay_t<T>::type{};
}
};
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,14 @@
#pragma once
namespace IPC
{
namespace Policies
{
class NullTimeoutFactory;
template <typename Context, typename TimeoutFactory = NullTimeoutFactory>
class TransactionManager;
} // Policies
} // IPC

Просмотреть файл

@ -0,0 +1,33 @@
#pragma once
#include "ThreadPool.h"
#include "IPC/detail/Callback.h"
#include "IPC/detail/KernelObject.h"
#include <memory>
#pragma warning(push)
#include <boost/optional.hpp>
#pragma warning(pop)
namespace IPC
{
namespace Policies
{
class WaitHandleFactory
{
public:
WaitHandleFactory() = default;
explicit WaitHandleFactory(boost::optional<ThreadPool> pool);
std::shared_ptr<void> operator()(detail::KernelObject obj, detail::Callback<bool()> handler) const;
private:
class Waiter;
boost::optional<ThreadPool> m_pool;
};
} // Policies
} // IPC

106
Inc/IPC/Server.h Normal file
Просмотреть файл

@ -0,0 +1,106 @@
#pragma once
#include "ServerFwd.h"
#include "detail/PacketConnectionHolder.h"
#include "detail/Packet.h"
#include "detail/Callback.h"
#include <memory>
namespace IPC
{
namespace detail
{
template <typename Request, typename Response, typename Traits>
class ServerTraits : public ServerPacketInfo<Request, Response, Traits>
{
public:
using HandlerCallback = std::conditional_t<
std::is_void<Response>::value,
Callback<void(Request&&)>,
Callback<void(Request&&, Callback<void(Response)>)>>;
using Context = HandlerCallback;
};
} // detail
template <typename Request, typename Response, typename Traits>
class Server : public detail::PacketConnectionHolder<detail::ServerTraits<Request, Response, Traits>>
{
using PacketConnectionHolder = detail::PacketConnectionHolder<detail::ServerTraits<Request, Response, Traits>>;
protected:
using typename PacketConnectionHolder::InputPacket;
using typename PacketConnectionHolder::OutputPacket;
public:
static_assert(!std::is_void<Request>::value, "Request cannot be void.");
static_assert(std::is_same<Connection, ServerConnection<Request, Response, Traits>>::value, "Connection definitions must be the same.");
using typename PacketConnectionHolder::Connection;
using HandlerCallback = typename PacketConnectionHolder::HandlerCallback;
template <typename Handler, typename CloseHandler>
Server(std::unique_ptr<Connection> connection, Handler&& handler, CloseHandler&& closeHandler)
: PacketConnectionHolder{
std::move(connection),
std::forward<Handler>(handler),
[this](InputPacket&& packet) { RequestHandler(std::move(packet)); },
std::forward<CloseHandler>(closeHandler) }
{}
private:
template <typename U = Response, std::enable_if_t<!std::is_void<U>::value>* = nullptr>
void RequestHandler(InputPacket&& packet)
{
struct State : std::pair<std::shared_ptr<Connection>, typename Traits::PacketId>
{
using std::pair<std::shared_ptr<Connection>, typename Traits::PacketId>::pair;
State(State&& other) = default;
~State()
{
if (auto& connection = this->first)
{
if (auto channel = connection->GetOutputChannel(std::nothrow))
{
channel->TrySend(OutputPacket{ this->second });
}
}
}
};
auto id = packet.GetId();
InvokeHandler(
std::move(packet),
[state = State{ this->GetSharedConnection(), id }](auto&& response) mutable
{
auto connection = std::move(state.first);
assert(connection);
connection->GetOutputChannel().Send(OutputPacket{ state.second, std::forward<decltype(response)>(response) });
});
}
template <typename U = Response, std::enable_if_t<std::is_void<U>::value>* = nullptr>
void RequestHandler(InputPacket&& packet)
{
InvokeHandler(std::move(packet));
}
auto& GetHandler()
{
return this->GetContext();
}
template <typename... Args>
void InvokeHandler(InputPacket&& packet, Args&&... args)
{
GetHandler()(std::move(packet.GetPayload()), std::forward<Args>(args)...);
}
};
} // IPC

17
Inc/IPC/ServerFwd.h Normal file
Просмотреть файл

@ -0,0 +1,17 @@
#pragma once
#include "detail/PacketConnectionFwd.h"
#include "detail/PacketFwd.h"
namespace IPC
{
struct DefaultTraits;
template <typename Request, typename Response, typename Traits = DefaultTraits>
class Server;
template <typename Request, typename Response, typename Traits = DefaultTraits>
using ServerConnection = detail::PacketConnection<detail::ServerPacketInfo<Request, Response, Traits>>;
} // IPC

195
Inc/IPC/SharedMemory.h Normal file
Просмотреть файл

@ -0,0 +1,195 @@
#pragma once
#pragma warning(push)
#pragma warning(disable : 4459)
#include <boost/interprocess/managed_windows_shared_memory.hpp>
#include <boost/interprocess/smart_ptr/unique_ptr.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/smart_ptr/weak_ptr.hpp>
#pragma warning(pop)
#include "detail/Alias.h"
#include "detail/SpinLock.h"
#include "detail/RecursiveSpinLock.h"
#include "Exception.h"
#include <string>
#include <type_traits>
namespace IPC
{
class SharedMemory
{
struct MutexFamily
{
using mutex_type = detail::SpinLock;
using recursive_mutex_type = detail::RecursiveSpinLock;
};
using ManagedSharedMemory = detail::ipc::basic_managed_windows_shared_memory<
char, detail::ipc::rbtree_best_fit<MutexFamily>, detail::ipc::iset_index>;
public:
using Handle = ManagedSharedMemory::handle_t;
template <typename T>
class Allocator : public ManagedSharedMemory::allocator<T>::type
{
using Base = typename ManagedSharedMemory::allocator<T>::type;
public:
template <typename U>
struct rebind
{
using other = Allocator<U>;
};
using Base::Base;
using Base::construct;
void construct(const typename Base::pointer& ptr, const boost::container::default_init_t&)
{
::new (static_cast<void*>(boost::interprocess::ipcdetail::to_raw_pointer(ptr)), boost_container_new_t()) typename Base::value_type;
}
};
template <typename T>
using Deleter = typename ManagedSharedMemory::deleter<T>::type;
template <typename T>
using UniquePtr = typename detail::ipc::managed_unique_ptr<T, ManagedSharedMemory>::type;
template <typename T>
using SharedPtr = typename detail::ipc::managed_shared_ptr<T, ManagedSharedMemory>::type;
template <typename T>
using WeakPtr = typename detail::ipc::managed_weak_ptr<T, ManagedSharedMemory>::type;
SharedMemory(create_only_t, const char* name, std::size_t size);
SharedMemory(open_only_t, const char* name);
SharedMemory(const SharedMemory& other) = delete;
SharedMemory& operator=(const SharedMemory& other) = delete;
SharedMemory(SharedMemory&& other) = default;
SharedMemory& operator=(SharedMemory&& other) = default;
template <typename T>
Allocator<T> GetAllocator()
{
return{ m_memory.get_allocator<T>() };
}
template <typename T>
Deleter<T> GetDeleter()
{
return m_memory.get_deleter<T>();
}
template <typename T, typename Name, typename... Args>
T& Construct(Name name, Args&&... args)
{
static_assert(IsValidName<Name>::value, "Unknown name or name tag is used.");
return *m_memory.construct<T>(name)(std::forward<Args>(args)...);
}
template <typename T, typename Name>
T& Find(Name name)
{
static_assert(IsValidName<Name>::value, "Unknown name or name tag is used.");
if (auto obj = m_memory.find<T>(name).first)
{
return *obj;
}
throw Exception{ "Unable to find the object." };
}
template <typename T>
void Destruct(T* obj)
{
assert(obj == nullptr || m_memory.belongs_to_segment(obj));
m_memory.destroy_ptr(obj);
}
template <typename T, typename Name, typename... Args>
UniquePtr<T> MakeUnique(Name name, Args&&... args)
{
static_assert(IsValidName<Name>::value, "Unknown name or name tag is used.");
return MakeUniquePtr(&Construct<T>(name, std::forward<Args>(args)...));
}
template <typename T>
UniquePtr<T> MakeUniquePtr(T* obj)
{
assert(obj == nullptr || m_memory.belongs_to_segment(obj));
return detail::ipc::make_managed_unique_ptr(obj, m_memory);
}
template <typename T, typename Name, typename... Args>
SharedPtr<T> MakeShared(Name name, Args&&... args)
{
static_assert(IsValidName<Name>::value, "Unknown name or name tag is used.");
return MakeSharedPtr(&Construct<T>(name, std::forward<Args>(args)...));
}
template <typename T>
SharedPtr<T> MakeSharedPtr(T* obj)
{
assert(obj == nullptr || Contains(obj));
return detail::ipc::make_managed_shared_ptr(obj, m_memory);
}
template <typename T>
WeakPtr<T> MakeWeakPtr(const SharedPtr<T>& obj)
{
return typename detail::ipc::managed_weak_ptr<T, ManagedSharedMemory>::type{ obj };
}
template <typename T>
Handle ToHandle(T& obj) const
{
assert(Contains(&obj));
return m_memory.get_handle_from_address(&obj);
}
template <typename T>
T& FromHandle(Handle handle) const
{
auto ptr = m_memory.get_address_from_handle(handle);
assert(Contains(ptr));
return *static_cast<T*>(ptr);
}
template <typename Function>
void InvokeAtomic(Function&& func)
{
m_memory.atomic_func(func);
}
bool Contains(const void* ptr) const;
const std::string& GetName() const;
std::size_t GetFreeSize() const;
static std::size_t GetMinSize();
private:
template <typename Name>
using IsValidName = std::bool_constant<
std::is_same<std::decay_t<Name>, const char*>::value
|| std::is_same<std::decay_t<Name>, decltype(anonymous_instance)>::value
|| std::is_same<std::decay_t<Name>, decltype(unique_instance)>::value>;
ManagedSharedMemory m_memory;
std::string m_name;
};
} // IPC

Просмотреть файл

@ -0,0 +1,28 @@
#pragma once
#include <memory>
namespace IPC
{
class SharedMemory;
class SharedMemoryCache
{
public:
SharedMemoryCache();
~SharedMemoryCache();
std::shared_ptr<SharedMemory> Create(const char* name, std::size_t size);
std::shared_ptr<SharedMemory> Open(const char* name);
private:
class Impl;
std::unique_ptr<Impl> m_impl;
};
} // IPC

144
Inc/IPC/Transport.h Normal file
Просмотреть файл

@ -0,0 +1,144 @@
#pragma once
#include "Acceptor.h"
#include "Accept.h"
#include "Connector.h"
#include "Connect.h"
#include <memory>
#include <mutex>
namespace IPC
{
struct DefaultTraits;
template <typename Request, typename Response, typename Traits = DefaultTraits>
class Transport
{
public:
using Client = Client<Request, Response, Traits>;
using Server = Server<Request, Response, Traits>;
using ClientConnector = ClientConnector<Request, Response, Traits>;
using ServerConnector = ServerConnector<Request, Response, Traits>;
using ClientAcceptor = ClientAcceptor<Request, Response, Traits>;
using ServerAcceptor = ServerAcceptor<Request, Response, Traits>;
Transport()
: Transport{ {} }
{}
explicit Transport(
ChannelSettings<Traits> channelSettings,
std::size_t hostInfoMemorySize = 0,
typename Traits::TimeoutFactory timeoutFactory = {},
typename Traits::ErrorHandler errorHandler = {},
typename Traits::TransactionManagerFactory transactionManagerFactory = {})
: m_channelSettings{ std::move(channelSettings) },
m_hostInfoMemorySize{ hostInfoMemorySize },
m_timeoutFactory{ std::move(timeoutFactory) },
m_errorHandler{ std::move(errorHandler) },
m_transactionManagerFactory{ std::move(transactionManagerFactory) }
{}
auto MakeClientConnector()
{
return ClientConnector{ m_channelSettings, m_transactionManagerFactory };
}
auto MakeServerConnector()
{
return ServerConnector{ m_channelSettings, m_transactionManagerFactory };
}
template <typename Handler>
auto MakeServerAcceptor(const char* name, Handler&& handler)
{
return ServerAcceptor{ name, std::forward<Handler>(handler), m_channelSettings, m_hostInfoMemorySize };
}
template <typename Handler>
auto MakeClientAcceptor(const char* name, Handler&& handler)
{
return ClientAcceptor{ name, std::forward<Handler>(handler), m_channelSettings, m_hostInfoMemorySize };
}
template <typename... TransactionArgs>
auto ConnectClient(
const char* name,
bool async,
std::shared_ptr<ClientConnector> connector = {},
TransactionArgs&&... transactionArgs)
{
if (!connector)
{
std::call_once(
m_clientConnectorOnceFlag,
[this] { m_clientConnector = std::make_shared<ClientConnector>(MakeClientConnector()); });
connector = m_clientConnector;
}
return IPC::ConnectClient(
name,
connector,
async,
m_timeoutFactory,
m_errorHandler,
m_transactionManagerFactory,
std::forward<TransactionArgs>(transactionArgs)...);
}
template <typename HandlerFactory, typename... TransactionArgs>
auto ConnectServer(
const char* name,
HandlerFactory&& handlerFactory,
bool async,
std::shared_ptr<ServerConnector> connector = {},
TransactionArgs&&... transactionArgs)
{
if (!connector)
{
std::call_once(
m_serverConnectorOnceFlag,
[this] { m_serverConnector = std::make_shared<ServerConnector>(MakeServerConnector()); });
connector = m_serverConnector;
}
return IPC::ConnectServer(
name,
connector,
std::forward<HandlerFactory>(handlerFactory),
async,
m_timeoutFactory,
m_errorHandler,
std::forward<TransactionArgs>(transactionArgs)...);
}
template <typename HandlerFactory>
auto AcceptServers(const char* name, HandlerFactory&& handlerFactory)
{
return IPC::AcceptServers<Request, Response, Traits>(
name, std::forward<HandlerFactory>(handlerFactory), m_channelSettings, m_hostInfoMemorySize, m_errorHandler);
}
auto AcceptClients(const char* name)
{
return IPC::AcceptClients<Request, Response, Traits>(
name, m_channelSettings, m_hostInfoMemorySize, m_errorHandler, m_transactionManagerFactory);
}
private:
ChannelSettings<Traits> m_channelSettings;
std::size_t m_hostInfoMemorySize;
typename Traits::TimeoutFactory m_timeoutFactory;
typename Traits::ErrorHandler m_errorHandler;
typename Traits::TransactionManagerFactory m_transactionManagerFactory;
std::shared_ptr<ClientConnector> m_clientConnector;
std::shared_ptr<ServerConnector> m_serverConnector;
std::once_flag m_clientConnectorOnceFlag;
std::once_flag m_serverConnectorOnceFlag;
};
} // IPC

34
Inc/IPC/Version.h Normal file
Просмотреть файл

@ -0,0 +1,34 @@
#pragma once
#include <type_traits>
#pragma warning(push)
#include <boost/version.hpp>
#include <boost/config.hpp>
#pragma warning(pop)
#define IPC_COMPILER_VERSION "MSVC-" BOOST_STRINGIZE(_MSC_VER)
#define IPC_BOOST_VERSION "BOOST-" BOOST_STRINGIZE(BOOST_VERSION)
#ifdef NDEBUG
#define IPC_LIB_NAME "IPC"
#else
#define IPC_LIB_NAME "IPCD"
#endif
#define IPC_VERSION 1000 // XYYY, X=major, YYY=minor
#define IPC_LIB_VERSION IPC_LIB_NAME "-" BOOST_STRINGIZE(IPC_VERSION)
#define IPC_VERSION_TOKEN IPC_COMPILER_VERSION "_" IPC_BOOST_VERSION "_" IPC_LIB_VERSION
namespace IPC
{
template <typename T>
struct Version : std::integral_constant<std::size_t, 0> // Specialize and change (bump) version when breaking ABI of T.
{};
} // IPC

37
Inc/IPC/detail/Accept.h Normal file
Просмотреть файл

@ -0,0 +1,37 @@
#pragma once
#include <memory>
#include <exception>
namespace IPC
{
namespace detail
{
template <typename Acceptor, typename Collection, typename ComponentFactory, typename ErrorHandler, typename... Args>
auto Accept(const char* name, std::shared_ptr<Collection> components, ComponentFactory&& componentFactory, ErrorHandler&& errorHandler, Args&&... args)
{
auto acceptor = std::make_shared<Acceptor>(
name,
[components, componentFactory = std::forward<ComponentFactory>(componentFactory), errorHandler = std::forward<ErrorHandler>(errorHandler)](auto&& futureConnection) mutable
{
try
{
components->Accept(
[&](auto&& closeHandler)
{
return componentFactory(futureConnection.get(), std::forward<decltype(closeHandler)>(closeHandler));
});
}
catch (...)
{
errorHandler(std::current_exception());
}
},
std::forward<Args>(args)...);
return [components = std::move(components), acceptor = std::move(acceptor)] { return components->GetComponents(); };
}
} // detail
} // IPC

28
Inc/IPC/detail/Alias.h Normal file
Просмотреть файл

@ -0,0 +1,28 @@
#pragma once
#pragma warning(disable : 4503) // decorated name length exceeded, name was truncated
#pragma warning(push)
#include <boost/interprocess/creation_tags.hpp>
#include <boost/interprocess/detail/segment_manager_helper.hpp>
#pragma warning(pop)
namespace IPC
{
namespace detail
{
namespace ipc = boost::interprocess;
} // detail
using detail::ipc::create_only_t;
using detail::ipc::create_only;
using detail::ipc::open_only_t;
using detail::ipc::open_only;
using detail::ipc::anonymous_instance;
using detail::ipc::unique_instance;
} // IPC

28
Inc/IPC/detail/Apply.h Normal file
Просмотреть файл

@ -0,0 +1,28 @@
#pragma once
#include <tuple>
#include <utility>
namespace IPC
{
namespace detail
{
template <typename Function, typename Tuple, std::size_t... I>
decltype(auto) ApplyTupleHelper(Function&& func, Tuple&& args, std::index_sequence<I...>)
{
(void)args;
return std::forward<Function>(func)(std::get<I>(std::forward<Tuple>(args))...);
}
template <typename Function, typename Tuple>
decltype(auto) ApplyTuple(Function&& func, Tuple&& args)
{
return ApplyTupleHelper(
std::forward<Function>(func),
std::forward<Tuple>(args),
std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tuple>>::value>{});
}
} // detail
} // IPC

91
Inc/IPC/detail/Callback.h Normal file
Просмотреть файл

@ -0,0 +1,91 @@
#pragma once
#pragma warning(push)
#include <boost/function.hpp>
#pragma warning(pop)
#include <type_traits>
#include <cassert>
namespace IPC
{
namespace detail
{
template <typename Function>
class CopyableFunction
{
public:
explicit CopyableFunction(Function&& func)
: m_func{ std::move(func) }
{}
CopyableFunction(const CopyableFunction& other)
: CopyableFunction{ std::move(const_cast<CopyableFunction&>(other)) }
{}
CopyableFunction(CopyableFunction&& other) = default;
template <typename... Args>
decltype(auto) operator()(Args&&... args)
{
return m_func(std::forward<Args>(args)...);
}
template <typename... Args>
decltype(auto) operator()(Args&&... args) const
{
return m_func(std::forward<Args>(args)...);
}
private:
Function m_func;
};
template <typename Function>
class Callback;
template <typename Result, typename... Args>
class Callback<Result(Args...)>
{
public:
Callback() = default;
template <typename Function, typename = std::enable_if_t<!std::is_base_of<Callback, std::remove_reference_t<Function>>::value>,
std::enable_if_t<!std::is_copy_constructible<std::decay_t<Function>>::value>* = nullptr>
Callback(Function&& func)
: m_func{ CopyableFunction<std::decay_t<Function>>{ std::forward<Function>(func) } }
{}
template <typename Function, typename = std::enable_if_t<!std::is_base_of<Callback, std::remove_reference_t<Function>>::value>,
std::enable_if_t<std::is_copy_constructible<std::decay_t<Function>>::value>* = nullptr>
Callback(Function&& func)
: m_func{ std::forward<Function>(func) }
{}
Callback(const Callback& other) = delete;
Callback& operator=(const Callback& other) = delete;
Callback(Callback&& other) = default;
Callback& operator=(Callback&& other) = default;
explicit operator bool() const
{
return static_cast<bool>(m_func);
}
template <typename... OtherArgs>
decltype(auto) operator()(OtherArgs&&... args) const
{
assert(m_func);
return m_func(std::forward<OtherArgs>(args)...);
}
private:
boost::function<Result(Args...)> m_func; // TODO: Use std::function when bugs are fixed in VC14 (capturing nested polymorphic lambdas fails).
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,113 @@
#pragma once
#include "IPC/SharedMemory.h"
#include "IPC/Version.h"
#include "Alias.h"
#include "RandomString.h"
#include "KernelEvent.h"
#include "SharedObject.h"
#include <string>
#include <typeinfo>
#include <memory>
#include <atomic>
#pragma warning(push)
#include <boost/interprocess/containers/string.hpp>
#pragma warning(pop)
namespace IPC
{
namespace detail
{
namespace
{
template <typename T>
const char* MakeVersionedName(const char* name)
{
static const std::string s_versionedTypeName = std::string{ typeid(T).name() } + "_V" + std::to_string(Version<T>::value) + "_";
thread_local std::string s_name = s_versionedTypeName;
s_name.resize(s_versionedTypeName.size());
if (name)
{
s_name += name;
}
return s_name.c_str();
}
}
template <typename T, template <typename U, typename Allocator> typename QueueT>
class ChannelBase
{
public:
ChannelBase(create_only_t, const char* name, std::shared_ptr<SharedMemory> memory)
: m_memory{ std::move(memory) },
m_queue{ create_only, *m_memory, MakeVersionedName<DataQueue>(name), m_memory->GetAllocator<void>() },
m_notEmptyEvent{ create_only, false, false, m_queue->m_notEmptyEventName.c_str() }
{}
ChannelBase(open_only_t, const char* name, std::shared_ptr<SharedMemory> memory)
: m_memory{ std::move(memory) },
m_queue{ open_only, *m_memory, MakeVersionedName<DataQueue>(name) },
m_notEmptyEvent{ open_only, m_queue->m_notEmptyEventName.c_str() }
{}
ChannelBase(const ChannelBase& other) = delete;
ChannelBase& operator=(const ChannelBase& other) = delete;
ChannelBase(ChannelBase&& other) = default;
ChannelBase& operator=(ChannelBase&& other) = default;
bool IsEmpty() const
{
return m_queue->IsEmpty();
}
const std::shared_ptr<SharedMemory>& GetMemory() const
{
return m_memory;
}
protected:
using Queue = QueueT<T, SharedMemory::Allocator<T>>;
Queue& GetQueue()
{
return *m_queue;
}
auto& GetNotEmptyEvent()
{
return m_notEmptyEvent;
}
auto& GetCounter()
{
return m_queue->m_count;
}
private:
using String = ipc::basic_string<char, std::char_traits<char>, SharedMemory::Allocator<char>>;
struct DataQueue : Queue
{
explicit DataQueue(const SharedMemory::Allocator<char>& allocator)
: ChannelBase::Queue{ allocator }, // TODO: Use "Queue" when VC14 bugs are fixed.
m_notEmptyEventName{ GenerateRandomString().c_str(), allocator }
{}
const String m_notEmptyEventName;
std::atomic_size_t m_count{ 0 };
};
std::shared_ptr<SharedMemory> m_memory;
SharedObject<DataQueue> m_queue;
KernelEvent m_notEmptyEvent;
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,106 @@
#pragma once
#include "IPC/ChannelSettings.h"
#include "IPC/InputChannel.h"
#include "IPC/OutputChannel.h"
namespace IPC
{
namespace detail
{
template <typename Traits>
class ChannelFactory;
template <>
class ChannelFactory<void>
{
public:
class InstanceBase
{
protected:
std::shared_ptr<SharedMemory> GetMemory(create_only_t, bool input, const char* name, const ChannelSettingsBase& settings);
std::shared_ptr<SharedMemory> GetMemory(open_only_t, bool input, const char* name, const ChannelSettingsBase& settings);
private:
std::shared_ptr<SharedMemory> GetMemory(bool open, bool input, const char* name, const ChannelSettingsBase& settings);
std::weak_ptr<SharedMemory> m_current;
};
};
template <typename Traits>
class ChannelFactory : public ChannelSettings<Traits>
{
public:
class Instance : public ChannelFactory<void>::InstanceBase, public ChannelSettings<Traits>
{
public:
explicit Instance(ChannelSettings<Traits> settings)
: ChannelSettings{ std::move(settings) }
{}
template <typename T>
auto CreateInput(const char* name)
{
return MakeInput<T>(create_only, name);
}
template <typename T>
auto OpenInput(const char* name)
{
return MakeInput<T>(open_only, name);
}
template <typename T>
auto CreateOutput(const char* name)
{
return MakeOutput<T>(create_only, name);
}
template <typename T>
auto OpenOutput(const char* name)
{
return MakeOutput<T>(open_only, name);
}
private:
template <typename T, typename OpenOrCreate>
auto MakeInput(OpenOrCreate openOrCreate, const char* name)
{
return InputChannel<T, Traits>{
openOrCreate, name, GetMemory(openOrCreate, true, name, *this), GetWaitHandleFactory(), GetReceiverFactory() };
}
template <typename T, typename OpenOrCreate>
auto MakeOutput(OpenOrCreate openOrCreate, const char* name)
{
return OutputChannel<T, Traits>{ openOrCreate, name, GetMemory(openOrCreate, false, name, *this) };
}
};
explicit ChannelFactory(ChannelSettings<Traits> settings)
: ChannelSettings{ std::move(settings) }
{}
auto MakeInstance() const
{
return Instance{ *this };
}
auto Override(const char* commonInput, const char* commonOutput) const
{
auto newSettings = static_cast<const ChannelSettings<Traits>&>(*this);
newSettings.Override(commonInput, commonOutput);
return ChannelFactory{ std::move(newSettings) };
}
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,71 @@
#pragma once
#include <IPC/SharedMemoryCache.h>
#include <IPC/SharedMemory.h>
#include <memory>
namespace IPC
{
namespace detail
{
class ChannelSettingsBase
{
public:
void SetInput(std::size_t memorySize, bool allowOverride);
void SetOutput(std::size_t memorySize, bool allowOverride);
void SetInput(std::shared_ptr<SharedMemory> memory, bool allowOverride);
void SetOutput(std::shared_ptr<SharedMemory> memory, bool allowOverride);
void SetInputOutput(std::size_t memorySize, bool allowOverride);
void SetInputOutput(std::shared_ptr<SharedMemory> memory, bool allowOverride);
const std::shared_ptr<SharedMemory>& GetCommonInput() const;
const std::shared_ptr<SharedMemory>& GetCommonOutput() const;
bool IsInputOverrideAllowed() const;
bool IsOutputOverrideAllowed() const;
bool IsSharedInputOutput() const;
const std::shared_ptr<SharedMemoryCache>& GetMemoryCache() const;
protected:
explicit ChannelSettingsBase(std::shared_ptr<SharedMemoryCache> cache);
private:
template <typename Traits>
friend class ChannelFactory;
struct ChannelConfig
{
std::size_t m_size{ 1024 * 1024 };
std::shared_ptr<SharedMemory> m_common;
bool m_allowOverride{ true };
};
struct Config
{
ChannelConfig m_input;
ChannelConfig m_output;
bool m_shared{ false };
};
void Override(const char* commonInput, const char* commonOutput);
const Config& GetConfig() const;
Config m_config;
std::shared_ptr<SharedMemoryCache> m_cache;
};
} // detail
} // IPC

177
Inc/IPC/detail/Connect.h Normal file
Просмотреть файл

@ -0,0 +1,177 @@
#pragma once
#include "Callback.h"
#include "IPC/Exception.h"
#include <string>
#include <memory>
#include <atomic>
#include <mutex>
#include <condition_variable>
namespace IPC
{
namespace detail
{
template <typename Connector, typename TimeoutFactory, typename ErrorHandler, typename ComponentFactory, typename... TransactionArgs>
auto Connect(
const char* acceptorName,
std::shared_ptr<Connector> connector,
bool async,
TimeoutFactory&& timeoutFactory,
ErrorHandler&& errorHandler,
ComponentFactory&& componentFactory,
TransactionArgs&&... transactionArgs)
{
using Component = decltype(componentFactory(
std::declval<std::unique_ptr<typename Connector::Connection>>(),
std::declval<Callback<void()>>()))::element_type; // Deduce the component type.
using ComponentHolder = std::shared_ptr<Component>; // Keep the component in shared_ptr.
using State = std::pair<ComponentHolder, Callback<void(bool)>>; // Store holder and a callback for reconnection.
// This class will make sure that the user is still interested in reconnection and all objects are valid while we process it.
struct Lifetime
: public std::enable_shared_from_this<Lifetime>,
private std::weak_ptr<State>, // Only user keeps a strong reference. We will lock only while processing.
private std::mutex, // Mutex to use for below condition variable.
private std::condition_variable // A condition variable to wait on State expiration.
{
using WeakState = std::weak_ptr<State>;
Lifetime(const std::shared_ptr<State>& state)
: WeakState{ state }
{}
// Tries to extend the current lifetime if not yet expired.
// The returned object releases the extra reference counter and notifies any waiting thread.
auto Extend()
{
struct Notifier : private std::pair<std::shared_ptr<State>, std::shared_ptr<Lifetime>>
{
using std::pair<std::shared_ptr<State>, std::shared_ptr<Lifetime>>::pair;
Notifier(Notifier&& other) = default;
// Checks if we were able to extend.
explicit operator bool() const
{
return this->first != nullptr;
}
~Notifier()
{
if (this->first) // Check if the content was moved out or it was already expired.
{
assert(this->second);
{
std::lock_guard<std::mutex> guard{ *this->second };
this->first.reset(); // Release the extra reference count.
}
this->second->notify_one(); // Notify any waiting thread about possible expiration.
}
}
};
return Notifier{ std::weak_ptr<State>::lock(), this->shared_from_this() };
}
// Blocks the current thread until any temporary extensions expire.
void Wait()
{
std::unique_lock<std::mutex> guard{ *this };
wait(guard, [this] { return this->expired(); }); // Block until the state expires.
}
};
auto state = std::make_shared<State>();
auto lifetime = std::make_shared<Lifetime>(state);
auto& holder = state->first;
auto& callback = state->second;
const auto reconnector = [lifetime, &callback] // Callback for recursive call to reconnection function.
{
if (auto ext = lifetime->Extend()) // Try extend the lifetime to make sure captured references are still valid.
{
callback(true); // Pass 'true' to indicate asynchronous Connect call.
}
};
using Retry = decltype(timeoutFactory(reconnector));
callback = // Set the reconnection function.
[lifetime, // Keep track of lifetime.
transactionArgs...,
&holder, // Capturing reference since it lives inside the state.
acceptorName = std::string{ acceptorName },
connector,
retry = std::make_shared<Retry>(timeoutFactory(reconnector)), // Make a timeout functor for the reconnection function.
errorHandler = std::forward<ErrorHandler>(errorHandler),
componentFactory = std::forward<ComponentFactory>(componentFactory)](bool async) mutable
{
std::atomic_store(&holder, {}); // Mark that the connection is closed and not available.
// A function that will store the newly established connection in the holder.
auto store = [&](auto&& futureConnection) mutable // Capture references since they live inside the state.
{
try
{
// Construct a new component (Client or Server) for the new connection and pass retry as close handler.
std::atomic_store(
&holder,
ComponentHolder{ componentFactory(futureConnection.get(), [retry] { (*retry)(); }) });
}
catch (...)
{
errorHandler(std::current_exception()); // If something goes wrong, invoke user-defined error handler
(*retry)(); // and retry.
}
};
if (async)
{
connector->Connect( // Connect asynchronously.
acceptorName.c_str(),
[lifetime, store](auto&& futureConnection) mutable
{
if (auto ext = lifetime->Extend()) // Try extend the lifetime to make sure captured references are still valid.
{
store(std::move(futureConnection)); // Try to construct a new component.
}
},
transactionArgs...);
}
else
{
store(connector->Connect(acceptorName.c_str(), transactionArgs...));// Connect synchronously and try to construct a new component.
}
};
callback(async); // Start connection process.
// Object that will keep the state alive and will block on destruction waiting on state expiration.
// This is needed to make sure that when the user destroys the returned function the stored component is already destructed.
std::shared_ptr<void> alive
{
nullptr,
[state, lifetime, connector](void*) mutable // Keep the connector alive to prevent deadlock while destructing the state on separate thread.
{
state.reset(); // Release the state reference.
lifetime->Wait(); // Block current thread if there are any extensions running.
}
};
return [alive, &holder] // Return a function that returns a component holder.
{
if (auto component = std::atomic_load(&holder)) // Holder reference is valid as long as the alive object is valid.
{
return component;
}
throw Exception{ "Connection is not available." };
};
}
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,71 @@
#pragma once
#include "IPC/InputChannel.h"
#include "IPC/OutputChannel.h"
#include "KernelEvent.h"
#include "Callback.h"
#include <tuple>
#include <memory>
#include <atomic>
#include <mutex>
namespace IPC
{
namespace detail
{
template <typename Input, typename Output, typename Traits>
struct ChannelHolder
{
using type = std::tuple<InputChannel<Input, Traits>, OutputChannel<Output, Traits>>;
};
template <typename Input, typename Traits>
struct ChannelHolder<Input, void, Traits>
{
using type = std::tuple<InputChannel<Input, Traits>>;
};
template <typename Output, typename Traits>
struct ChannelHolder<void, Output, Traits>
{
using type = std::tuple<OutputChannel<Output, Traits>>;
};
template <typename Input, typename Output, typename Traits>
using ChannelHolderOf = typename ChannelHolder<Input, Output, Traits>::type;
class ConnectionBase
{
public:
ConnectionBase(const ConnectionBase& other) = delete;
ConnectionBase& operator=(const ConnectionBase& other) = delete;
virtual ~ConnectionBase();
bool IsClosed() const;
void Close();
bool RegisterCloseHandler(Callback<void()> handler, bool combine = false);
bool UnregisterCloseHandler();
protected:
explicit ConnectionBase(KernelEvent localCloseEvent);
void ThrowIfClosed() const;
void CloseHandler();
private:
std::atomic_bool m_isClosed{ false };
KernelEvent m_closeEvent;
std::mutex m_closeHandlerMutex;
Callback<void()> m_closeHandler;
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,69 @@
#pragma once
#include "IPC/Connection.h"
#include "Exception.h"
#include <memory>
#include <type_traits>
namespace IPC
{
namespace detail
{
template <typename Input, typename Output, typename Traits>
class ConnectionHolder
{
public:
using Connection = Connection<Input, Output, Traits>;
explicit ConnectionHolder(std::unique_ptr<Connection> connection)
: m_connection{ std::move(connection) }
{}
ConnectionHolder(const ConnectionHolder& other) = delete;
ConnectionHolder& operator=(const ConnectionHolder& other) = delete;
virtual ~ConnectionHolder() = default;
Connection& GetConnection()
{
return *m_connection;
}
const Connection& GetConnection() const
{
return *m_connection;
}
protected:
template <typename I = Input, typename CloseHandler, std::enable_if_t<std::is_void<I>::value>* = nullptr>
void Register(CloseHandler&& closeHandler)
{
if (!this->m_connection->RegisterCloseHandler(std::forward<CloseHandler>(closeHandler)))
{
throw Exception{ "Failed to register for close event." };
}
}
template <typename I = Input, typename Handler, typename CloseHandler, std::enable_if_t<!std::is_void<I>::value>* = nullptr>
void Register(Handler&& handler, CloseHandler&& closeHandler)
{
if (!m_connection->GetInputChannel().RegisterReceiver(std::forward<Handler>(handler)))
{
throw Exception{ "Failed to register a receiver." };
}
Register<void>(std::forward<CloseHandler>(closeHandler));
}
std::shared_ptr<Connection> GetSharedConnection() const
{
return m_connection;
}
private:
std::shared_ptr<Connection> m_connection;
};
} // detail
} // IPC

64
Inc/IPC/detail/Info.h Normal file
Просмотреть файл

@ -0,0 +1,64 @@
#pragma once
#include "Alias.h"
#include "IPC/SharedMemory.h"
#pragma warning(push)
#include <boost/interprocess/containers/string.hpp>
#include <boost/optional.hpp>
#pragma warning(pop)
namespace IPC
{
namespace detail
{
using String = ipc::basic_string<char, std::char_traits<char>, SharedMemory::Allocator<char>>;
struct Settings
{
boost::optional<String> m_commonInputMemoryName;
boost::optional<String> m_commonOutputMemoryName;
};
struct AcceptorHostInfo
{
explicit AcceptorHostInfo(const String::allocator_type& allocator);
std::uint32_t m_processId{};
String m_acceptorCloseEventName;
Settings m_settings;
// TODO: Add compatibility version info.
};
struct ConnectorPingInfo
{
explicit ConnectorPingInfo(const String::allocator_type& allocator);
std::uint32_t m_processId{};
String m_connectorCloseEventName;
String m_connectorInfoChannelName;
String m_acceptorInfoChannelName;
Settings m_settings;
};
struct ConnectorInfo
{
explicit ConnectorInfo(const String::allocator_type& allocator);
String m_channelName;
};
struct AcceptorInfo
{
explicit AcceptorInfo(const String::allocator_type& allocator);
String m_closeEventName;
String m_channelName;
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,26 @@
#pragma once
#include "Alias.h"
#include "KernelObject.h"
namespace IPC
{
namespace detail
{
class KernelEvent : public KernelObject
{
public:
KernelEvent(nullptr_t);
KernelEvent(create_only_t, bool manualReset, bool initialState = false, const char* name = nullptr);
KernelEvent(open_only_t, const char* name);
bool Signal();
bool Reset();
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,34 @@
#pragma once
#include <memory>
#include <chrono>
#include <type_traits>
namespace IPC
{
namespace detail
{
class KernelObject
{
public:
KernelObject(std::nullptr_t);
explicit KernelObject(void* handle, bool verifyValidHandle = false);
void Wait() const;
bool Wait(std::chrono::milliseconds timeout) const;
bool IsSignaled() const;
explicit operator bool() const;
explicit operator void*() const;
private:
std::shared_ptr<void> m_handle;
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,19 @@
#pragma once
#include "KernelObject.h"
namespace IPC
{
namespace detail
{
class KernelProcess : public KernelObject
{
public:
explicit KernelProcess(std::uint32_t pid);
static std::uint32_t GetCurrentProcessId();
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,194 @@
#pragma once
#include "IPC/detail/SpinLock.h"
#include <memory>
#include <atomic>
#include <mutex>
namespace IPC
{
namespace detail
{
namespace LockFree
{
/// Provides a lock-free access to a list of containers with dynamically growing
/// capacity. Capacity growth is synchronized. Can be allocated directly in
/// shared memory as along as container type has same capability.
template <typename Container, typename AllocatorT>
class ContainerList
{
public:
/// Constructs a list with a single container node.
template <typename... Args>
explicit ContainerList(const AllocatorT& allocator, Args&&... args)
: m_allocator{ allocator },
m_head{ std::forward<Args>(args)... }
{}
/// Applies the provided function to each constant container node in a list.
/// Iteration stops when function returns true or end of list is reached.
template <typename Function>
bool TryApply(Function&& func) const
{
return m_head.TryApply(std::forward<Function>(func)) == nullptr;
}
/// Applies the provided function to each container node in a list.
/// Iteration stops when function returns true or end of list is reached.
template <typename Function>
bool TryApply(Function&& func)
{
return m_head.TryApply(std::forward<Function>(func)) == nullptr;
}
/// Applies the provided function to each container node in a list.
/// Iteration continues until the function returns true. New nodes
/// are constructed with the provided arguments when end of list is reached.
template <typename Function, typename... Args>
void Apply(Function&& func, Args&&... args)
{
m_head.Apply(std::forward<Function>(func), m_allocator, m_lock, std::forward<Args>(args)...);
}
private:
class Node
{
public:
using Allocator = typename std::allocator_traits<AllocatorT>::template rebind_alloc<Node>;
template <typename... Args>
Node(Args&&... args)
: m_container{ std::forward<Args>(args)... }
{}
~Node()
{
auto node = std::move(m_next); // Manual iterative destruction to avoid stack overflow.
while (node)
{
auto next = std::move(node->m_next);
node = std::move(next);
}
}
template <typename Function>
const Node* TryApply(Function&& func) const
{
return TryApply(std::forward<Function>(func), this);
}
template <typename Function>
Node* TryApply(Function&& func)
{
return TryApply(std::forward<Function>(func), this);
}
template <typename Function, typename... Args>
void Apply(Function&& func, Allocator& allocator, SpinLock& lock, Args&&... args)
{
if (auto node = TryApply(func))
{
for (auto newNode = Allocate(allocator, args...);
!node->UpdateTail(func, lock, std::move(newNode))
&& ((node = node->TryApply(func)) != nullptr); )
{}
}
}
private:
class Deleter
{
public:
using pointer = typename std::allocator_traits<Allocator>::pointer;
Deleter() = default;
explicit Deleter(Allocator& allocator)
: m_allocator{ &allocator }
{}
void operator()(const pointer& node) const
{
assert(node);
assert(m_allocator);
std::allocator_traits<Allocator>::destroy(*m_allocator, std::addressof(*node));
std::allocator_traits<Allocator>::deallocate(*m_allocator, node, 1);
}
private:
using AllocatorPtr = typename std::allocator_traits<
typename std::allocator_traits<Allocator>::template rebind_alloc<Allocator>>::pointer;
AllocatorPtr m_allocator{};
};
template <typename Function, typename NodeT>
static NodeT* TryApply(Function&& func, NodeT* node)
{
assert(node);
for (; ; node = std::addressof(*node->m_next))
{
if (func(node->m_container))
{
return nullptr;
}
if (node->m_tail.load(std::memory_order_relaxed))
{
return node;
}
}
}
template <typename Function>
__declspec(noinline) bool UpdateTail(Function&& func, SpinLock& lock, std::unique_ptr<Node, Deleter>&& newNode)
{
assert(newNode);
std::lock_guard<SpinLock> guard{ lock };
if (m_tail.load(std::memory_order_acquire) && func(newNode->m_container))
{
m_next = std::move(newNode);
m_tail.store(false, std::memory_order_release); // Clear the flag after m_next is set.
return true;
}
return false;
}
template <typename... Args>
__declspec(noinline) static auto Allocate(Allocator& allocator, Args&&... args)
{
auto node = std::allocator_traits<Allocator>::allocate(allocator, 1);
try
{
std::allocator_traits<Allocator>::construct(allocator, std::addressof(*node), std::forward<Args>(args)...);
}
catch (...)
{
std::allocator_traits<Allocator>::deallocate(allocator, node, 1);
throw;
}
return std::unique_ptr<Node, Deleter>{ std::move(node), Deleter{ allocator } };
}
Container m_container;
std::atomic_bool m_tail{ true };
std::unique_ptr<Node, Deleter> m_next;
};
typename Node::Allocator m_allocator; // Allocator must be the first one.
Node m_head;
SpinLock m_lock;
};
} // LockFree
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,188 @@
#pragma once
#pragma warning(push)
#include <boost/lockfree/queue.hpp>
#include <boost/optional.hpp>
#pragma warning(pop)
#include <array>
namespace IPC
{
namespace detail
{
namespace LockFree
{
/// Provides a multi-reader/multi-writer queue with a compile-time fixed capacity
/// where pushing and popping is lock-free. Supports any element type. Can also
/// be allocated directly in shared memory as along as T has same capability.
template <typename T, std::size_t Capacity, typename Enable = void>
class FixedQueue;
/// Specialization for complex (non-trivially copyable) types.
template <typename T, std::size_t Capacity, typename Enable>
class FixedQueue
{
public:
FixedQueue()
{
for (std::size_t i = 0; i < Capacity; ++i)
{
m_pool.push(i);
}
}
FixedQueue(const FixedQueue& other) = delete;
FixedQueue& operator=(const FixedQueue& other) = delete;
~FixedQueue()
{
m_queue.consume_all(
[this](std::size_t index)
{
auto obj = reinterpret_cast<T*>(&m_storage[index]);
obj->~T();
(void)obj;
});
}
bool IsEmpty() const
{
return m_queue.empty();
}
template <typename U>
bool Push(U&& value)
{
std::size_t index;
if (m_pool.pop(index))
{
try
{
new (&m_storage[index]) T(std::forward<U>(value));
}
catch (...)
{
auto result = m_pool.push(index);
assert(result);
(void)result;
throw;
}
auto result = m_queue.push(index);
assert(result);
return result;
}
return false;
}
boost::optional<T> Pop()
{
boost::optional<T> value;
std::size_t index;
if (m_queue.pop(index))
{
auto obj = reinterpret_cast<T*>(&m_storage[index]);
auto restoreIndex = [&]
{
auto result = m_pool.push(index);
assert(result);
(void)result;
};
try
{
value = std::move(*obj);
obj->~T();
}
catch (...)
{
restoreIndex();
throw;
}
restoreIndex();
}
return value;
}
template <typename Function>
std::size_t ConsumeAll(Function&& func)
{
return m_queue.consume_all(
[this, &func](std::size_t index)
{
auto obj = reinterpret_cast<T*>(&m_storage[index]);
auto restoreIndex = [&]
{
auto result = m_pool.push(index);
assert(result);
(void)result;
};
try
{
func(std::move(*obj));
obj->~T();
}
catch (...)
{
restoreIndex();
throw;
}
restoreIndex();
});
}
private:
using Queue = boost::lockfree::queue<std::size_t, boost::lockfree::capacity<Capacity>>;
Queue m_pool;
Queue m_queue;
std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, Capacity> m_storage;
};
/// Specialization for trivially copyable types.
template <typename T, std::size_t Capacity>
class FixedQueue<T, Capacity, std::enable_if_t<boost::has_trivial_destructor<T>::value && boost::has_trivial_assign<T>::value>>
{
public:
bool IsEmpty() const
{
return m_queue.empty();
}
bool Push(const T& value)
{
return m_queue.push(value);
}
boost::optional<T> Pop()
{
T value;
return m_queue.pop(value) ? boost::optional<T>{ std::move(value) } : boost::none;
}
template <typename Function>
std::size_t ConsumeAll(Function&& func)
{
return m_queue.consume_all([this, &func](T& value) { func(std::move(value)); });
}
private:
boost::lockfree::queue<T, boost::lockfree::capacity<Capacity>> m_queue;
};
} // LockFree
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,228 @@
#pragma once
#include "Queue.h"
#include "ContainerList.h"
#include <memory>
#include <array>
#include <atomic>
#pragma warning(push)
#include <boost/optional.hpp>
#pragma warning(pop)
namespace IPC
{
namespace detail
{
namespace LockFree
{
/// Provides an object pool of any type with dynamically growing capacity
/// where each object is referred by index. Object retrieval and return are
/// lock-free. Capacity growth is synchronized. Can be allocated directly in
/// shared memory as along as T has same capability.
template <typename T, typename Allocator, std::uint32_t BucketSize = 64>
class IndexedObjectPool
{
public:
using Index = std::uint32_t;
IndexedObjectPool()
: IndexedObjectPool{ {} }
{}
explicit IndexedObjectPool(const Allocator& allocator)
: m_buckets{ allocator },
m_pool{ allocator }
{}
/// Retrieves or constructs a new object with index as first argument
/// followed by provided arguments.
/// \returns A pair of object reference and index.
template <typename... Args>
std::pair<T&, Index> Take(Args&&... args)
{
if (auto index = m_pool.Pop())
{
auto item = Get(*index);
assert(item);
item->MarkUsed();
return{ **item, *index };
}
return Construct(std::forward<Args>(args)...);
}
/// Returns the object with given index back to the pool.
bool Return(Index index)
{
return Return(index, [](T&) {});
}
/// Returns the object with given index back to the pool and
/// invokes the function before it becomes available for retrieval.
template <typename Function>
bool Return(Index index, Function&& func)
{
if (auto item = Get(index))
{
if (item->MarkFree())
{
try
{
std::forward<Function>(func)(**item);
}
catch (...)
{
m_pool.Push(index);
throw;
}
m_pool.Push(index);
return true;
}
}
return false;
}
/// Returns all objects back to pool.
void ReturnAll()
{
ReturnAll([](T&) {});
}
/// Returns all objects back to pool and invokes the function
/// before each of them becomes available for retrieval.
template <typename Function>
void ReturnAll(Function&& func)
{
Apply(
[&](auto& item)
{
assert(item);
if (item.MarkFree())
{
func(*item);
}
});
}
private:
class Bucket
{
public:
class Item : public boost::optional<T>
{
public:
void MarkUsed()
{
m_free.clear();
}
bool MarkFree()
{
return !m_free.test_and_set();
}
private:
std::atomic_flag m_free{ ATOMIC_FLAG_INIT };
};
template <typename... Args>
std::pair<T*, Index> Construct(Index offset, Args&&... args)
{
auto index = m_size++;
if (index < BucketSize)
{
auto& item = m_items[index];
index += offset;
item.emplace(index, std::forward<Args>(args)...); // Intentionally waste the slot if .ctor throws.
return{ &*item, index };
}
--m_size;
return{};
}
Item* operator[](Index index)
{
return index < m_size.load(std::memory_order_relaxed) ? &m_items[index] : nullptr;
}
template <typename Function>
void Apply(Function&& func)
{
for (auto it = m_items.begin(), end = it;
it != (end = std::next(m_items.begin(), m_size.load(std::memory_order_relaxed))); )
{
for (; it != end; ++it)
{
if (auto& item = *it) // Item might be optional::empty due to exception in .ctor.
{
func(item);
}
}
}
}
private:
std::array<Item, BucketSize> m_items{};
std::atomic_uint32_t m_size{ 0 };
};
typename Bucket::Item* Get(Index index)
{
typename Bucket::Item* item{ nullptr };
auto search = [&item, i = Index{}, itemIndex = index % BucketSize, bucketIndex = index / BucketSize](auto& bucket) mutable
{
if (i == bucketIndex)
{
item = bucket[itemIndex];
return true;
}
++i;
return false;
};
return m_buckets.TryApply(search) ? item : nullptr;
}
template <typename... Args>
__declspec(noinline) std::pair<T&, Index> Construct(Args&&... args)
{
std::pair<T*, Index> result;
m_buckets.Apply(
[&, i = Index{}](auto& bucket) mutable
{
result = bucket.Construct(i++ * BucketSize, std::forward<decltype(args)>(args)...); // TODO: Use Args when VC14 bugs are fixed.
return result.first != nullptr;
});
assert(result.first);
return{ *result.first, result.second };
}
template <typename Function>
void Apply(Function&& func)
{
m_buckets.TryApply([&](auto& bucket) { bucket.Apply(func); return false; });
}
ContainerList<Bucket, Allocator> m_buckets;
Queue<Index, Allocator, BucketSize> m_pool;
};
} // LockFree
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,79 @@
#pragma once
#include "QueueFwd.h"
#include "FixedQueue.h"
#include "ContainerList.h"
#include <new>
#pragma warning(push)
#include <boost/interprocess/exceptions.hpp>
#pragma warning(pop)
namespace IPC
{
namespace detail
{
namespace LockFree
{
/// Provides a multi-reader/multi-writer queue with dynamically growing capacity
/// where pushing and popping is lock-free and capacity growth is synchronized.
/// Supports any element type. Can also be allocated directly in shared memory
/// as along as T has same capability.
template <typename T, typename Allocator, std::size_t BucketSize>
class Queue
{
public:
explicit Queue(const Allocator& allocator)
: m_queues{ allocator }
{}
bool IsEmpty() const
{
return !m_queues.TryApply([](const auto& queue) { return !queue.IsEmpty(); });
}
template <typename U>
bool Push(U&& value)
{
try
{
m_queues.Apply([&](auto& queue) { return queue.Push(std::forward<U>(value)); }); // Push won't move out the value on failure.
return true;
}
catch (const boost::interprocess::bad_alloc& /*e*/)
{
return false;
}
catch (const std::bad_alloc& /*e*/)
{
return false;
}
}
boost::optional<T> Pop()
{
boost::optional<T> result;
m_queues.TryApply([&](auto& queue) { return static_cast<bool>(result = queue.Pop()); });
return result;
}
template <typename Function>
std::size_t ConsumeAll(Function&& func)
{
std::size_t count{ 0 };
m_queues.TryApply([&](auto& queue) { count += queue.ConsumeAll(func); return false; });
return count;
}
private:
ContainerList<FixedQueue<T, BucketSize>, Allocator> m_queues;
};
} // LockFree
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,17 @@
#pragma once
#include <cstdlib>
namespace IPC
{
namespace detail
{
namespace LockFree
{
template <typename T, typename Allocator, std::size_t BucketSize = 64>
class Queue;
} // LockFree
} // detail
} // IPC

100
Inc/IPC/detail/Packet.h Normal file
Просмотреть файл

@ -0,0 +1,100 @@
#pragma once
#include "PacketFwd.h"
#pragma warning(push)
#include <boost/optional.hpp>
#pragma warning(pop)
namespace IPC
{
namespace detail
{
template <typename T>
class RequestPacket<T, void>
{
public:
RequestPacket() = default;
RequestPacket(const T& payload)
: m_payload{ payload }
{}
RequestPacket(T&& payload)
: m_payload{ std::move(payload) }
{}
T& GetPayload()
{
return m_payload;
}
private:
T m_payload{};
};
template <typename T, typename Id>
class RequestPacket : public RequestPacket<T, void>
{
using Base = RequestPacket<T, void>;
public:
RequestPacket() = default;
template <typename U>
RequestPacket(const Id& id, U&& payload)
: Base{ std::forward<U>(payload) },
m_id{ id }
{}
const Id& GetId() const
{
return m_id;
}
private:
Id m_id{};
};
template <typename T, typename Id>
class ResponsePacket
{
public:
ResponsePacket() = default;
template <typename U>
ResponsePacket(const Id& id, U&& payload)
: m_id{ id },
m_payload{ std::forward<U>(payload) }
{}
ResponsePacket(const Id& id)
: m_id{ id }
{}
const Id& GetId() const
{
return m_id;
}
bool IsValid() const
{
return static_cast<bool>(m_payload);
}
T& GetPayload()
{
assert(IsValid());
return *m_payload;
}
private:
Id m_id{};
boost::optional<T> m_payload;
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,15 @@
#pragma once
#include "IPC/ConnectionFwd.h"
namespace IPC
{
namespace detail
{
template <typename PacketTraits>
using PacketConnection = Connection<typename PacketTraits::InputPacket, typename PacketTraits::OutputPacket, typename PacketTraits::Traits>;
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,47 @@
#pragma once
#include "ConnectionHolder.h"
#include <memory>
#include <type_traits>
namespace IPC
{
namespace detail
{
template <typename PacketTraits>
class PacketConnectionHolder
: public ConnectionHolder<typename PacketTraits::InputPacket, typename PacketTraits::OutputPacket, typename PacketTraits::Traits>,
protected PacketTraits,
protected PacketTraits::Context
{
using Context = typename PacketTraits::Context;
public:
~PacketConnectionHolder()
{
this->GetConnection().Close();
}
protected:
template <typename... Handlers>
PacketConnectionHolder(std::unique_ptr<typename PacketConnectionHolder::Connection> connection, Context context, Handlers&&... handlers)
: ConnectionHolder{ std::move(connection) },
Context{ std::move(context) }
{
this->Register(std::forward<Handlers>(handlers)...);
}
const Context& GetContext() const
{
return *this;
}
Context& GetContext()
{
return *this;
}
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,59 @@
#pragma once
#include <type_traits>
namespace IPC
{
namespace detail
{
template <typename T, typename Id>
class RequestPacket;
template <typename T, typename Id>
class ResponsePacket;
template <typename Request, typename Response, typename Traits>
struct PacketInfo
{
using RequestPacket = RequestPacket<Request, typename Traits::PacketId>;
using ResponsePacket = ResponsePacket<Response, typename Traits::PacketId>;
};
template <typename Request, typename Traits>
struct PacketInfo<Request, void, Traits>
{
using RequestPacket = RequestPacket<Request, void>;
using ResponsePacket = void;
};
template <typename Response, typename Traits>
struct PacketInfo<void, Response, Traits>
{
static_assert(!std::is_same<Response, Response>::value, "Request cannot be void.");
};
template <typename RequestT, typename ResponseT, typename TraitsT>
struct ClientPacketInfo
{
using Request = RequestT;
using Response = ResponseT;
using Traits = TraitsT;
using InputPacket = typename PacketInfo<Request, Response, Traits>::ResponsePacket;
using OutputPacket = typename PacketInfo<Request, Response, Traits>::RequestPacket;
};
template <typename Request, typename Response, typename Traits>
struct ServerPacketInfo : ClientPacketInfo<Request, Response, Traits>
{
using InputPacket = typename PacketInfo<Request, Response, Traits>::RequestPacket;
using OutputPacket = typename PacketInfo<Request, Response, Traits>::ResponsePacket;
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,13 @@
#pragma once
#include <string>
namespace IPC
{
namespace detail
{
std::string GenerateRandomString();
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,26 @@
#pragma once
#include "SpinLock.h"
#include <atomic>
#include <thread>
namespace IPC
{
namespace detail
{
class RecursiveSpinLock
{
public:
void lock();
void unlock();
private:
SpinLock m_lock;
std::atomic<std::thread::id> m_ownerId{};
volatile std::size_t m_lockCount{ 0 };
};
} // detail
} // IPC

Просмотреть файл

@ -0,0 +1,47 @@
#pragma once
#include "Alias.h"
#include "IPC/SharedMemory.h"
namespace IPC
{
namespace detail
{
template <typename T>
class SharedObject : public SharedMemory::SharedPtr<T>
{
using SharedPtr = SharedMemory::SharedPtr<T>;
public:
template <typename... Args>
SharedObject(create_only_t, SharedMemory& memory, const char* name, Args&&... args)
: m_owner{ Create(memory, name, std::forward<Args>(args)...) }
{
SharedPtr::operator=(*m_owner);
}
SharedObject(open_only_t, SharedMemory& memory, const char* name)
: SharedPtr{ Open(memory, name) }
{}
private:
template <typename... Args>
static auto Create(SharedMemory& memory, const char* name, Args&&... args)
{
auto obj = memory.MakeShared<T>(anonymous_instance, std::forward<Args>(args)...);
return name ? memory.MakeShared<SharedPtr>(name, std::move(obj)) : memory.MakeShared<SharedPtr>(unique_instance, std::move(obj));
}
static auto Open(SharedMemory& memory, const char* name)
{
SharedPtr obj;
memory.InvokeAtomic([&] { obj = name ? memory.Find<SharedPtr>(name) : memory.Find<SharedPtr>(unique_instance); });
return obj;
}
SharedMemory::SharedPtr<SharedPtr> m_owner;
};
} // detail
} // IPC

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше