зеркало из https://github.com/microsoft/IPC.git
Initial commit.
This commit is contained in:
Родитель
0b4a694632
Коммит
f3050e22c5
|
@ -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
|
||||
|
|
|
@ -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
|
Двоичный файл не отображается.
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
struct DefaultTraits;
|
||||
|
||||
template <typename Input, typename Output, typename Traits = DefaultTraits>
|
||||
class Connection;
|
||||
|
||||
} // IPC
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace IPC
|
||||
{
|
||||
class Exception : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
} // IPC
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче