blob: 7d17bc0504cba754ca26ee474493ab13f164de61 [file] [log] [blame]
using J2N.Threading;
using Lucene.Net.Util;
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Console = Lucene.Net.Util.SystemConsole;
namespace Lucene.Net.Store
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using IOUtils = Lucene.Net.Util.IOUtils;
/// <summary>
/// Simple standalone server that must be running when you
/// use <see cref="VerifyingLockFactory"/>. This server simply
/// verifies at most one process holds the lock at a time.
/// Run without any args to see usage.
/// </summary>
/// <seealso cref="VerifyingLockFactory"/>
/// <seealso cref="LockStressTest"/>
public static class LockVerifyServer // LUCENENET specific: CA1052 Static holder types should be Static or NotInheritable
{
[STAThread]
public static void Main(string[] args)
{
if (args.Length != 2)
{
// LUCENENET specific - our wrapper console shows the correct usage
throw new ArgumentException();
//Console.WriteLine("Usage: java Lucene.Net.Store.LockVerifyServer bindToIp clients\n");
//Environment.FailFast("1");
}
int arg = 0;
string hostname = args[arg++];
int maxClients = Convert.ToInt32(args[arg++], CultureInfo.InvariantCulture);
IPAddress ipAddress = IPAddress.Parse(hostname);
using Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 30000);// SoTimeout = 30000; // initially 30 secs to give clients enough time to startup
s.Bind(new IPEndPoint(ipAddress, 0));
s.Listen(maxClients);
Console.WriteLine("Listening on " + ((IPEndPoint)s.LocalEndPoint).Port.ToString() + "...");
// we set the port as a sysprop, so the ANT task can read it. For that to work, this server must run in-process:
Environment.SetEnvironmentVariable("lockverifyserver.port", ((IPEndPoint)s.LocalEndPoint).Port.ToString(CultureInfo.InvariantCulture));
object localLock = new object();
int[] lockedID = new int[1];
lockedID[0] = -1;
CountdownEvent startingGun = new CountdownEvent(1);
ThreadJob[] threads = new ThreadJob[maxClients];
for (int count = 0; count < maxClients; count++)
{
Socket cs = s.Accept();
threads[count] = new ThreadAnonymousClass(localLock, lockedID, startingGun, cs);
threads[count].Start();
}
// start
Console.WriteLine("All clients started, fire gun...");
startingGun.Signal();
// wait for all threads to finish
foreach (ThreadJob t in threads)
{
t.Join();
}
// cleanup sysprop
Environment.SetEnvironmentVariable("lockverifyserver.port", null);
Console.WriteLine("Server terminated.");
}
private class ThreadAnonymousClass : ThreadJob
{
private readonly object localLock;
private readonly int[] lockedID;
private readonly CountdownEvent startingGun;
private readonly Socket cs;
public ThreadAnonymousClass(object localLock, int[] lockedID, CountdownEvent startingGun, Socket cs)
{
this.localLock = localLock;
this.lockedID = lockedID;
this.startingGun = startingGun;
this.cs = cs;
}
public override void Run()
{
using Stream stream = new NetworkStream(cs);
BinaryReader intReader = new BinaryReader(stream);
BinaryWriter intWriter = new BinaryWriter(stream);
try
{
int id = intReader.ReadInt32();
if (id < 0)
{
throw new IOException("Client closed connection before communication started.");
}
startingGun.Wait();
intWriter.Write(43);
stream.Flush();
while (true)
{
int command = stream.ReadByte();
if (command < 0)
{
return; // closed
}
lock (localLock)
{
int currentLock = lockedID[0];
if (currentLock == -2)
{
return; // another thread got error, so we exit, too!
}
switch (command)
{
case 1:
// Locked
if (currentLock != -1)
{
lockedID[0] = -2;
throw new InvalidOperationException("id " + id + " got lock, but " + currentLock + " already holds the lock");
}
lockedID[0] = id;
break;
case 0:
// Unlocked
if (currentLock != id)
{
lockedID[0] = -2;
throw new InvalidOperationException("id " + id + " released the lock, but " + currentLock + " is the one holding the lock");
}
lockedID[0] = -1;
break;
default:
throw new Exception("Unrecognized command: " + command);
}
intWriter.Write((byte)command);
stream.Flush();
}
}
}
catch (IOException ioe)
{
throw new Exception(ioe.ToString(), ioe);
}
catch (Exception e)
{
// LUCENENET NOTE: We need to throw a new exception
// to ensure this is Exception and not some other type.
throw new Exception(e.ToString(), e);
}
finally
{
IOUtils.DisposeWhileHandlingException(cs);
}
}
}
}
}