diff --git a/build/Mlos.NetCore.Grpc.props b/build/Mlos.NetCore.Grpc.props
index 5b56d1a12f..8168211ac7 100644
--- a/build/Mlos.NetCore.Grpc.props
+++ b/build/Mlos.NetCore.Grpc.props
@@ -4,34 +4,41 @@
$(BaseDir)\out\Grpc.out\$(GrpcServices)\$(BuildType)
2.29.0
+ 3.12.0
-
-
+
-
-
-
-
+
-
-
-
+
-
-
+
+
+
+
+ $([System.IO.Path]::GetFullPath('$(BaseDir)\source\Mlos.Grpc.Binplace\Mlos.Grpc.Binplace.csproj'))
+ $([System.IO.Path]::GetFullPath('$(MSBuildProjectFullPath)'))
+
+
+
+
diff --git a/source/Examples/SmartCache/Main.cpp b/source/Examples/SmartCache/Main.cpp
index c6b82aefa3..fe3b596c75 100644
--- a/source/Examples/SmartCache/Main.cpp
+++ b/source/Examples/SmartCache/Main.cpp
@@ -43,7 +43,7 @@ using namespace SmartCache;
// HRESULTs are an error code encoding mechanism typically used in Windows environments.
// See Also: https://en.wikipedia.org/wiki/HRESULT
//
-void CheckHR(HRESULT hr)
+void ThrowIfFail(HRESULT hr)
{
if (FAILED(hr))
{
@@ -74,7 +74,7 @@ main(
//
Mlos::Core::InterProcessMlosContextInitializer mlosContextInitializer;
HRESULT hr = mlosContextInitializer.Initialize();
- CheckHR(hr);
+ ThrowIfFail(hr);
Mlos::Core::InterProcessMlosContext mlosContext(std::move(mlosContextInitializer));
@@ -124,7 +124,7 @@ main(
hr = mlosContext.RegisterSettingsAssembly(
"SmartCache.SettingsRegistry.dll",
SmartCache::ObjectDeserializationHandler::DispatchTableBaseIndex());
- CheckHR(hr);
+ ThrowIfFail(hr);
// Create a component configuration object.
// This will be stored in a shared memory region below for use by both the
@@ -145,7 +145,7 @@ main(
// config for this component and if not creates it.
//
hr = mlosContext.RegisterComponentConfig(config);
- CheckHR(hr);
+ ThrowIfFail(hr);
// Create an instance of our SmartCache component to tune.
//
diff --git a/source/Examples/SmartCache/SmartCache.sln b/source/Examples/SmartCache/SmartCache.sln
index 2742b4cfee..e1de60c248 100644
--- a/source/Examples/SmartCache/SmartCache.sln
+++ b/source/Examples/SmartCache/SmartCache.sln
@@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SmartCache", "SmartCache.vcxproj", "{0CE12F0A-E7B5-4E52-B6DF-E9BA1F525030}"
ProjectSection(ProjectDependencies) = postProject
{E4407270-6E64-4E87-A4B7-3B932121BC81} = {E4407270-6E64-4E87-A4B7-3B932121BC81}
+ {5DB4C179-4A97-40F9-8155-8C44B2BE416D} = {5DB4C179-4A97-40F9-8155-8C44B2BE416D}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartCache.SettingsRegistry", "SmartCache.SettingsRegistry\SmartCache.SettingsRegistry.csproj", "{E4407270-6E64-4E87-A4B7-3B932121BC81}"
diff --git a/source/Mlos.Agent.Server/MlosAgentServer.cs b/source/Mlos.Agent.Server/MlosAgentServer.cs
index 5fbce6837a..d3aef5bb59 100644
--- a/source/Mlos.Agent.Server/MlosAgentServer.cs
+++ b/source/Mlos.Agent.Server/MlosAgentServer.cs
@@ -104,10 +104,15 @@ public static void Main(string[] args)
MlosContext.OptimizerFactory = new MlosOptimizer.BayesianOptimizerFactory(optimizerAddressUri);
}
- // Create (or open) the circular buffer shared memory before running the target process.
+ // In the active learning mode, create a new shared memory map before running the target process.
+ // On Linux, we unlink existing shared memory map, if they exist.
+ // If the agent is not in the active learning mode, create new or open existing to communicate with the target process.
//
+ using MlosContext mlosContext = (executableFilePath != null)
+ ? InterProcessMlosContext.Create()
+ : InterProcessMlosContext.CreateOrOpen();
using var mainAgent = new MainAgent();
- mainAgent.InitializeSharedChannel();
+ mainAgent.InitializeSharedChannel(mlosContext);
// Active learning mode.
//
@@ -153,7 +158,9 @@ public static void Main(string[] args)
Console.WriteLine("Starting Mlos.Agent");
Task mlosAgentTask = Task.Factory.StartNew(
() => mainAgent.RunAgent(),
- TaskCreationOptions.LongRunning);
+ CancellationToken.None,
+ TaskCreationOptions.LongRunning,
+ TaskScheduler.Current);
Task waitForTargetProcessTask = Task.Factory.StartNew(
() =>
@@ -165,13 +172,15 @@ public static void Main(string[] args)
mainAgent.UninitializeSharedChannel();
}
},
- TaskCreationOptions.LongRunning);
+ CancellationToken.None,
+ TaskCreationOptions.LongRunning,
+ TaskScheduler.Current);
Console.WriteLine("Waiting for Mlos.Agent to exit");
while (true)
{
- Task.WaitAny(new[] { mlosAgentTask, waitForTargetProcessTask });
+ Task.WaitAny(mlosAgentTask, waitForTargetProcessTask);
if (mlosAgentTask.IsFaulted && targetProcessManager != null && !waitForTargetProcessTask.IsCompleted)
{
diff --git a/source/Mlos.Agent.Server/TargetProcessManager.cs b/source/Mlos.Agent.Server/TargetProcessManager.cs
index 9e4e736f21..9090977ae5 100644
--- a/source/Mlos.Agent.Server/TargetProcessManager.cs
+++ b/source/Mlos.Agent.Server/TargetProcessManager.cs
@@ -7,9 +7,7 @@
// -----------------------------------------------------------------------
using System;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Text;
namespace Mlos.Agent.Server
{
@@ -22,9 +20,10 @@ namespace Mlos.Agent.Server
internal class TargetProcessManager : IDisposable
{
private readonly string executableFilePath;
+
private Process targetProcess;
- private bool disposed = false;
+ private bool isDisposed;
public TargetProcessManager(string executableFilePath)
{
@@ -40,18 +39,15 @@ public void Dispose()
private void Dispose(bool disposing)
{
- if (disposed)
+ if (isDisposed || !disposing)
{
return;
}
- if (disposing)
- {
- targetProcess?.Dispose();
- targetProcess = null;
- }
+ targetProcess?.Dispose();
+ targetProcess = null;
- disposed = true;
+ isDisposed = true;
}
public void StartTargetProcess()
diff --git a/source/Mlos.Agent/MainAgent.cs b/source/Mlos.Agent/MainAgent.cs
index f4ca52d861..b350f2b020 100644
--- a/source/Mlos.Agent/MainAgent.cs
+++ b/source/Mlos.Agent/MainAgent.cs
@@ -13,7 +13,6 @@
using System.Runtime.InteropServices;
using Mlos.Core;
-using Proxy.Mlos.Core;
using MlosProxy = Proxy.Mlos.Core;
using MlosProxyInternal = Proxy.Mlos.Core.Internal;
@@ -28,18 +27,6 @@ namespace Mlos.Agent
///
public class MainAgent : IDisposable
{
- ///
- /// Shared memory mapping name must start with "Host_" prefix, to be accessible from certain applications.
- /// TODO: Make these config regions configurable to support multiple processes.
- ///
- private const string GlobalMemoryMapName = "Host_Mlos.GlobalMemory";
- private const string ControlChannelMemoryMapName = "Host_Mlos.ControlChannel";
- private const string FeedbackChannelMemoryMapName = "Host_Mlos.FeedbackChannel";
- private const string ControlChannelSemaphoreName = @"Global\ControlChannel_Event"; //// FIXME: Use non-backslashes for Linux environments.
- private const string FeedbackChannelSemaphoreName = @"Global\FeedbackChannel_Event";
- private const string SharedConfigMemoryMapName = "Host_Mlos.Config.SharedMemory";
- private const int SharedMemorySize = 65536;
-
private readonly SettingsAssemblyManager settingsAssemblyManager = new SettingsAssemblyManager();
private readonly Dictionary memoryRegions = new Dictionary();
@@ -53,15 +40,9 @@ public class MainAgent : IDisposable
private bool isDisposed;
#region Shared objects
- private SharedMemoryRegionView globalMemoryRegionView;
-
- private SharedMemoryMapView controlChannelMemoryMapView;
- private SharedMemoryMapView feedbackChannelMemoryMapView;
- private NamedEvent controlChannelNamedEvent;
- private NamedEvent feedbackChannelNamedEvent;
+ private MlosContext mlosContext;
- private SharedMemoryRegionView sharedConfigMemoryMapView;
#endregion
#region Mlos.Agent setup
@@ -69,48 +50,17 @@ public class MainAgent : IDisposable
///
/// Initialize shared channel.
///
- public void InitializeSharedChannel()
+ /// Mlos context instance.
+ public void InitializeSharedChannel(MlosContext mlosContext)
{
- // Create or open the memory mapped files.
- //
- Console.WriteLine("Create or open memory mapped files");
- globalMemoryRegionView = SharedMemoryRegionView.CreateOrOpen(GlobalMemoryMapName, SharedMemorySize);
- controlChannelMemoryMapView = SharedMemoryMapView.CreateOrOpen(ControlChannelMemoryMapName, SharedMemorySize);
- feedbackChannelMemoryMapView = SharedMemoryMapView.CreateOrOpen(FeedbackChannelMemoryMapName, SharedMemorySize);
- sharedConfigMemoryMapView = SharedMemoryRegionView.CreateOrOpen(SharedConfigMemoryMapName, SharedMemorySize);
-
- // Create channel synchronization primitives.
- //
- Console.WriteLine("Create channel synchronization primitives");
- controlChannelNamedEvent = NamedEvent.CreateOrOpen(ControlChannelSemaphoreName);
- feedbackChannelNamedEvent = NamedEvent.CreateOrOpen(FeedbackChannelSemaphoreName);
-
- // Setup feedback channel.
- //
- Console.WriteLine("Setup feedback channel");
-
- MlosProxyInternal.GlobalMemoryRegion globalMemoryRegion = globalMemoryRegionView.MemoryRegion();
-
- Console.WriteLine("Setup feedback channel");
-
-
- var feedbackChannel = new SharedChannel(
- buffer: feedbackChannelMemoryMapView.Buffer,
- size: (uint)feedbackChannelMemoryMapView.MemSize,
- sync: globalMemoryRegion.FeedbackChannelSynchronization)
- {
- ChannelPolicy = { NotificationEvent = feedbackChannelNamedEvent },
- };
+ this.mlosContext = mlosContext;
// Set SharedConfig memory region.
//
- Console.WriteLine("Set SharedConfig memory region.");
-
- sharedConfigManager.SetMemoryRegion(new MlosProxyInternal.SharedConfigMemoryRegion { Buffer = sharedConfigMemoryMapView.MemoryRegion().Buffer });
+ sharedConfigManager.SetMemoryRegion(new MlosProxyInternal.SharedConfigMemoryRegion { Buffer = mlosContext.SharedConfigMemoryRegion.Buffer });
// Setup MlosContext.
//
- MlosContext.FeedbackChannel = feedbackChannel;
MlosContext.SharedConfigManager = sharedConfigManager;
// Initialize callbacks.
@@ -129,7 +79,7 @@ public void InitializeSharedChannel()
// Register assemblies from the shared config.
// Assembly Mlos.NetCore does not have a config, as it is always registered first.
//
- for (uint index = 1; index < globalMemoryRegion.RegisteredSettingsAssemblyCount.Load(); index++)
+ for (uint index = 1; index < mlosContext.GlobalMemoryRegion.RegisteredSettingsAssemblyCount.Load(); index++)
{
RegisterSettingsAssembly(assemblyIndex: index);
}
@@ -144,8 +94,8 @@ public void UninitializeSharedChannel()
// Signal named event to close any waiter threads.
//
- controlChannelNamedEvent.Signal();
- feedbackChannelNamedEvent.Signal();
+ mlosContext.TerminateControlChannel();
+ mlosContext.TerminateFeedbackChannel();
}
///
@@ -301,15 +251,13 @@ private void RegisterSharedConfigMemoryRegionRequestMessageCallback(MlosProxyInt
///
/// #TODO remove, this is not required.
- /// set the terminate channel in sync object and signal.
///
///
- private void TerminateReaderThreadRequestMessageCallback(TerminateReaderThreadRequestMessage msg)
+ private void TerminateReaderThreadRequestMessageCallback(MlosProxy.TerminateReaderThreadRequestMessage msg)
{
// Terminate the channel.
//
- MlosProxyInternal.GlobalMemoryRegion globalMemoryRegion = globalMemoryRegionView.MemoryRegion();
- ChannelSynchronization controlChannelSync = globalMemoryRegion.ControlChannelSynchronization;
+ MlosProxy.ChannelSynchronization controlChannelSync = mlosContext.GlobalMemoryRegion.ControlChannelSynchronization;
controlChannelSync.TerminateChannel.Store(true);
}
@@ -320,20 +268,9 @@ private void TerminateReaderThreadRequestMessageCallback(TerminateReaderThreadRe
///
public void RunAgent()
{
- // Create the shared memory control channel.
- //
- var globalMemoryRegion = globalMemoryRegionView.MemoryRegion();
- var controlChannel = new SharedChannel(
- buffer: controlChannelMemoryMapView.Buffer,
- size: (uint)controlChannelMemoryMapView.MemSize,
- sync: globalMemoryRegion.ControlChannelSynchronization)
- {
- ChannelPolicy = { NotificationEvent = controlChannelNamedEvent },
- };
-
// Process the messages from the control channel.
//
- controlChannel.ProcessMessages(dispatchTable: ref globalDispatchTable);
+ MlosContext.ControlChannel.ProcessMessages(dispatchTable: ref globalDispatchTable);
}
protected virtual void Dispose(bool disposing)
@@ -343,32 +280,16 @@ protected virtual void Dispose(bool disposing)
return;
}
- // Close shared memory.
+ // Dispose MlosContext.
//
- globalMemoryRegionView?.Dispose();
- globalMemoryRegionView = null;
-
- controlChannelMemoryMapView?.Dispose();
- controlChannelMemoryMapView = null;
-
- feedbackChannelMemoryMapView?.Dispose();
- feedbackChannelMemoryMapView = null;
-
- sharedConfigMemoryMapView?.Dispose();
- sharedConfigMemoryMapView = null;
-
- controlChannelNamedEvent?.Dispose();
- controlChannelNamedEvent = null;
-
- feedbackChannelNamedEvent?.Dispose();
- feedbackChannelNamedEvent = null;
+ mlosContext?.Dispose();
+ mlosContext = null;
isDisposed = true;
}
public void Dispose()
{
- // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
diff --git a/source/Mlos.Core/CMakeLists.txt b/source/Mlos.Core/CMakeLists.txt
index 231c0e4c51..6bcf6dd5e2 100644
--- a/source/Mlos.Core/CMakeLists.txt
+++ b/source/Mlos.Core/CMakeLists.txt
@@ -6,6 +6,8 @@ include("${MLOS_ROOT}/build/Mlos.Cpp.cmake")
add_library(${PROJECT_NAME} STATIC
GlobalMemoryRegion.cpp
+ InternalMlosContext.cpp
+ InterProcessMlosContext.cpp
Mlos.Core.cpp
MlosContext.cpp
NamedEvent.Linux.cpp
diff --git a/source/Mlos.Core/InterProcessMlosContext.cpp b/source/Mlos.Core/InterProcessMlosContext.cpp
new file mode 100644
index 0000000000..3db14d2509
--- /dev/null
+++ b/source/Mlos.Core/InterProcessMlosContext.cpp
@@ -0,0 +1,152 @@
+//*********************************************************************
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root
+// for license information.
+//
+// @File: InterProcessMlosContext.cpp
+//
+// Purpose:
+//
+//
+// Notes:
+//
+//
+//*********************************************************************
+
+#include "Mlos.Core.h"
+#include "Mlos.Core.inl"
+
+namespace Mlos
+{
+namespace Core
+{
+//----------------------------------------------------------------------------
+// NAME: InterProcessMlosContextInitializer::Constructor.
+//
+// PURPOSE:
+// Move constructor.
+//
+// NOTES:
+//
+InterProcessMlosContextInitializer::InterProcessMlosContextInitializer(InterProcessMlosContextInitializer&& initializer) noexcept
+ : m_globalMemoryRegionView(std::move(initializer.m_globalMemoryRegionView)),
+ m_controlChannelMemoryMapView(std::move(initializer.m_controlChannelMemoryMapView)),
+ m_feedbackChannelMemoryMapView(std::move(initializer.m_feedbackChannelMemoryMapView)),
+ m_controlChannelPolicy(std::move(initializer.m_controlChannelPolicy)),
+ m_feedbackChannelPolicy(std::move(initializer.m_feedbackChannelPolicy))
+{
+}
+
+//----------------------------------------------------------------------------
+// NAME: InterProcessMlosContextInitializer::Initialize
+//
+// PURPOSE:
+// Opens the shared memory and synchronization primitives used for the communication channel.
+//
+// NOTES:
+//
+_Check_return_
+HRESULT InterProcessMlosContextInitializer::Initialize()
+{
+ // #TODO const as codegen, pass a config struct ?
+ //
+ const size_t SharedMemorySize = 65536;
+
+ // TODO: Make these config regions configurable to support multiple processes.
+ // Note: Shared memory mapping name must start with "Host_" prefix, to be accessible from certain applications.
+ //
+ HRESULT hr = m_globalMemoryRegionView.CreateOrOpen("Host_Mlos.GlobalMemory", SharedMemorySize);
+ if (SUCCEEDED(hr))
+ {
+ // Increase the usage counter. When closing global shared memory, we will decrease the counter.
+ // If there is no process using the shared memory, we will clean the OS resources. On Windows OS,
+ // this is no-op; on Linux, we unlink created files.
+ //
+ Internal::GlobalMemoryRegion& globalMemoryRegion = m_globalMemoryRegionView.MemoryRegion();
+ globalMemoryRegion.AttachedProcessesCount.fetch_add(1);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_controlChannelMemoryMapView.CreateOrOpen("Host_Mlos.ControlChannel", SharedMemorySize);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_feedbackChannelMemoryMapView.CreateOrOpen("Host_Mlos.FeedbackChannel", SharedMemorySize);
+ }
+
+ // FIXME: Use non-backslashes for Linux environments.
+ //
+ if (SUCCEEDED(hr))
+ {
+ hr = m_controlChannelPolicy.m_notificationEvent.CreateOrOpen("Global\\ControlChannel_Event");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_feedbackChannelPolicy.m_notificationEvent.CreateOrOpen("Global\\FeedbackChannel_Event");
+ }
+
+ if (FAILED(hr))
+ {
+ // Close all the shared maps if we fail to create one.
+ //
+ m_globalMemoryRegionView.Close();
+ m_controlChannelMemoryMapView.Close();
+ m_feedbackChannelMemoryMapView.Close();
+ m_controlChannelPolicy.m_notificationEvent.Close();
+ m_feedbackChannelPolicy.m_notificationEvent.Close();
+ }
+
+ return hr;
+}
+
+//----------------------------------------------------------------------------
+// NAME: InterProcessMlosContext::Constructor
+//
+// PURPOSE:
+// Creates InterProcessMlosContext.
+//
+// NOTES:
+//
+InterProcessMlosContext::InterProcessMlosContext(InterProcessMlosContextInitializer&& initializer) noexcept
+ : MlosContext(initializer.m_globalMemoryRegionView.MemoryRegion(), m_controlChannel, m_controlChannel, m_feedbackChannel),
+ m_contextInitializer(std::move(initializer)),
+ m_controlChannel(
+ m_contextInitializer.m_globalMemoryRegionView.MemoryRegion().ControlChannelSynchronization,
+ m_contextInitializer.m_controlChannelMemoryMapView,
+ std::move(m_contextInitializer.m_controlChannelPolicy)),
+ m_feedbackChannel(
+ m_contextInitializer.m_globalMemoryRegionView.MemoryRegion().FeedbackChannelSynchronization,
+ m_contextInitializer.m_feedbackChannelMemoryMapView,
+ std::move(m_contextInitializer.m_feedbackChannelPolicy))
+{
+}
+
+//----------------------------------------------------------------------------
+// NAME: InterProcessMlosContext::Destructor
+//
+// PURPOSE:
+// Destroys InterProcessMlosContext object.
+//
+// NOTES:
+//
+InterProcessMlosContext::~InterProcessMlosContext()
+{
+ // Decrease the usage counter.
+ //
+ uint32_t usageCount = m_globalMemoryRegion.AttachedProcessesCount.fetch_sub(1);
+ if (usageCount == 1)
+ {
+ // This is the last process using shared memory map.
+ //
+ m_contextInitializer.m_globalMemoryRegionView.CleanupOnClose = true;
+ m_contextInitializer.m_controlChannelMemoryMapView.CleanupOnClose = true;
+ m_contextInitializer.m_feedbackChannelMemoryMapView.CleanupOnClose = true;
+ m_controlChannel.ChannelPolicy.m_notificationEvent.CleanupOnClose = true;
+ m_feedbackChannel.ChannelPolicy.m_notificationEvent.CleanupOnClose = true;
+ }
+}
+}
+}
diff --git a/source/Mlos.Core/InterProcessMlosContext.h b/source/Mlos.Core/InterProcessMlosContext.h
new file mode 100644
index 0000000000..b692b06387
--- /dev/null
+++ b/source/Mlos.Core/InterProcessMlosContext.h
@@ -0,0 +1,93 @@
+//*********************************************************************
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root
+// for license information.
+//
+// @File: InterProcessMlosContext.h
+//
+// Purpose:
+//
+//
+// Notes:
+//
+//
+//*********************************************************************
+
+#pragma once
+
+namespace Mlos
+{
+namespace Core
+{
+//----------------------------------------------------------------------------
+// NAME: InterProcessMlosContextInitializer
+//
+// PURPOSE:
+// Helper class used to initialize shared memory for inter-process MlosContexts.
+//
+// NOTES:
+//
+class InterProcessMlosContextInitializer
+{
+public:
+ InterProcessMlosContextInitializer() {}
+
+ _Check_return_
+ HRESULT Initialize();
+
+ InterProcessMlosContextInitializer(InterProcessMlosContextInitializer&& initializer) noexcept;
+
+ InterProcessMlosContextInitializer(const InterProcessMlosContextInitializer&) = delete;
+
+ InterProcessMlosContextInitializer& operator=(const InterProcessMlosContextInitializer&) = delete;
+
+private:
+ // Global shared memory region.
+ //
+ SharedMemoryRegionView m_globalMemoryRegionView;
+
+ // Named shared memory for Telemetry and Control Channel.
+ //
+ SharedMemoryMapView m_controlChannelMemoryMapView;
+
+ // Named shared memory for Feedback Channel.
+ //
+ SharedMemoryMapView m_feedbackChannelMemoryMapView;
+
+ // Channel policy for control channel.
+ //
+ InterProcessSharedChannelPolicy m_controlChannelPolicy;
+
+ // Channel policy for feedback channel.
+ //
+ InterProcessSharedChannelPolicy m_feedbackChannelPolicy;
+
+ friend class InterProcessMlosContext;
+};
+
+//----------------------------------------------------------------------------
+// NAME: InterProcessMlosContext
+//
+// PURPOSE:
+// Implementation of an inter-process MlosContext.
+//
+class InterProcessMlosContext : public MlosContext
+{
+public:
+ InterProcessMlosContext(InterProcessMlosContextInitializer&&) noexcept;
+
+ ~InterProcessMlosContext();
+
+private:
+ InterProcessMlosContextInitializer m_contextInitializer;
+
+ InterProcessSharedChannel m_controlChannel;
+
+ InterProcessSharedChannel m_feedbackChannel;
+
+ NamedEvent m_controlChannelNamedEvent;
+
+ NamedEvent m_feedbackChannelNamedEvent;
+};
+}
+}
diff --git a/source/Mlos.Core/InternalMlosContext.cpp b/source/Mlos.Core/InternalMlosContext.cpp
new file mode 100644
index 0000000000..3d23f5ae00
--- /dev/null
+++ b/source/Mlos.Core/InternalMlosContext.cpp
@@ -0,0 +1,100 @@
+//*********************************************************************
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root
+// for license information.
+//
+// @File: InternalMlosContext.cpp
+//
+// Purpose:
+//
+//
+// Notes:
+//
+//
+//*********************************************************************
+
+#include "Mlos.Core.h"
+#include "Mlos.Core.inl"
+
+namespace Mlos
+{
+namespace Core
+{
+//----------------------------------------------------------------------------
+// NAME: InternalMlosContextInitializer::Constructor.
+//
+// PURPOSE:
+// Move constructor.
+//
+// NOTES:
+//
+InternalMlosContextInitializer::InternalMlosContextInitializer(InternalMlosContextInitializer&& initializer) noexcept
+ : m_globalMemoryRegionView(std::move(initializer.m_globalMemoryRegionView)),
+ m_controlChannelMemoryMapView(std::move(initializer.m_controlChannelMemoryMapView)),
+ m_feedbackChannelMemoryMapView(std::move(initializer.m_feedbackChannelMemoryMapView))
+{
+}
+
+//----------------------------------------------------------------------------
+// NAME: InternalMlosContextInitializer::Initialize
+//
+// PURPOSE:
+// Opens the shared memory used for the communication channel.
+//
+// NOTES:
+//
+_Check_return_
+HRESULT InternalMlosContextInitializer::Initialize()
+{
+ const size_t SharedMemorySize = 65536;
+
+ HRESULT hr = S_OK;
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_globalMemoryRegionView.Create("Test_Mlos.GlobalMemory", SharedMemorySize);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_controlChannelMemoryMapView.Create("Test_SharedChannelMemory", SharedMemorySize);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_feedbackChannelMemoryMapView.Create("Test_FeedbackChannelMemory", SharedMemorySize);
+ }
+
+ if (FAILED(hr))
+ {
+ // Close all the shared maps if we fail to create one.
+ //
+ m_globalMemoryRegionView.Close();
+ m_controlChannelMemoryMapView.Close();
+ m_feedbackChannelMemoryMapView.Close();
+ }
+
+ return hr;
+}
+
+//----------------------------------------------------------------------------
+// NAME: InternalMlosContext::Constructor
+//
+// PURPOSE:
+// Creates InternalMlosContext.
+//
+// NOTES:
+//
+InternalMlosContext::InternalMlosContext(InternalMlosContextInitializer&& initializer) noexcept
+ : MlosContext(initializer.m_globalMemoryRegionView.MemoryRegion(), m_controlChannel, m_controlChannel, m_feedbackChannel),
+ m_contextInitializer(std::move(initializer)),
+ m_controlChannel(
+ m_contextInitializer.m_globalMemoryRegionView.MemoryRegion().ControlChannelSynchronization,
+ m_contextInitializer.m_controlChannelMemoryMapView),
+ m_feedbackChannel(
+ m_contextInitializer.m_globalMemoryRegionView.MemoryRegion().FeedbackChannelSynchronization,
+ m_contextInitializer.m_feedbackChannelMemoryMapView)
+{
+}
+}
+}
diff --git a/source/Mlos.Core/InternalMlosContext.h b/source/Mlos.Core/InternalMlosContext.h
new file mode 100644
index 0000000000..9d0a8addca
--- /dev/null
+++ b/source/Mlos.Core/InternalMlosContext.h
@@ -0,0 +1,84 @@
+//*********************************************************************
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root
+// for license information.
+//
+// @File: InternalMlosContext.h
+//
+// Purpose:
+//
+//
+// Notes:
+//
+//
+//*********************************************************************
+
+#pragma once
+
+namespace Mlos
+{
+namespace Core
+{
+//----------------------------------------------------------------------------
+// NAME: InternalMlosContextInitializer
+//
+// PURPOSE:
+// Helper class used to initialize shared memory for TestMlosContext.
+//
+// NOTES:
+//
+class InternalMlosContextInitializer
+{
+public:
+ InternalMlosContextInitializer() {}
+
+ _Check_return_
+ HRESULT Initialize();
+
+ InternalMlosContextInitializer(InternalMlosContextInitializer&& initializer) noexcept;
+
+ InternalMlosContextInitializer(const InternalMlosContextInitializer&) = delete;
+
+ InternalMlosContextInitializer& operator=(const InternalMlosContextInitializer&) = delete;
+
+private:
+ // Global shared memory region.
+ //
+ SharedMemoryRegionView m_globalMemoryRegionView;
+
+ // Named shared memory for Telemetry and Control Channel.
+ //
+ SharedMemoryMapView m_controlChannelMemoryMapView;
+
+ // Named shared memory for Feedback Channel.
+ //
+ SharedMemoryMapView m_feedbackChannelMemoryMapView;
+
+ friend class InternalMlosContext;
+};
+
+//----------------------------------------------------------------------------
+// NAME: InternalMlosContext
+//
+// PURPOSE:
+// Simple implementation of MlosContext.
+// Single channel used to send control and telemetry messages.
+// Channel does not use OS synchronization primitive, sender and receiver thread should be running inside the same process.
+//
+// NOTES:
+// Intended to use only in the test.
+//
+class InternalMlosContext : public MlosContext
+{
+public:
+ InternalMlosContext(InternalMlosContextInitializer&&) noexcept;
+
+private:
+ InternalMlosContextInitializer m_contextInitializer;
+
+ TestSharedChannel m_controlChannel;
+
+ TestSharedChannel m_feedbackChannel;
+};
+}
+}
diff --git a/source/Mlos.Core/Mlos.Core.h b/source/Mlos.Core/Mlos.Core.h
index 794b0e0ddf..f15d50b93c 100644
--- a/source/Mlos.Core/Mlos.Core.h
+++ b/source/Mlos.Core/Mlos.Core.h
@@ -70,6 +70,7 @@ constexpr int32_t INVALID_FD_VALUE = -1;
#endif
#define UNUSED(x) (void)x
+#define ReturnIfFail(hr) if (FAILED(hr)) return hr;
#include "MlosPlatform.h"
@@ -125,6 +126,8 @@ constexpr int32_t INVALID_FD_VALUE = -1;
// Include Mlos Client API.
//
#include "MlosContext.h"
+#include "InternalMlosContext.h"
+#include "InterProcessMlosContext.h"
#include "StaticSingleton.h"
#include "StaticVector.h"
diff --git a/source/Mlos.Core/Mlos.Core.vcxproj b/source/Mlos.Core/Mlos.Core.vcxproj
index 201d017dcc..df4e141b37 100644
--- a/source/Mlos.Core/Mlos.Core.vcxproj
+++ b/source/Mlos.Core/Mlos.Core.vcxproj
@@ -26,6 +26,8 @@
+
+
Create
@@ -43,6 +45,8 @@
+
+
diff --git a/source/Mlos.Core/Mlos.Core.vcxproj.filters b/source/Mlos.Core/Mlos.Core.vcxproj.filters
index b1e5df186a..0098815f25 100644
--- a/source/Mlos.Core/Mlos.Core.vcxproj.filters
+++ b/source/Mlos.Core/Mlos.Core.vcxproj.filters
@@ -14,7 +14,8 @@
MemoryRegions
-
+
+
@@ -59,6 +60,9 @@
+
+
+
diff --git a/source/Mlos.Core/MlosContext.cpp b/source/Mlos.Core/MlosContext.cpp
index 88ea0eefb7..a1f1b106a0 100644
--- a/source/Mlos.Core/MlosContext.cpp
+++ b/source/Mlos.Core/MlosContext.cpp
@@ -226,165 +226,5 @@ bool MlosContext::IsFeedbackChannelActive()
{
return !(m_feedbackChannel.Sync.TerminateChannel);
}
-
-//----------------------------------------------------------------------------
-// NAME: InternalMlosContextInitializer::Constructor.
-//
-// PURPOSE:
-// Move constructor.
-//
-// NOTES:
-//
-InternalMlosContextInitializer::InternalMlosContextInitializer(InternalMlosContextInitializer&& initializer) noexcept
- : m_globalMemoryRegionView(std::move(initializer.m_globalMemoryRegionView)),
- m_controlChannelMemoryMapView(std::move(initializer.m_controlChannelMemoryMapView)),
- m_feedbackChannelMemoryMapView(std::move(initializer.m_feedbackChannelMemoryMapView))
-{
-}
-
-//----------------------------------------------------------------------------
-// NAME: InternalMlosContextInitializer::Initialize
-//
-// PURPOSE:
-// Opens the shared memory used for the communication channel.
-//
-// NOTES:
-//
-HRESULT InternalMlosContextInitializer::Initialize()
-{
- const size_t SharedMemorySize = 65536;
-
- HRESULT hr = m_globalMemoryRegionView.Create("Test_Mlos.GlobalMemory", SharedMemorySize);
- if (FAILED(hr))
- {
- return hr;
- }
-
- hr = m_controlChannelMemoryMapView.Create("Test_SharedChannelMemory", SharedMemorySize);
- if (FAILED(hr))
- {
- return hr;
- }
-
- hr = m_feedbackChannelMemoryMapView.Create("Test_FeedbackChannelMemory", SharedMemorySize);
- if (FAILED(hr))
- {
- return hr;
- }
-
- return hr;
-}
-
-//----------------------------------------------------------------------------
-// NAME: InternalMlosContext::Constructor
-//
-// PURPOSE:
-// Creates InternalMlosContext.
-//
-// NOTES:
-//
-InternalMlosContext::InternalMlosContext(InternalMlosContextInitializer&& initializer) noexcept
- : MlosContext(initializer.m_globalMemoryRegionView.MemoryRegion(), m_controlChannel, m_controlChannel, m_feedbackChannel),
- m_contextInitializer(std::move(initializer)),
- m_controlChannel(
- m_contextInitializer.m_globalMemoryRegionView.MemoryRegion().ControlChannelSynchronization,
- m_contextInitializer.m_controlChannelMemoryMapView),
- m_feedbackChannel(
- m_contextInitializer.m_globalMemoryRegionView.MemoryRegion().FeedbackChannelSynchronization,
- m_contextInitializer.m_feedbackChannelMemoryMapView)
-{
-}
-
-//----------------------------------------------------------------------------
-// NAME: InterProcessMlosContextInitializer::Constructor.
-//
-// PURPOSE:
-// Move constructor.
-//
-// NOTES:
-//
-InterProcessMlosContextInitializer::InterProcessMlosContextInitializer(InterProcessMlosContextInitializer&& initializer) noexcept
- : m_globalMemoryRegionView(std::move(initializer.m_globalMemoryRegionView)),
- m_controlChannelMemoryMapView(std::move(initializer.m_controlChannelMemoryMapView)),
- m_feedbackChannelMemoryMapView(std::move(initializer.m_feedbackChannelMemoryMapView)),
- m_controlChannelPolicy(std::move(initializer.m_controlChannelPolicy)),
- m_feedbackChannelPolicy(std::move(initializer.m_feedbackChannelPolicy))
-{
-}
-
-//----------------------------------------------------------------------------
-// NAME: InterProcessMlosContextInitializer::Initialize
-//
-// PURPOSE:
-// Opens the shared memory and synchronization primitives used for the communication channel.
-//
-// NOTES:
-//
-HRESULT InterProcessMlosContextInitializer::Initialize()
-{
- // #TODO const as codegen, pass a config struct ?
- //
- const size_t SharedMemorySize = 65536;
-
- // TODO: Make these config regions configurable to support multiple processes.
-
- // Note: Shared memory mapping name must start with "Host_" prefix, to be accessible from certain applications.
-
- HRESULT hr = m_globalMemoryRegionView.CreateOrOpen("Host_Mlos.GlobalMemory", SharedMemorySize);
- if (FAILED(hr))
- {
- return hr;
- }
-
- hr = m_controlChannelMemoryMapView.CreateOrOpen("Host_Mlos.ControlChannel", SharedMemorySize);
- if (FAILED(hr))
- {
- return hr;
- }
-
- hr = m_feedbackChannelMemoryMapView.CreateOrOpen("Host_Mlos.FeedbackChannel", SharedMemorySize);
- if (FAILED(hr))
- {
- return hr;
- }
-
- // FIXME: Use non-backslashes for Linux environments.
-
- hr = m_controlChannelPolicy.m_notificationEvent.CreateOrOpen("Global\\ControlChannel_Event");
- if (FAILED(hr))
- {
- return hr;
- }
-
- hr = m_feedbackChannelPolicy.m_notificationEvent.CreateOrOpen("Global\\FeedbackChannel_Event");
- if (FAILED(hr))
- {
- return hr;
- }
-
- return hr;
-}
-
-//----------------------------------------------------------------------------
-// NAME: InterProcessMlosContext::Constructor
-//
-// PURPOSE:
-// Creates InterProcessMlosContext.
-//
-// NOTES:
-//
-InterProcessMlosContext::InterProcessMlosContext(InterProcessMlosContextInitializer&& initializer) noexcept
- : MlosContext(initializer.m_globalMemoryRegionView.MemoryRegion(), m_controlChannel, m_controlChannel, m_feedbackChannel),
- m_contextInitializer(std::move(initializer)),
- m_controlChannel(
- m_contextInitializer.m_globalMemoryRegionView.MemoryRegion().ControlChannelSynchronization,
- m_contextInitializer.m_controlChannelMemoryMapView,
- std::move(m_contextInitializer.m_controlChannelPolicy)),
- m_feedbackChannel(
- m_contextInitializer.m_globalMemoryRegionView.MemoryRegion().FeedbackChannelSynchronization,
- m_contextInitializer.m_feedbackChannelMemoryMapView,
- std::move(m_contextInitializer.m_feedbackChannelPolicy))
-{
-}
}
}
diff --git a/source/Mlos.Core/MlosContext.h b/source/Mlos.Core/MlosContext.h
index 5751d4dccd..f6c887f282 100644
--- a/source/Mlos.Core/MlosContext.h
+++ b/source/Mlos.Core/MlosContext.h
@@ -75,7 +75,7 @@ class MlosContext
bool IsFeedbackChannelActive();
-private:
+protected:
// Creates a shared memory view and registers it with Mlos Agent.
//
template
@@ -111,130 +111,5 @@ class MlosContext
template
friend class ComponentConfig;
};
-
-//----------------------------------------------------------------------------
-// NAME: InternalMlosContextInitializer
-//
-// PURPOSE:
-// Helper class used to initialize shared memory for TestMlosContext.
-//
-// NOTES:
-//
-class InternalMlosContextInitializer
-{
-public:
- InternalMlosContextInitializer() {}
-
- HRESULT Initialize();
-
- InternalMlosContextInitializer(InternalMlosContextInitializer&& initializer) noexcept;
-
- InternalMlosContextInitializer(const InternalMlosContextInitializer&) = delete;
-
- InternalMlosContextInitializer& operator=(const InternalMlosContextInitializer&) = delete;
-
-public:
- // Global shared memory region.
- //
- SharedMemoryRegionView m_globalMemoryRegionView;
-
- // Named shared memory for Telemetry and Control Channel.
- //
- SharedMemoryMapView m_controlChannelMemoryMapView;
-
- // Named shared memory for Feedback Channel.
- //
- SharedMemoryMapView m_feedbackChannelMemoryMapView;
-};
-
-//----------------------------------------------------------------------------
-// NAME: InternalMlosContext
-//
-// PURPOSE:
-// Simple implementation of MlosContext.
-// Single channel used to send control and telemetry messages.
-// Channel does not use OS synchronization primitive, sender and receiver thread should be running inside the same process.
-//
-// NOTES:
-// Intended to use only in the test.
-//
-class InternalMlosContext : public MlosContext
-{
-public:
- InternalMlosContext(InternalMlosContextInitializer&&) noexcept;
-
-private:
- InternalMlosContextInitializer m_contextInitializer;
-
- TestSharedChannel m_controlChannel;
-
- TestSharedChannel m_feedbackChannel;
-};
-
-//----------------------------------------------------------------------------
-// NAME: InterProcessMlosContextInitializer
-//
-// PURPOSE:
-// Helper class used to initialize shared memory for inter-process MlosContexts.
-//
-// NOTES:
-//
-class InterProcessMlosContextInitializer
-{
-public:
- InterProcessMlosContextInitializer() {}
-
- HRESULT Initialize();
-
- InterProcessMlosContextInitializer(InterProcessMlosContextInitializer&& initializer) noexcept;
-
- InterProcessMlosContextInitializer(const InterProcessMlosContextInitializer&) = delete;
-
- InterProcessMlosContextInitializer& operator=(const InterProcessMlosContextInitializer&) = delete;
-
-public:
- // Global shared memory region.
- //
- SharedMemoryRegionView m_globalMemoryRegionView;
-
- // Named shared memory for Telemetry and Control Channel.
- //
- SharedMemoryMapView m_controlChannelMemoryMapView;
-
- // Named shared memory for Feedback Channel.
- //
- SharedMemoryMapView m_feedbackChannelMemoryMapView;
-
- // Channel policy for control channel.
- //
- InterProcessSharedChannelPolicy m_controlChannelPolicy;
-
- // Channel policy for feedback channel.
- //
- InterProcessSharedChannelPolicy m_feedbackChannelPolicy;
-};
-
-//----------------------------------------------------------------------------
-// NAME: InterProcessMlosContext
-//
-// PURPOSE:
-// Implementation of an inter-process MlosContext.
-//
-class InterProcessMlosContext : public MlosContext
-{
-public:
- InterProcessMlosContext(InterProcessMlosContextInitializer&&) noexcept;
-
-private:
- InterProcessMlosContextInitializer m_contextInitializer;
-
- InterProcessSharedChannel m_controlChannel;
-
- InterProcessSharedChannel m_feedbackChannel;
-
- NamedEvent m_controlChannelNamedEvent;
-
- NamedEvent m_feedbackChannelNamedEvent;
-};
}
}
diff --git a/source/Mlos.Core/NamedEvent.Linux.cpp b/source/Mlos.Core/NamedEvent.Linux.cpp
index 828785c3c6..942c940d3b 100644
--- a/source/Mlos.Core/NamedEvent.Linux.cpp
+++ b/source/Mlos.Core/NamedEvent.Linux.cpp
@@ -15,8 +15,9 @@
#include "Mlos.Core.h"
-#include
#include
+#include
+#include
using namespace Mlos::Core;
@@ -28,7 +29,9 @@ namespace Core
// NAME: NamedEvent::Constructor.
//
NamedEvent::NamedEvent() noexcept
- : m_semaphore(SEM_FAILED)
+ : m_semaphore(SEM_FAILED),
+ m_namedEventName(nullptr),
+ CleanupOnClose(false)
{
}
@@ -39,7 +42,9 @@ NamedEvent::NamedEvent() noexcept
// Move constructor.
//
NamedEvent::NamedEvent(NamedEvent&& namedEvent) noexcept
- : m_semaphore(std::exchange(namedEvent.m_semaphore, SEM_FAILED))
+ : m_semaphore(std::exchange(namedEvent.m_semaphore, SEM_FAILED)),
+ m_namedEventName(std::exchange(namedEvent.m_namedEventName, nullptr)),
+ CleanupOnClose(std::exchange(namedEvent.CleanupOnClose, false))
{
}
@@ -57,6 +62,12 @@ HRESULT NamedEvent::CreateOrOpen(const char* const namedEventName) noexcept
{
HRESULT hr = S_OK;
+ m_namedEventName = strdup(namedEventName);
+ if (m_namedEventName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
m_semaphore = sem_open(namedEventName, O_CREAT, S_IRUSR | S_IWUSR, 0);
if (m_semaphore == SEM_FAILED)
{
@@ -85,10 +96,38 @@ HRESULT NamedEvent::Open(const char* const namedEventName) noexcept
// NAME: NamedEvent::Destructor.
//
NamedEvent::~NamedEvent()
+{
+ Close();
+}
+
+//----------------------------------------------------------------------------
+// NAME: NamedEvent::Close
+//
+// PURPOSE:
+// Closes a named event object.
+//
+void NamedEvent::Close()
{
if (m_semaphore != SEM_FAILED)
{
sem_close(m_semaphore);
+ m_semaphore = SEM_FAILED;
+
+ if (CleanupOnClose)
+ {
+ if (m_namedEventName != nullptr)
+ {
+ sem_unlink(m_namedEventName);
+ }
+
+ CleanupOnClose = false;
+ }
+ }
+
+ if (m_namedEventName != nullptr)
+ {
+ free(m_namedEventName);
+ m_namedEventName = nullptr;
}
}
diff --git a/source/Mlos.Core/NamedEvent.Linux.h b/source/Mlos.Core/NamedEvent.Linux.h
index 35c0746613..c4b39c83e2 100644
--- a/source/Mlos.Core/NamedEvent.Linux.h
+++ b/source/Mlos.Core/NamedEvent.Linux.h
@@ -41,6 +41,10 @@ class NamedEvent
_Check_return_
HRESULT Open(const char* const namedEventName) noexcept;
+ // Closes a named event object.
+ //
+ void Close();
+
// Sets the named event object to the signaled state.
//
_Check_return_
@@ -51,8 +55,14 @@ class NamedEvent
_Check_return_
HRESULT Wait();
+public:
+ // Indicates if we should cleanup OS resources when closing the shared memory map view.
+ //
+ bool CleanupOnClose;
+
private:
sem_t* m_semaphore;
+ char* m_namedEventName;
};
}
diff --git a/source/Mlos.Core/NamedEvent.Window.cpp b/source/Mlos.Core/NamedEvent.Window.cpp
index 32f31216b7..8a45f3a200 100644
--- a/source/Mlos.Core/NamedEvent.Window.cpp
+++ b/source/Mlos.Core/NamedEvent.Window.cpp
@@ -25,7 +25,8 @@ namespace Core
// NAME: NamedEvent::Constructor.
//
NamedEvent::NamedEvent() noexcept
- : m_hEvent(nullptr)
+ : m_hEvent(nullptr),
+ CleanupOnClose(false)
{
}
@@ -36,7 +37,8 @@ NamedEvent::NamedEvent() noexcept
// Move constructor.
//
NamedEvent::NamedEvent(NamedEvent&& namedEvent) noexcept
- : m_hEvent(std::exchange(namedEvent.m_hEvent, nullptr))
+ : m_hEvent(std::exchange(namedEvent.m_hEvent, nullptr)),
+ CleanupOnClose(std::exchange(namedEvent.CleanupOnClose, false))
{
}
@@ -101,7 +103,7 @@ HRESULT NamedEvent::CreateOrOpen(const char* const namedEventName) noexcept
// NAME: NamedEvent::Open
//
// PURPOSE:
-// Opens a named event object.
+// Opens an existing named event object.
//
// RETURNS:
// HRESULT.
@@ -141,6 +143,10 @@ HRESULT NamedEvent::Open(const char* const namedEventName) noexcept
//
void NamedEvent::Close()
{
+ // Windows OS will remove the named event once the last process using it will terminate, just reset the flag.
+ //
+ CleanupOnClose = false;
+
CloseHandle(m_hEvent);
m_hEvent = nullptr;
}
diff --git a/source/Mlos.Core/NamedEvent.Window.h b/source/Mlos.Core/NamedEvent.Window.h
index 1ca5ff5b21..8ac5aa8901 100644
--- a/source/Mlos.Core/NamedEvent.Window.h
+++ b/source/Mlos.Core/NamedEvent.Window.h
@@ -33,7 +33,7 @@ class NamedEvent
_Check_return_
HRESULT CreateOrOpen(const char* const namedEventName) noexcept;
- // Opens a named event object.
+ // Opens an existing named event object.
//
_Check_return_
HRESULT Open(const char* const namedEventName) noexcept;
@@ -56,6 +56,11 @@ class NamedEvent
//
bool IsInvalid();
+ // Indicates if we should cleanup OS resources when closing the shared memory map view.
+ // No-op on Windows.
+ //
+ bool CleanupOnClose;
+
private:
HANDLE m_hEvent;
};
diff --git a/source/Mlos.Core/SharedMemoryMapView.Linux.cpp b/source/Mlos.Core/SharedMemoryMapView.Linux.cpp
index c3c0c95078..3e371650b9 100644
--- a/source/Mlos.Core/SharedMemoryMapView.Linux.cpp
+++ b/source/Mlos.Core/SharedMemoryMapView.Linux.cpp
@@ -16,19 +16,33 @@
#include "Mlos.Core.h"
#include
-#include
+#include
+#include
#include
+#include
-using namespace Mlos::Core;
-
+namespace Mlos
+{
+namespace Core
+{
//----------------------------------------------------------------------------
// NAME: SharedMemoryMapView::Constructor.
//
SharedMemoryMapView::SharedMemoryMapView() noexcept
: MemSize(0),
m_fdSharedMemory(INVALID_FD_VALUE),
- Buffer(nullptr)
+ m_sharedMemoryMapName(nullptr),
+ Buffer(nullptr),
+ CleanupOnClose(false)
+{
+}
+
+//----------------------------------------------------------------------------
+// NAME: SharedMemoryMapView::Destructor
+//
+SharedMemoryMapView::~SharedMemoryMapView()
{
+ Close();
}
//----------------------------------------------------------------------------
@@ -40,7 +54,9 @@ SharedMemoryMapView::SharedMemoryMapView() noexcept
SharedMemoryMapView::SharedMemoryMapView(SharedMemoryMapView&& sharedMemoryMapView) noexcept :
MemSize(std::exchange(sharedMemoryMapView.MemSize, 0)),
m_fdSharedMemory(std::exchange(sharedMemoryMapView.m_fdSharedMemory, INVALID_FD_VALUE)),
- Buffer(std::exchange(sharedMemoryMapView.Buffer, nullptr))
+ m_sharedMemoryMapName(std::exchange(sharedMemoryMapView.m_sharedMemoryMapName, nullptr)),
+ Buffer(std::exchange(sharedMemoryMapView.Buffer, nullptr)),
+ CleanupOnClose(std::exchange(sharedMemoryMapView.CleanupOnClose, 0))
{
}
@@ -59,6 +75,12 @@ HRESULT SharedMemoryMapView::Create(const char* const sharedMemoryMapName, size_
{
shm_unlink(sharedMemoryMapName);
+ m_sharedMemoryMapName = strdup(sharedMemoryMapName);
+ if (m_sharedMemoryMapName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
m_fdSharedMemory = shm_open(sharedMemoryMapName, O_EXCL | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
return MapMemoryView(memSize);
@@ -77,6 +99,12 @@ HRESULT SharedMemoryMapView::Create(const char* const sharedMemoryMapName, size_
//
HRESULT SharedMemoryMapView::CreateOrOpen(const char* const sharedMemoryMapName, size_t memSize) noexcept
{
+ m_sharedMemoryMapName = strdup(sharedMemoryMapName);
+ if (m_sharedMemoryMapName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
m_fdSharedMemory = shm_open(sharedMemoryMapName, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
return MapMemoryView(memSize);
@@ -95,38 +123,112 @@ HRESULT SharedMemoryMapView::CreateOrOpen(const char* const sharedMemoryMapName,
//
HRESULT SharedMemoryMapView::Open(const char* const sharedMemoryMapName) noexcept
{
+ m_sharedMemoryMapName = strdup(sharedMemoryMapName);
+ if (m_sharedMemoryMapName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
m_fdSharedMemory = shm_open(sharedMemoryMapName, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+ if (m_fdSharedMemory == INVALID_FD_VALUE)
+ {
+ return HRESULT_FROM_ERRNO(errno);
+ }
return MapMemoryView(0 /* memSize */);
}
HRESULT SharedMemoryMapView::MapMemoryView(size_t memSize) noexcept
{
- if (m_fdSharedMemory == -1)
+ if (m_fdSharedMemory == INVALID_FD_VALUE)
{
return HRESULT_FROM_ERRNO(errno);
}
- if (ftruncate(m_fdSharedMemory, memSize) == -1)
+ HRESULT hr = S_OK;
+
+ if (memSize == 0)
{
- return HRESULT_FROM_ERRNO(errno);
+ // Obtain the size of the shared map.
+ //
+ struct stat statBuffer = { 0 };
+ if (fstat(m_fdSharedMemory, &statBuffer) != -1)
+ {
+ memSize = statBuffer.st_size;
+ }
+ else
+ {
+ hr = HRESULT_FROM_ERRNO(errno);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (ftruncate(m_fdSharedMemory, memSize) == -1)
+ {
+ hr = HRESULT_FROM_ERRNO(errno);
+ }
}
- Buffer = mmap(0, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_fdSharedMemory, 0);
+ if (SUCCEEDED(hr))
+ {
+ void* pointer = mmap(0, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_fdSharedMemory, 0);
+ if (pointer != MAP_FAILED)
+ {
+ Buffer.Pointer = reinterpret_cast(pointer);
+ MemSize = memSize;
+ }
+ else
+ {
+ hr = HRESULT_FROM_ERRNO(errno);
+ }
+ }
- //#handle failure
- MemSize = memSize;
+ if (FAILED(hr))
+ {
+ Close();
+ }
- return S_OK;
+ return hr;
}
//----------------------------------------------------------------------------
-// NAME: SharedMemoryMapView::Destructor
+// NAME: SharedMemoryMapView::Close
//
-SharedMemoryMapView::~SharedMemoryMapView()
+// PURPOSE:
+// Closes a shared memory view.
+//
+void SharedMemoryMapView::Close()
{
+ if (Buffer.Pointer != nullptr)
+ {
+ munmap(Buffer.Pointer, MemSize);
+ Buffer = nullptr;
+
+ MemSize = 0;
+ }
+
if (m_fdSharedMemory != INVALID_FD_VALUE)
{
close(m_fdSharedMemory);
+ m_fdSharedMemory = INVALID_FD_VALUE;
+
+ if (CleanupOnClose)
+ {
+ if (m_sharedMemoryMapName != nullptr)
+ {
+ shm_unlink(m_sharedMemoryMapName);
+ }
+
+ CleanupOnClose = false;
+ }
+ }
+
+ if (m_sharedMemoryMapName != nullptr)
+ {
+ free(m_sharedMemoryMapName);
+ m_sharedMemoryMapName = nullptr;
}
}
+}
+}
diff --git a/source/Mlos.Core/SharedMemoryMapView.Linux.h b/source/Mlos.Core/SharedMemoryMapView.Linux.h
index 47ad2cccaf..c8a7e10365 100644
--- a/source/Mlos.Core/SharedMemoryMapView.Linux.h
+++ b/source/Mlos.Core/SharedMemoryMapView.Linux.h
@@ -43,6 +43,10 @@ class SharedMemoryMapView
_Check_return_
HRESULT CreateOrOpen(const char* const sharedMemoryMapName, size_t memSize) noexcept;
+ // Closes a shared memory view.
+ //
+ void Close();
+
private:
_Check_return_
HRESULT MapMemoryView(size_t memSize) noexcept;
@@ -51,8 +55,13 @@ class SharedMemoryMapView
size_t MemSize;
BytePtr Buffer;
+ // Indicates if we should cleanup OS resources when closing the shared memory map view.
+ //
+ bool CleanupOnClose;
+
private:
int m_fdSharedMemory;
+ char* m_sharedMemoryMapName;
};
}
diff --git a/source/Mlos.Core/SharedMemoryMapView.Windows.cpp b/source/Mlos.Core/SharedMemoryMapView.Windows.cpp
index 532082b960..7fc9534562 100644
--- a/source/Mlos.Core/SharedMemoryMapView.Windows.cpp
+++ b/source/Mlos.Core/SharedMemoryMapView.Windows.cpp
@@ -28,7 +28,8 @@ namespace Core
SharedMemoryMapView::SharedMemoryMapView() noexcept
: MemSize(0),
m_hMapFile(nullptr),
- Buffer(nullptr)
+ Buffer(nullptr),
+ CleanupOnClose(false)
{
}
@@ -41,7 +42,8 @@ SharedMemoryMapView::SharedMemoryMapView() noexcept
SharedMemoryMapView::SharedMemoryMapView(SharedMemoryMapView&& sharedMemoryMapView) noexcept
: MemSize(std::exchange(sharedMemoryMapView.MemSize, 0)),
m_hMapFile(std::exchange(sharedMemoryMapView.m_hMapFile, nullptr)),
- Buffer(std::exchange(sharedMemoryMapView.Buffer, nullptr))
+ Buffer(std::exchange(sharedMemoryMapView.Buffer, nullptr)),
+ CleanupOnClose(std::exchange(sharedMemoryMapView.CleanupOnClose, 0))
{
}
@@ -57,7 +59,7 @@ SharedMemoryMapView::~SharedMemoryMapView()
// NAME: SharedMemoryMapView::Create
//
// PURPOSE:
-// Creates a shared memory map view.
+// Creates a new shared memory map view.
//
// RETURNS:
// HRESULT.
@@ -245,6 +247,10 @@ HRESULT SharedMemoryMapView::MapMemoryView(size_t memSize) noexcept
//
void SharedMemoryMapView::Close()
{
+ // Windows OS will remove the shared memory map once the last process detaches from it, just reset the flag.
+ //
+ CleanupOnClose = false;
+
UnmapViewOfFile(Buffer.Pointer);
Buffer.Pointer = nullptr;
diff --git a/source/Mlos.Core/SharedMemoryMapView.Windows.h b/source/Mlos.Core/SharedMemoryMapView.Windows.h
index 99555c4d33..5660374e09 100644
--- a/source/Mlos.Core/SharedMemoryMapView.Windows.h
+++ b/source/Mlos.Core/SharedMemoryMapView.Windows.h
@@ -64,6 +64,11 @@ class SharedMemoryMapView
size_t MemSize;
BytePtr Buffer;
+ // Indicates if we should cleanup OS resources when closing the shared memory map view.
+ // No-op on Windows.
+ //
+ bool CleanupOnClose;
+
private:
HANDLE m_hMapFile;
};
diff --git a/source/Mlos.Grpc.Binplace/Makefile b/source/Mlos.Grpc.Binplace/Makefile
new file mode 100644
index 0000000000..72387073da
--- /dev/null
+++ b/source/Mlos.Grpc.Binplace/Makefile
@@ -0,0 +1,2 @@
+RelativePathToProjectRoot := ../..
+include $(RelativePathToProjectRoot)/build/DotnetWrapper.mk
diff --git a/source/Mlos.Grpc.Binplace/Mlos.Grpc.Binplace.csproj b/source/Mlos.Grpc.Binplace/Mlos.Grpc.Binplace.csproj
new file mode 100644
index 0000000000..7bb43bee46
--- /dev/null
+++ b/source/Mlos.Grpc.Binplace/Mlos.Grpc.Binplace.csproj
@@ -0,0 +1,33 @@
+
+
+
+
+ {83BA54CA-1BC2-4591-9109-314BF2A0190F}
+ Both
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/Mlos.NetCore.UnitTest/HashTableTests.cs b/source/Mlos.NetCore.UnitTest/HashTableTests.cs
index 78452928c6..13abfbd65b 100644
--- a/source/Mlos.NetCore.UnitTest/HashTableTests.cs
+++ b/source/Mlos.NetCore.UnitTest/HashTableTests.cs
@@ -40,6 +40,7 @@ public HashTableTests()
public void Insert()
{
using var sharedMemoryRegionView = SharedMemoryRegionView.Create(SharedMemoryMapName, SharedMemorySize);
+ sharedMemoryRegionView.CleanupOnClose = true;
MlosProxyInternal.SharedConfigMemoryRegion sharedConfigMemoryRegion = sharedMemoryRegionView.MemoryRegion();
diff --git a/source/Mlos.NetCore.UnitTest/Mlos.NetCore.UnitTest.csproj b/source/Mlos.NetCore.UnitTest/Mlos.NetCore.UnitTest.csproj
index d8bc52a0ff..a95c6cd6e5 100644
--- a/source/Mlos.NetCore.UnitTest/Mlos.NetCore.UnitTest.csproj
+++ b/source/Mlos.NetCore.UnitTest/Mlos.NetCore.UnitTest.csproj
@@ -28,6 +28,7 @@
+
diff --git a/source/Mlos.NetCore.UnitTest/SharedChannelTests.cs b/source/Mlos.NetCore.UnitTest/SharedChannelTests.cs
index bd56bc147d..850396caff 100644
--- a/source/Mlos.NetCore.UnitTest/SharedChannelTests.cs
+++ b/source/Mlos.NetCore.UnitTest/SharedChannelTests.cs
@@ -19,7 +19,7 @@
namespace Mlos.NetCore.UnitTest
{
- public class SharedChannelTests : IDisposable
+ public sealed class SharedChannelTests : IDisposable
{
private const string GlobalMemoryMapName = "Mlos.NetCore.Global.UnitTest";
private const string SharedChannelMemoryMapName = "Mlos.NetCore.SharedChannelTests.UnitTest";
@@ -40,7 +40,9 @@ public SharedChannelTests()
// Initialize shared channel.
//
globalChannelMemoryRegionView = SharedMemoryRegionView.Create(GlobalMemoryMapName, SharedMemorySize);
+ globalChannelMemoryRegionView.CleanupOnClose = true;
sharedChannelMemoryMapView = SharedMemoryMapView.Create(SharedChannelMemoryMapName, SharedMemorySize);
+ sharedChannelMemoryMapView.CleanupOnClose = true;
MlosProxyInternal.GlobalMemoryRegion globalMemoryRegion = globalChannelMemoryRegionView.MemoryRegion();
@@ -53,7 +55,7 @@ public void Dispose()
GC.SuppressFinalize(this);
}
- protected virtual void Dispose(bool disposing)
+ private void Dispose(bool disposing)
{
if (isDisposed || !disposing)
{
diff --git a/source/Mlos.NetCore.UnitTest/SharedMemoryMapViewTests.cs b/source/Mlos.NetCore.UnitTest/SharedMemoryMapViewTests.cs
new file mode 100644
index 0000000000..5d91691e9b
--- /dev/null
+++ b/source/Mlos.NetCore.UnitTest/SharedMemoryMapViewTests.cs
@@ -0,0 +1,56 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root
+// for license information.
+//
+// -----------------------------------------------------------------------
+
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+using Mlos.Core;
+using Mlos.UnitTest;
+using Xunit;
+
+using MlosProxyInternal = Proxy.Mlos.Core.Internal;
+using TestSharedChannel = Mlos.Core.SharedChannel;
+using UnitTestProxy = Proxy.Mlos.UnitTest;
+
+namespace Mlos.NetCore.UnitTest
+{
+ public sealed class SharedMemoryMapViewTests
+ {
+ private const string SharedMemoryMapName = "Mlos.NetCore.SharedMapTest.UnitTest";
+ private const int SharedMemorySize = 4096;
+
+ ///
+ /// Verifies that on Linux, shared memory is unlinked on dispose.
+ ///
+ [Fact]
+ public void VerifySharedMemoryMapUnlink()
+ {
+ // Create a new shared memory maps.
+ //
+ var newsSharedChannelMemoryMap = SharedMemoryMapView.Create(SharedMemoryMapName, SharedMemorySize);
+ newsSharedChannelMemoryMap.CleanupOnClose = true;
+ newsSharedChannelMemoryMap.Dispose();
+
+ try
+ {
+ // Verify we can open already created shared memory.
+ //
+ using var openedSharedChannelMemoryMap = SharedMemoryMapView.Open(SharedMemoryMapName, SharedMemorySize);
+ newsSharedChannelMemoryMap.CleanupOnClose = true;
+
+ Assert.False(true, "Shared memory map should be deleted");
+ }
+ catch (FileNotFoundException)
+ {
+ // We are expecting failure.
+ //
+ }
+ }
+ }
+}
diff --git a/source/Mlos.NetCore/Codegen/GlobalMemoryRegion.cs b/source/Mlos.NetCore/Codegen/GlobalMemoryRegion.cs
index 878336214b..46f6ee18eb 100644
--- a/source/Mlos.NetCore/Codegen/GlobalMemoryRegion.cs
+++ b/source/Mlos.NetCore/Codegen/GlobalMemoryRegion.cs
@@ -29,6 +29,11 @@ internal partial struct GlobalMemoryRegion
///
internal ChannelSynchronization FeedbackChannelSynchronization;
+ ///
+ /// Gets or sets information how many processes are using the global memory region.
+ ///
+ internal AtomicUInt32 AttachedProcessesCount;
+
///
/// Total number of regions.
///
diff --git a/source/Mlos.NetCore/InterProcessMlosContext.cs b/source/Mlos.NetCore/InterProcessMlosContext.cs
new file mode 100644
index 0000000000..db6a9213f3
--- /dev/null
+++ b/source/Mlos.NetCore/InterProcessMlosContext.cs
@@ -0,0 +1,157 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root
+// for license information.
+//
+// -----------------------------------------------------------------------
+
+using MlosProxy = Proxy.Mlos.Core;
+using MlosProxyInternal = Proxy.Mlos.Core.Internal;
+
+namespace Mlos.Core
+{
+ ///
+ /// Inter-process MlosContexts.
+ ///
+ public class InterProcessMlosContext : MlosContext
+ {
+ ///
+ /// Shared memory mapping name must start with "Host_" prefix, to be accessible from certain applications.
+ /// TODO: Make these config regions configurable to support multiple processes.
+ ///
+ private const string GlobalMemoryMapName = "Host_Mlos.GlobalMemory";
+ private const string ControlChannelMemoryMapName = "Host_Mlos.ControlChannel";
+ private const string FeedbackChannelMemoryMapName = "Host_Mlos.FeedbackChannel";
+ private const string ControlChannelSemaphoreName = @"Global\ControlChannel_Event"; //// FIXME: Use non-backslashes for Linux environments.
+ private const string FeedbackChannelSemaphoreName = @"Global\FeedbackChannel_Event";
+
+ private const string SharedConfigMemoryMapName = "Host_Mlos.Config.SharedMemory";
+
+ private const int SharedMemorySize = 65536;
+
+ ///
+ /// Always create...
+ ///
+ /// InterProcessMlosContext instance.
+ public static InterProcessMlosContext Create()
+ {
+ // Create or open the memory mapped files.
+ //
+ SharedMemoryRegionView globalMemoryRegionView = SharedMemoryRegionView.Create(GlobalMemoryMapName, SharedMemorySize);
+ SharedMemoryMapView controlChannelMemoryMapView = SharedMemoryMapView.Create(ControlChannelMemoryMapName, SharedMemorySize);
+ SharedMemoryMapView feedbackChannelMemoryMapView = SharedMemoryMapView.Create(FeedbackChannelMemoryMapName, SharedMemorySize);
+ SharedMemoryRegionView sharedConfigMemoryMapView = SharedMemoryRegionView.Create(SharedConfigMemoryMapName, SharedMemorySize);
+
+ // Create channel synchronization primitives.
+ //
+ NamedEvent controlChannelNamedEvent = NamedEvent.CreateOrOpen(ControlChannelSemaphoreName);
+ NamedEvent feedbackChannelNamedEvent = NamedEvent.CreateOrOpen(FeedbackChannelSemaphoreName);
+
+ return new InterProcessMlosContext(
+ globalMemoryRegionView,
+ controlChannelMemoryMapView,
+ feedbackChannelMemoryMapView,
+ sharedConfigMemoryMapView,
+ controlChannelNamedEvent,
+ feedbackChannelNamedEvent);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// InterProcessMlosContext instance.
+ public static InterProcessMlosContext CreateOrOpen()
+ {
+ // Create or open the memory mapped files.
+ //
+ SharedMemoryRegionView globalMemoryRegionView = SharedMemoryRegionView.CreateOrOpen(GlobalMemoryMapName, SharedMemorySize);
+ SharedMemoryMapView controlChannelMemoryMapView = SharedMemoryMapView.CreateOrOpen(ControlChannelMemoryMapName, SharedMemorySize);
+ SharedMemoryMapView feedbackChannelMemoryMapView = SharedMemoryMapView.CreateOrOpen(FeedbackChannelMemoryMapName, SharedMemorySize);
+ SharedMemoryRegionView sharedConfigMemoryMapView = SharedMemoryRegionView.CreateOrOpen(SharedConfigMemoryMapName, SharedMemorySize);
+
+ // Create channel synchronization primitives.
+ //
+ NamedEvent controlChannelNamedEvent = NamedEvent.CreateOrOpen(ControlChannelSemaphoreName);
+ NamedEvent feedbackChannelNamedEvent = NamedEvent.CreateOrOpen(FeedbackChannelSemaphoreName);
+
+ return new InterProcessMlosContext(
+ globalMemoryRegionView,
+ controlChannelMemoryMapView,
+ feedbackChannelMemoryMapView,
+ sharedConfigMemoryMapView,
+ controlChannelNamedEvent,
+ feedbackChannelNamedEvent);
+ }
+
+ internal InterProcessMlosContext(
+ SharedMemoryRegionView globalMemoryRegionView,
+ SharedMemoryMapView controlChannelMemoryMapView,
+ SharedMemoryMapView feedbackChannelMemoryMapView,
+ SharedMemoryRegionView sharedConfigMemoryMapView,
+ NamedEvent controlChannelNamedEvent,
+ NamedEvent feedbackChannelNamedEvent)
+ {
+ this.globalMemoryRegionView = globalMemoryRegionView;
+ this.controlChannelMemoryMapView = controlChannelMemoryMapView;
+ this.feedbackChannelMemoryMapView = feedbackChannelMemoryMapView;
+ this.sharedConfigMemoryMapView = sharedConfigMemoryMapView;
+
+ this.controlChannelNamedEvent = controlChannelNamedEvent;
+ this.feedbackChannelNamedEvent = feedbackChannelNamedEvent;
+
+ MlosProxyInternal.GlobalMemoryRegion globalMemoryRegion = globalMemoryRegionView.MemoryRegion();
+
+ // Increase the usage counter. When closing global shared memory, we will decrease the counter.
+ // If there is no process using the shared memory, we will clean the OS resources. On Windows OS,
+ // this is no-op; on Linux, we unlink created files.
+ //
+ globalMemoryRegion.AttachedProcessesCount.FetchAdd(1);
+
+ // Create the control channel instance.
+ //
+ ControlChannel = new SharedChannel(
+ buffer: controlChannelMemoryMapView.Buffer,
+ size: (uint)controlChannelMemoryMapView.MemSize,
+ sync: globalMemoryRegion.ControlChannelSynchronization)
+ {
+ ChannelPolicy = { NotificationEvent = controlChannelNamedEvent },
+ };
+
+ // Create the feedback channel instance.
+ //
+ FeedbackChannel = new SharedChannel(
+ buffer: feedbackChannelMemoryMapView.Buffer,
+ size: (uint)feedbackChannelMemoryMapView.MemSize,
+ sync: globalMemoryRegion.FeedbackChannelSynchronization)
+ {
+ ChannelPolicy = { NotificationEvent = feedbackChannelNamedEvent },
+ };
+ }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (isDisposed || !disposing)
+ {
+ return;
+ }
+
+ uint usageCount = GlobalMemoryRegion.AttachedProcessesCount.FetchSub(1);
+
+ // The last one out shut off the lights.
+ //
+ if (usageCount == 0)
+ {
+ globalMemoryRegionView.CleanupOnClose = true;
+ controlChannelMemoryMapView.CleanupOnClose = true;
+ feedbackChannelMemoryMapView.CleanupOnClose = true;
+ sharedConfigMemoryMapView.CleanupOnClose = true;
+ controlChannelNamedEvent.CleanupOnClose = true;
+ feedbackChannelNamedEvent.CleanupOnClose = true;
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/source/Mlos.NetCore/Mlos.NetCore.csproj b/source/Mlos.NetCore/Mlos.NetCore.csproj
index b8cc5cdb64..f81f50e92d 100644
--- a/source/Mlos.NetCore/Mlos.NetCore.csproj
+++ b/source/Mlos.NetCore/Mlos.NetCore.csproj
@@ -29,9 +29,6 @@
-
-
-
@@ -42,7 +39,11 @@
+
+
+
+
diff --git a/source/Mlos.NetCore/MlosContext.cs b/source/Mlos.NetCore/MlosContext.cs
index cb538c8b9d..8ae2144432 100644
--- a/source/Mlos.NetCore/MlosContext.cs
+++ b/source/Mlos.NetCore/MlosContext.cs
@@ -6,6 +6,10 @@
//
// -----------------------------------------------------------------------
+using System;
+
+using MlosProxyInternal = Proxy.Mlos.Core.Internal;
+
namespace Mlos.Core
{
///
@@ -18,12 +22,21 @@ namespace Mlos.Core
/// See Also: Mlos.Core/MlosContext.h for the corresponding C++ smart
/// component side.
///
- public static class MlosContext
+ public abstract class MlosContext : IDisposable
{
+ #region Shared public objects
+
+ ///
+ /// Gets or sets the control channel instance.
+ /// #TODO, those should not be static. Pass a MlosContext to the experiment class.
+ ///
+ public static ISharedChannel ControlChannel { get; protected set; }
+
///
/// Gets or sets the feedback channel instance.
+ /// #TODO, those should not be static. Pass a MlosContext to the experiment class.
///
- public static ISharedChannel FeedbackChannel { get; set; }
+ public static ISharedChannel FeedbackChannel { get; protected set; }
public static ISharedConfigAccessor SharedConfigManager { get; set; }
@@ -38,5 +51,95 @@ public static class MlosContext
/// example).
///
public static IOptimizerFactory OptimizerFactory { get; set; }
+
+ #endregion
+
+ protected SharedMemoryRegionView globalMemoryRegionView;
+ protected SharedMemoryMapView controlChannelMemoryMapView;
+ protected SharedMemoryMapView feedbackChannelMemoryMapView;
+ protected SharedMemoryRegionView sharedConfigMemoryMapView;
+
+ protected NamedEvent controlChannelNamedEvent;
+ protected NamedEvent feedbackChannelNamedEvent;
+
+ protected bool isDisposed;
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (isDisposed || !disposing)
+ {
+ return;
+ }
+
+ // Close shared memory.
+ //
+ globalMemoryRegionView?.Dispose();
+ globalMemoryRegionView = null;
+
+ controlChannelMemoryMapView?.Dispose();
+ controlChannelMemoryMapView = null;
+
+ feedbackChannelMemoryMapView?.Dispose();
+ feedbackChannelMemoryMapView = null;
+
+ sharedConfigMemoryMapView?.Dispose();
+ sharedConfigMemoryMapView = null;
+
+ controlChannelNamedEvent?.Dispose();
+ controlChannelNamedEvent = null;
+
+ feedbackChannelNamedEvent?.Dispose();
+ feedbackChannelNamedEvent = null;
+
+ isDisposed = true;
+ }
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ public MlosProxyInternal.GlobalMemoryRegion GlobalMemoryRegion => globalMemoryRegionView.MemoryRegion();
+
+ public MlosProxyInternal.SharedConfigMemoryRegion SharedConfigMemoryRegion => sharedConfigMemoryMapView.MemoryRegion();
+
+ ///
+ /// Terminate the control channel.
+ ///
+ public void TerminateControlChannel()
+ {
+ // Terminate the channel to avoid deadlocks if the buffer is full, and there is no active reader thread.
+ //
+ ControlChannel.SyncObject.TerminateChannel.Store(true);
+ controlChannelNamedEvent.Signal();
+ }
+
+ ///
+ /// Terminates the feedback channel.
+ ///
+ public void TerminateFeedbackChannel()
+ {
+ FeedbackChannel.SyncObject.TerminateChannel.Store(true);
+ feedbackChannelNamedEvent.Signal();
+ }
+
+ ///
+ /// Checks if the control channel is still active.
+ ///
+ ///
+ public bool IsControlChannelActive()
+ {
+ return !ControlChannel.SyncObject.TerminateChannel.Load();
+ }
+
+ ///
+ /// Checks if the feedback channel is still active.
+ ///
+ ///
+ public bool IsFeedbackChannelActive()
+ {
+ return !FeedbackChannel.SyncObject.TerminateChannel.Load();
+ }
}
}
diff --git a/source/Mlos.NetCore/NamedEvent.Windows.cs b/source/Mlos.NetCore/NamedEvent.Windows.cs
index 55750a71d2..7ab3261876 100644
--- a/source/Mlos.NetCore/NamedEvent.Windows.cs
+++ b/source/Mlos.NetCore/NamedEvent.Windows.cs
@@ -70,18 +70,15 @@ public override bool Wait()
///
protected override void Dispose(bool disposing)
{
- if (disposed)
+ if (isDisposed || !disposing)
{
return;
}
- if (disposing)
- {
- eventHandle?.Dispose();
- eventHandle = null;
- }
+ eventHandle?.Dispose();
+ eventHandle = null;
- disposed = true;
+ isDisposed = true;
}
private EventSafeHandle eventHandle;
diff --git a/source/Mlos.NetCore/NamedEvent.cs b/source/Mlos.NetCore/NamedEvent.cs
index 20ea64d0c6..441433fa8f 100644
--- a/source/Mlos.NetCore/NamedEvent.cs
+++ b/source/Mlos.NetCore/NamedEvent.cs
@@ -38,11 +38,15 @@ public static NamedEvent CreateOrOpen(string name)
}
}
+ ///
+ /// Finalizes an instance of the class.
+ ///
~NamedEvent()
{
Dispose(false);
}
+ ///
public void Dispose()
{
// Dispose of unmanaged resources.
@@ -75,6 +79,12 @@ public void Dispose()
///
/// True if object has been disposed.
///
- protected bool disposed = false;
+ protected bool isDisposed = false;
+
+ ///
+ /// Indicates if we should cleanup OS resources when closing the shared memory map view.
+ ///
+ ///
+ public bool CleanupOnClose;
}
-}
\ No newline at end of file
+}
diff --git a/source/Mlos.NetCore/NamedSemaphore.Linux.cs b/source/Mlos.NetCore/NamedSemaphore.Linux.cs
index 30da670265..ba4daaedd1 100644
--- a/source/Mlos.NetCore/NamedSemaphore.Linux.cs
+++ b/source/Mlos.NetCore/NamedSemaphore.Linux.cs
@@ -6,10 +6,15 @@
//
// -----------------------------------------------------------------------
-using System.Threading;
+using System.ComponentModel;
+using System.IO;
+using System.Runtime.InteropServices;
namespace Mlos.Core.Linux
{
+ ///
+ /// Named semaphore.
+ ///
public class NamedSemaphore : Mlos.Core.NamedEvent
{
///
@@ -31,6 +36,15 @@ private NamedSemaphore(string name, Native.OpenFlags openFlags)
openFlags,
Native.ModeFlags.S_IRUSR | Native.ModeFlags.S_IWUSR,
0);
+
+ if (semaphoreHandle.IsInvalid)
+ {
+ throw new IOException(
+ $"Failed to create a NamedSemaphore {name}",
+ innerException: new Win32Exception(Marshal.GetLastWin32Error()));
+ }
+
+ semaphoreName = name;
}
///
@@ -55,19 +69,30 @@ public override bool Wait()
///
protected override void Dispose(bool disposing)
{
- if (disposed)
+ if (isDisposed || !disposing)
{
return;
}
- if (disposing)
+ semaphoreHandle?.Dispose();
+
+ if (CleanupOnClose)
{
- semaphoreHandle?.Dispose();
+ // Unlink semaphore. Ignore the errors.
+ //
+ if (semaphoreName != null)
+ {
+ _ = Native.SemaphoreUnlink(semaphoreName);
+ }
+
+ CleanupOnClose = false;
}
- disposed = true;
+ isDisposed = true;
}
private readonly SemaphoreSafeHandle semaphoreHandle;
+
+ private readonly string semaphoreName;
}
-}
\ No newline at end of file
+}
diff --git a/source/Mlos.NetCore/SharedChannel.cs b/source/Mlos.NetCore/SharedChannel.cs
index 2c479dd6e4..91f8dd6333 100644
--- a/source/Mlos.NetCore/SharedChannel.cs
+++ b/source/Mlos.NetCore/SharedChannel.cs
@@ -32,6 +32,11 @@ void SendMessage(ref TMessage msg)
///
///
void ProcessMessages(ref DispatchEntry[] dispatchTable);
+
+ ///
+ /// Channel synchronization object.
+ ///
+ internal MlosProxy.ChannelSynchronization SyncObject { get; }
}
///
@@ -763,5 +768,10 @@ internal bool HasReadersInWaitingState()
/// Channel control policy.
///
public TChannelPolicy ChannelPolicy;
+
+ ///
+ /// Channel synchronization object.
+ ///
+ MlosProxy.ChannelSynchronization ISharedChannel.SyncObject => Sync;
}
}
diff --git a/source/Mlos.NetCore/SharedMemoryMapView.Linux.cs b/source/Mlos.NetCore/SharedMemoryMapView.Linux.cs
index 5ff16b8d54..efd31a81a1 100644
--- a/source/Mlos.NetCore/SharedMemoryMapView.Linux.cs
+++ b/source/Mlos.NetCore/SharedMemoryMapView.Linux.cs
@@ -13,6 +13,9 @@
namespace Mlos.Core.Linux
{
+ ///
+ /// Linux implementation of shared memory map view.
+ ///
public sealed class SharedMemoryMapView : Mlos.Core.SharedMemoryMapView
{
///
@@ -121,27 +124,32 @@ private SharedMemoryMapView(string sharedMemoryMapName, ulong sharedMemorySize,
///
protected override void Dispose(bool disposing)
{
- if (disposed)
+ if (isDisposed || !disposing)
{
return;
}
- if (disposing)
- {
- // Close shared memory.
- //
- sharedMemoryHandle?.Dispose();
+ // Close shared memory.
+ //
+ sharedMemoryHandle?.Dispose();
+ if (CleanupOnClose)
+ {
// Unlink shared map. Ignore the errors.
//
- _ = Native.SharedMemoryUnlink(sharedMemoryMapName);
+ if (sharedMemoryMapName != null)
+ {
+ _ = Native.SharedMemoryUnlink(sharedMemoryMapName);
+ }
+
+ CleanupOnClose = false;
}
- disposed = true;
+ isDisposed = true;
}
- private readonly string sharedMemoryMapName;
-
private SharedMemorySafeHandle sharedMemoryHandle;
+
+ private readonly string sharedMemoryMapName;
}
}
diff --git a/source/Mlos.NetCore/SharedMemoryMapView.Windows.cs b/source/Mlos.NetCore/SharedMemoryMapView.Windows.cs
index 2c08c33543..5012c0c5ac 100644
--- a/source/Mlos.NetCore/SharedMemoryMapView.Windows.cs
+++ b/source/Mlos.NetCore/SharedMemoryMapView.Windows.cs
@@ -12,6 +12,9 @@
namespace Mlos.Core.Windows
{
+ ///
+ /// Windows implementation of shared memory map view.
+ ///
public class SharedMemoryMapView : Mlos.Core.SharedMemoryMapView
{
///
@@ -115,23 +118,20 @@ private SharedMemoryMapView(SharedMemorySafeHandle sharedMemoryHandle, ulong sha
///
protected override void Dispose(bool disposing)
{
- if (disposed)
+ if (isDisposed || !disposing)
{
return;
}
- if (disposing)
- {
- // Close the memory mapping.
- //
- memoryMappingHandle?.Dispose();
+ // Close the memory mapping.
+ //
+ memoryMappingHandle?.Dispose();
- // Close the shared memory.
- //
- sharedMemoryHandle?.Dispose();
- }
+ // Close the shared memory.
+ //
+ sharedMemoryHandle?.Dispose();
- disposed = true;
+ isDisposed = true;
}
private readonly MemoryMappingSafeHandle memoryMappingHandle;
diff --git a/source/Mlos.NetCore/SharedMemoryMapView.cs b/source/Mlos.NetCore/SharedMemoryMapView.cs
index cf54bda0db..9654c8bc70 100644
--- a/source/Mlos.NetCore/SharedMemoryMapView.cs
+++ b/source/Mlos.NetCore/SharedMemoryMapView.cs
@@ -1,112 +1,118 @@
-// -----------------------------------------------------------------------
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License. See LICENSE in the project root
-// for license information.
-//
-// -----------------------------------------------------------------------
-
-using System;
-using System.Runtime.ConstrainedExecution;
-using System.Runtime.InteropServices;
-
-namespace Mlos.Core
-{
- public abstract class SharedMemoryMapView : CriticalFinalizerObject, IDisposable
- {
- ///
- /// Creates a new shared memory view.
- ///
- ///
- ///
- /// Thrown when executed on unsupported OS.
- ///
- public static SharedMemoryMapView Create(string sharedMemoryMapName, ulong sharedMemorySize)
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- return Windows.SharedMemoryMapView.Create(sharedMemoryMapName, sharedMemorySize);
- }
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- return Linux.SharedMemoryMapView.Create(sharedMemoryMapName, sharedMemorySize);
- }
- else
- {
- throw new InvalidOperationException("Unsupported OS.");
- }
- }
-
- ///
- /// Creates or opens a shared memory view.
- ///
- ///
- ///
- ///
- public static SharedMemoryMapView CreateOrOpen(string sharedMemoryMapName, ulong sharedMemorySize)
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- return Windows.SharedMemoryMapView.CreateOrOpen(sharedMemoryMapName, sharedMemorySize);
- }
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- return Linux.SharedMemoryMapView.CreateOrOpen(sharedMemoryMapName, sharedMemorySize);
- }
- else
- {
- throw new InvalidOperationException("Unsupported OS.");
- }
- }
-
- ///
- /// Opens an existing shared memory view.
- ///
- ///
- ///
- ///
- public static SharedMemoryMapView Open(string sharedMemoryMapName, ulong sharedMemorySize)
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- return Windows.SharedMemoryMapView.Open(sharedMemoryMapName, sharedMemorySize);
- }
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- return Linux.SharedMemoryMapView.Open(sharedMemoryMapName, sharedMemorySize);
- }
- else
- {
- throw new InvalidOperationException("Unsupported OS.");
- }
- }
-
- ~SharedMemoryMapView()
- {
- Dispose(false);
- }
-
- public void Dispose()
- {
- // Dispose of unmanaged resources.
- //
- Dispose(true);
-
- // Suppress finalization.
- //
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Protected implementation of Dispose pattern.
- ///
- ///
- protected abstract void Dispose(bool disposing);
-
- public IntPtr Buffer;
-
- public ulong MemSize;
-
- protected bool disposed = false;
- }
-}
\ No newline at end of file
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root
+// for license information.
+//
+// -----------------------------------------------------------------------
+
+using System;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
+
+namespace Mlos.Core
+{
+ public abstract class SharedMemoryMapView : CriticalFinalizerObject, IDisposable
+ {
+ ///
+ /// Creates a new shared memory view.
+ ///
+ ///
+ ///
+ /// Thrown when executed on unsupported OS.
+ ///
+ public static SharedMemoryMapView Create(string sharedMemoryMapName, ulong sharedMemorySize)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return Windows.SharedMemoryMapView.Create(sharedMemoryMapName, sharedMemorySize);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return Linux.SharedMemoryMapView.Create(sharedMemoryMapName, sharedMemorySize);
+ }
+ else
+ {
+ throw new InvalidOperationException("Unsupported OS.");
+ }
+ }
+
+ ///
+ /// Creates or opens a shared memory view.
+ ///
+ ///
+ ///
+ ///
+ public static SharedMemoryMapView CreateOrOpen(string sharedMemoryMapName, ulong sharedMemorySize)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return Windows.SharedMemoryMapView.CreateOrOpen(sharedMemoryMapName, sharedMemorySize);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return Linux.SharedMemoryMapView.CreateOrOpen(sharedMemoryMapName, sharedMemorySize);
+ }
+ else
+ {
+ throw new InvalidOperationException("Unsupported OS.");
+ }
+ }
+
+ ///
+ /// Opens an existing shared memory view.
+ ///
+ ///
+ ///
+ ///
+ public static SharedMemoryMapView Open(string sharedMemoryMapName, ulong sharedMemorySize)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return Windows.SharedMemoryMapView.Open(sharedMemoryMapName, sharedMemorySize);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return Linux.SharedMemoryMapView.Open(sharedMemoryMapName, sharedMemorySize);
+ }
+ else
+ {
+ throw new InvalidOperationException("Unsupported OS.");
+ }
+ }
+
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~SharedMemoryMapView()
+ {
+ Dispose(false);
+ }
+
+ ///
+ public void Dispose()
+ {
+ // Dispose of unmanaged resources.
+ //
+ Dispose(true);
+
+ // Suppress finalization.
+ //
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Protected implementation of Dispose pattern.
+ ///
+ ///
+ protected abstract void Dispose(bool disposing);
+
+ public IntPtr Buffer;
+
+ public ulong MemSize;
+
+ public bool CleanupOnClose;
+
+ protected bool isDisposed;
+ }
+}
diff --git a/source/Mlos.NetCore/SharedMemoryRegionView.cs b/source/Mlos.NetCore/SharedMemoryRegionView.cs
index 4bf941d5ea..1fafaa0726 100644
--- a/source/Mlos.NetCore/SharedMemoryRegionView.cs
+++ b/source/Mlos.NetCore/SharedMemoryRegionView.cs
@@ -92,6 +92,9 @@ public SharedMemoryRegionView(SharedMemoryMapView sharedMemoryMap)
};
}
+ ///
+ /// Size of the shared memory map.
+ ///
public ulong MemSize => sharedMemoryMap.MemSize;
///
@@ -108,18 +111,15 @@ public T MemoryRegion()
private void Dispose(bool disposing)
{
- if (disposed)
+ if (isDisposed || !disposing)
{
return;
}
- if (disposing)
- {
- sharedMemoryMap?.Dispose();
- sharedMemoryMap = null;
- }
+ sharedMemoryMap?.Dispose();
+ sharedMemoryMap = null;
- disposed = true;
+ isDisposed = true;
}
///
@@ -131,6 +131,15 @@ public void Dispose()
private SharedMemoryMapView sharedMemoryMap;
- private bool disposed = false;
+ private bool isDisposed = false;
+
+ ///
+ /// Indicates if we should cleanup OS resources when closing the shared memory map view.
+ ///
+ public bool CleanupOnClose
+ {
+ get { return sharedMemoryMap.CleanupOnClose; }
+ set { sharedMemoryMap.CleanupOnClose = value; }
+ }
}
}