Skip to content

Commit

Permalink
Common(-Tests): implemented SingleApplicationInstance
Browse files Browse the repository at this point in the history
This implementation covers MS .NET on Windows, Mono with SHM on Unix, Mono
without SHM on Unix. It uses named mutex + Remoting or file mutex + Remoting
depending on what is supported by the operating system and CLI runtime.

 * MS .NET on Windows -> named mutex
 * Mono on Windows -> named mutex
 * Mono with SHM on Linux -> named mutex
 * Mono without SHM on Linux -> file lock

For file mutex support you need to compile this class with -define:MONO_UNIX
and -r:Mono.Posix.dll otherwise it will throw a NotSupportedException when
named mutex is not available.
  • Loading branch information
meebey committed Jan 21, 2015
1 parent 2b760df commit b6f538d
Show file tree
Hide file tree
Showing 5 changed files with 433 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/Common-Tests/Common-Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<Compile Include="PatternTests.cs" />
<Compile Include="Crc32Tests.cs" />
<Compile Include="RateLimiterTests.cs" />
<Compile Include="SingleApplicationInstanceTests.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
Expand Down
127 changes: 127 additions & 0 deletions src/Common-Tests/SingleApplicationInstanceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Smuxi - Smart MUltipleXed Irc
//
// Copyright (c) 2015 Mirco Bauer <meebey@meebey.net>
//
// Full GPL License: <http://www.gnu.org/licenses/gpl.txt>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using NUnit.Framework;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Threading;
using System.IO;

namespace Smuxi.Common
{
[TestFixture]
public class SingleApplicationInstanceTests
{
SingleApplicationInstance<TestApplication> FirstInstance { get; set; }

class TestApplication : MarshalByRefObject
{
public int InvokeCounter { get; private set; }

public void Invoke()
{
InvokeCounter++;
}

public override object InitializeLifetimeService()
{
// I want to live forever
return null;
}
}

[SetUp]
public void SetUp()
{
FirstInstance = new SingleApplicationInstance<TestApplication>("test");
}

[TearDown]
public void TearDown()
{
if (FirstInstance != null) {
FirstInstance.Dispose();
}
try {
var mutex = Mutex.OpenExisting("test");
Assert.Fail();
} catch (WaitHandleCannotBeOpenedException) {
}
}

[Test]
public void IsFirstInstance()
{
var instance1 = FirstInstance;
Assert.IsTrue(instance1.IsFirstInstance);

using (var instance2 = new SingleApplicationInstance<TestApplication>("test")) {
Assert.IsFalse(instance2.IsFirstInstance);
}
}

[Test]
public void Dispose()
{
FirstInstance.Dispose();
FirstInstance = null;

try {
var mutex = Mutex.OpenExisting("test");
Assert.Fail();
} catch (WaitHandleCannotBeOpenedException) {
}
Assert.IsNull(ChannelServices.GetChannel("ipc"));

var appData = Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData
);
var lockDirectory = Path.Combine(appData, "SingleApplicationInstance");
var lockFile = Path.Combine(lockDirectory, "test");
//Assert.IsFalse(File.Exists(lockFile));

FirstInstance = new SingleApplicationInstance<TestApplication>("test");
Assert.IsTrue(FirstInstance.IsFirstInstance);
//Assert.IsTrue(File.Exists(lockFile));
}

[Test]
public void Invoke()
{
var instance1 = FirstInstance;
Assert.IsTrue(instance1.IsFirstInstance);

instance1.FirstInstance = new TestApplication();
Assert.IsNotNull(instance1.FirstInstance);
Assert.AreEqual(0, instance1.FirstInstance.InvokeCounter);
instance1.FirstInstance.Invoke();
Assert.AreEqual(1, instance1.FirstInstance.InvokeCounter);

using (var instance2 = new SingleApplicationInstance<TestApplication>("test")) {
Assert.IsFalse(instance2.IsFirstInstance);

Assert.IsNotNull(instance2.FirstInstance);
Assert.AreEqual(1, instance2.FirstInstance.InvokeCounter);
instance2.FirstInstance.Invoke();
Assert.AreEqual(2, instance2.FirstInstance.InvokeCounter);
}
}
}
}
6 changes: 4 additions & 2 deletions src/Common/Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\bin\debug</OutputPath>
<DefineConstants>DEBUG;TRACE;LOG4NET;NET_2_0;NDESK_OPTIONS</DefineConstants>
<DefineConstants>DEBUG;TRACE;LOG4NET;NET_2_0;NDESK_OPTIONS;MONO_UNIX</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
Expand All @@ -31,7 +31,7 @@
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\bin\release</OutputPath>
<DefineConstants>TRACE;LOG4NET;NET_2_0;NDESK_OPTIONS</DefineConstants>
<DefineConstants>TRACE;LOG4NET;NET_2_0;NDESK_OPTIONS;MONO_UNIX</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Execution>
Expand All @@ -58,6 +58,7 @@
<Compile Include="AtomFeed.cs" />
<Compile Include="SpecialFolderPatternConverter.cs" />
<Compile Include="RateLimiter.cs" />
<Compile Include="SingleApplicationInstance.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Defines.cs.in" />
Expand All @@ -70,6 +71,7 @@
<Reference Include="Mono.Posix" />
<Reference Include="System.Core" />
<Reference Include="System.Xml" />
<Reference Include="System.Runtime.Remoting" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
6 changes: 4 additions & 2 deletions src/Common/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ EXTRA_DIST =

if ENABLE_RELEASE
ASSEMBLY_COMPILER_COMMAND = @MCS@
ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ "-define:NET_2_0,NDESK_OPTIONS"
ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ "-define:NET_2_0,NDESK_OPTIONS,MONO_UNIX"

ASSEMBLY = ../../bin/release/smuxi-common.dll
ASSEMBLY_MDB =
Expand All @@ -19,7 +19,7 @@ endif

if ENABLE_DEBUG
ASSEMBLY_COMPILER_COMMAND = @MCS@
ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -debug -optimize+ "-define:DEBUG,TRACE,LOG4NET,NET_2_0,NDESK_OPTIONS"
ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -debug -optimize+ "-define:DEBUG,TRACE,LOG4NET,NET_2_0,NDESK_OPTIONS,MONO_UNIX"

ASSEMBLY = ../../bin/debug/smuxi-common.dll
ASSEMBLY_MDB = $(ASSEMBLY).mdb
Expand Down Expand Up @@ -59,6 +59,7 @@ FILES = \
NDesk.Options.cs \
Defines.cs \
SpecialFolderPatternConverter.cs \
SingleApplicationInstance.cs \
TaskQueue.cs \
ThreadPoolQueue.cs \
Platform.cs \
Expand All @@ -75,6 +76,7 @@ EXTRAS = \
REFERENCES = \
System \
System.Core \
System.Runtime.Remoting \
System.Xml \
Mono.Posix

Expand Down

0 comments on commit b6f538d

Please sign in to comment.