Commit 062248b6 authored by Marc Gravell's avatar Marc Gravell

If the incoming socket isn't vanilla, we need to use async-read rather than...

If the incoming socket isn't vanilla, we need to use async-read rather than socket-poll (the value of .Available is meaningless)
parent a92d7ff3
......@@ -66,10 +66,10 @@ public void ConnectToSSLServer(int port, string sslHost)
// perf: sync/multi-threaded
TestConcurrent(db, key, 30, 10);
//TestConcurrent(db, key, 30, 20);
//TestConcurrent(db, key, 30, 30);
//TestConcurrent(db, key, 30, 40);
//TestConcurrent(db, key, 30, 50);
TestConcurrent(db, key, 30, 20);
TestConcurrent(db, key, 30, 30);
TestConcurrent(db, key, 30, 40);
TestConcurrent(db, key, 30, 50);
}
}
......
......@@ -7,7 +7,6 @@
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting.Lifetime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
......@@ -822,8 +821,6 @@ private void OnHeartbeat()
long now = Environment.TickCount;
Interlocked.Exchange(ref lastHeartbeatTicks, now);
Interlocked.Exchange(ref lastGlobalHeartbeatTicks, now);
var lease = pulse == null ? null : pulse.GetLifetimeService() as ILease;
if (lease != null) lease.Renew(TimeSpan.FromMinutes(5));
Trace("heartbeat");
var tmp = serverSnapshot;
......@@ -1238,8 +1235,6 @@ internal async Task<bool> ReconfigureAsync(bool first, bool reconfigureAll, Text
{
LogLocked(log, "Starting heartbeat...");
pulse = new Timer(heartbeat, this, MillisecondsPerHeartbeat, MillisecondsPerHeartbeat);
ILease lease = pulse.InitializeLifetimeService() as ILease;
if(lease != null) lease.Renew(TimeSpan.FromMinutes(5));
}
string stormLog = GetStormLog();
......
......@@ -470,10 +470,11 @@ static void WriteUnified(Stream stream, long value)
WriteRaw(stream, value, withLengthPrefix: true);
}
void ISocketCallback.Connected(Stream stream)
SocketMode ISocketCallback.Connected(Stream stream)
{
try
{
var socketMode = SocketMode.Poll;
// disallow connection in some cases
OnDebugAbort();
......@@ -486,6 +487,7 @@ void ISocketCallback.Connected(Stream stream)
var ssl = new SslStream(stream, false, config.CertificateValidationCallback, config.CertificateSelectionCallback, EncryptionPolicy.RequireEncryption);
ssl.AuthenticateAsClient(config.SslHost);
stream = ssl;
socketMode = SocketMode.Async;
}
OnWrapForLogging(ref stream, physicalName);
......@@ -495,12 +497,13 @@ void ISocketCallback.Connected(Stream stream)
multiplexer.Trace("Connected", physicalName);
bridge.OnConnected(this);
return socketMode;
}
catch (Exception ex)
{
RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex); // includes a bridge.OnDisconnected
multiplexer.Trace("Could not connect: " + ex.Message, physicalName);
return SocketMode.Abort;
}
}
......@@ -630,13 +633,27 @@ void ISocketCallback.Read()
do
{
int space = EnsureSpaceAndComputeBytesToRead();
int bytesRead = netStream.Read(ioBuffer, ioBufferBytes, space);
var tmp = netStream;
int bytesRead = tmp == null ? 0 : tmp.Read(ioBuffer, ioBufferBytes, space);
if (!ProcessReadBytes(bytesRead)) return; // EOF
} while (socketToken.Available != 0);
multiplexer.Trace("Buffer exhausted", physicalName);
// ^^^ note that the socket manager will call us again when there is something to do
}
catch (Exception ex)
{
RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
}
}
private bool ProcessReadBytes(int bytesRead)
{
if (bytesRead <= 0)
{
multiplexer.Trace("EOF", physicalName);
RecordConnectionFailed(ConnectionFailureType.SocketClosed);
return;
return false;
}
Interlocked.Exchange(ref lastReadTickCount, Environment.TickCount);
......@@ -657,15 +674,51 @@ void ISocketCallback.Read()
}
ioBufferBytes = count;
}
} while (socketToken.Available != 0);
multiplexer.Trace("Buffer exhausted", physicalName);
// ^^^ note that the socket manager will call us again when there is something to do
return true;
}
catch (Exception ex)
static readonly AsyncCallback endRead = result =>
{
PhysicalConnection physical;
if (result.CompletedSynchronously || (physical = result.AsyncState as PhysicalConnection) == null) return;
physical.multiplexer.Trace("Completed synchronously: processing in callback", physical.physicalName);
if(physical.EndReading(result)) physical.BeginReading();
};
private bool EndReading(IAsyncResult result)
{
try
{
var tmp = netStream;
int bytesRead = tmp == null ? 0 : tmp.EndRead(result);
return ProcessReadBytes(bytesRead);
} catch(Exception ex)
{
RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
return false;
}
}
void ISocketCallback.StartReading()
{
BeginReading();
}
void BeginReading()
{
bool keepReading;
do
{
keepReading = false;
int space = EnsureSpaceAndComputeBytesToRead();
multiplexer.Trace("Beginning async read...", physicalName);
var result = netStream.BeginRead(ioBuffer, ioBufferBytes, space, endRead, this);
if (result.CompletedSynchronously)
{
multiplexer.Trace("Completed synchronously: processing immediately", physicalName);
keepReading = EndReading(result);
}
} while (keepReading);
}
private RawResult ReadArray(byte[] buffer, ref int offset, ref int count)
{
var itemCount = ReadLineTerminatedString(ResultType.Integer, buffer, ref offset, ref count);
......
......@@ -155,7 +155,7 @@ private void ReadImpl()
foreach(var pair in allSocketPairs)
{
var callback = pair.Callback;
if (callback != null) callback.OnHeartbeat();
if (callback != null) try { callback.OnHeartbeat(); } catch { }
}
}
......@@ -275,9 +275,8 @@ public TimeValue(int microSeconds)
}
}
internal void Shutdown(SocketToken token)
private void Shutdown(Socket socket)
{
var socket = token.Socket;
if (socket != null)
{
lock (socketLookup)
......@@ -289,6 +288,10 @@ internal void Shutdown(SocketToken token)
try { socket.Dispose(); } catch { }
}
}
internal void Shutdown(SocketToken token)
{
Shutdown(token.Socket);
}
private readonly object QueueDrainSyncLock = new object();
static readonly WaitCallback HelpProcessItems = state =>
......@@ -483,8 +486,20 @@ private void EndConnect(IAsyncResult ar)
var callback = tuple.Item2;
socket.EndConnect(ar);
var netStream = new NetworkStream(socket, false);
callback.Connected(netStream);
var socketMode = callback == null ? SocketMode.Abort : callback.Connected(netStream);
switch (socketMode)
{
case SocketMode.Poll:
AddRead(socket, callback);
break;
case SocketMode.Async:
try { callback.StartReading(); }
catch { Shutdown(socket); }
break;
default:
Shutdown(socket);
break;
}
}
catch
{
......@@ -507,18 +522,29 @@ internal interface ISocketCallback
/// <summary>
/// Indicates that a socket has connected
/// </summary>
void Connected(Stream stream);
SocketMode Connected(Stream stream);
/// <summary>
/// Indicates that data is available on the socket, and that the consumer should read from the socket
/// Indicates that data is available on the socket, and that the consumer should read synchronously from the socket while there is data
/// </summary>
void Read();
/// <summary>
/// Indicates that we cannot know whether data is available, and that the consume should commence reading asynchronously
/// </summary>
void StartReading();
/// <summary>
/// Indicates that the socket has signalled an error condition
/// </summary>
void Error();
void OnHeartbeat();
}
internal enum SocketMode
{
Abort,
Poll,
Async
}
internal struct SocketToken
{
internal readonly Socket Socket;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment