Skip to content

Commit 77ff168

Browse files
committed
Various bug fixes and XML comments (see README for 1.0.1)
1 parent aafe973 commit 77ff168

5 files changed

Lines changed: 149 additions & 15 deletions

File tree

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
SIMPLSOCKETS 1.0.0
1+
SIMPLSOCKETS 1.0.1
22
===========
33

4+
45
A spinoff library of Dache that provides highly efficient, scalable, simple socket communication.
56

67
http://www.getdache.net
@@ -12,6 +13,23 @@ VERSION HISTORY
1213
============================================
1314

1415

16+
1.0.1
17+
------------------
18+
19+
20+
-Fixed logic error in BlockingQueue constructor where _queue wasn't actually assigned
21+
22+
-Fixed logic error in Pool where resetItemMethod was not always called when Popping an item
23+
24+
-Fixed atomicity of Error event so that it is raised exactly once on disconnection regardless of multithreaded use
25+
26+
-On error, communication methods now exit gracefully after Error event is raised (no bubbled exceptions)
27+
28+
-Exposed CurrentlyConnectedClients property on ISimplSocketServer
29+
30+
-Added XML comments to a few classes
31+
32+
1533
1.0.0
1634
------------------
1735

SimplSockets/BlockingQueue.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,38 @@
44

55
namespace SimplSockets
66
{
7+
/// <summary>
8+
/// A queue that wraps a regular generic queue but when empty will block Dequeue threads until an item is available.
9+
/// </summary>
10+
/// <typeparam name="T">The type of the object contained in the queue.</typeparam>
711
internal sealed class BlockingQueue<T>
812
{
13+
// The underlying queue
914
Queue<T> _queue = null;
15+
// The semaphore used for blocking
1016
Semaphore _semaphore = new Semaphore(0, Int32.MaxValue);
1117

18+
/// <summary>
19+
/// The constructor.
20+
/// </summary>
1221
public BlockingQueue()
1322
{
14-
new Queue<T>();
23+
_queue = new Queue<T>();
1524
}
1625

26+
/// <summary>
27+
/// The constructor.
28+
/// </summary>
29+
/// <param name="capacity">Sets the initial queue capacity.</param>
1730
public BlockingQueue(int capacity)
1831
{
1932
_queue = new Queue<T>(capacity);
2033
}
2134

35+
/// <summary>
36+
/// Enqueues an item.
37+
/// </summary>
38+
/// <param name="item">An item.</param>
2239
public void Enqueue(T item)
2340
{
2441
lock (_queue)
@@ -29,6 +46,10 @@ public void Enqueue(T item)
2946
_semaphore.Release();
3047
}
3148

49+
/// <summary>
50+
/// Dequeues an item. Will block if the queue is empty until an item becomes available.
51+
/// </summary>
52+
/// <returns>An item.</returns>
3253
public T Dequeue()
3354
{
3455
_semaphore.WaitOne();

SimplSockets/ISimplSocketServer.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public interface ISimplSocketServer : IDisposable
2626
/// </summary>
2727
void Close();
2828

29+
/// <summary>
30+
/// Gets the currently connected client count.
31+
/// </summary>
32+
int CurrentlyConnectedClients { get; }
33+
2934
/// <summary>
3035
/// An event that is fired whenever a message is received. Hook into this to process messages and potentially call Reply to send a response.
3136
/// </summary>

SimplSockets/Pool.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,27 @@
33

44
namespace SimplSockets
55
{
6+
/// <summary>
7+
/// A pool of objects that can be reused to manage memory efficiently.
8+
/// </summary>
9+
/// <typeparam name="T">The type of object that is pooled.</typeparam>
610
internal sealed class Pool<T> where T : class
711
{
12+
// The queue that holds the items
813
private readonly Queue<T> _queue = null;
14+
// The initial pool count
915
private readonly int _initialPoolCount = 0;
16+
// The method that creates a new item
1017
private readonly Func<T> _newItemMethod = null;
18+
// The method that resets an item's state
1119
private readonly Action<T> _resetItemMethod = null;
1220

21+
/// <summary>
22+
/// The constructor.
23+
/// </summary>
24+
/// <param name="poolCount">The count of items in the pool.</param>
25+
/// <param name="newItemMethod">The method that creates a new item.</param>
26+
/// <param name="resetItemMethod">The method that resets an item's state.</param>
1327
public Pool(int poolCount, Func<T> newItemMethod, Action<T> resetItemMethod)
1428
{
1529
_queue = new Queue<T>(poolCount);
@@ -18,6 +32,10 @@ public Pool(int poolCount, Func<T> newItemMethod, Action<T> resetItemMethod)
1832
_resetItemMethod = resetItemMethod;
1933
}
2034

35+
/// <summary>
36+
/// Pushes an item into the pool for later re-use.
37+
/// </summary>
38+
/// <param name="item">The item.</param>
2139
public void Push(T item)
2240
{
2341
// Limit queue size
@@ -32,10 +50,15 @@ public void Push(T item)
3250
}
3351
}
3452

53+
/// <summary>
54+
/// Pops an item out of the pool for use. The item will have its state reset.
55+
/// </summary>
56+
/// <returns></returns>
3557
public T Pop()
3658
{
3759
T result = null;
3860

61+
// Cheap check
3962
if (_queue.Count == 0)
4063
{
4164
result = _newItemMethod();
@@ -48,9 +71,15 @@ public T Pop()
4871

4972
lock (_queue)
5073
{
74+
// Double lock check
5175
if (_queue.Count == 0)
5276
{
53-
return _newItemMethod();
77+
result = _newItemMethod();
78+
if (_resetItemMethod != null)
79+
{
80+
_resetItemMethod(result);
81+
}
82+
return result;
5483
}
5584

5685
result = _queue.Dequeue();

SimplSockets/SimplSocket.cs

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ public void Connect(EndPoint endPoint)
181181
{
182182
HandleCommunicationError(_socket, ex);
183183
}
184+
catch (ObjectDisposedException)
185+
{
186+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
187+
return;
188+
}
184189

185190
// Get a message state from the pool
186191
var messageState = _messageStatePool.Pop();
@@ -222,7 +227,7 @@ public void Listen(EndPoint localEndpoint)
222227

223228
// Create socket
224229
_socket = _socketFunc();
225-
230+
226231
try
227232
{
228233
_socket.Bind(localEndpoint);
@@ -234,7 +239,10 @@ public void Listen(EndPoint localEndpoint)
234239
catch (SocketException ex)
235240
{
236241
HandleCommunicationError(_socket, ex);
237-
return;
242+
}
243+
catch (ObjectDisposedException)
244+
{
245+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
238246
}
239247
}
240248

@@ -288,6 +296,11 @@ public void Send(byte[] message)
288296
{
289297
HandleCommunicationError(_socket, ex);
290298
}
299+
catch (ObjectDisposedException)
300+
{
301+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
302+
return;
303+
}
291304
}
292305

293306
/// <summary>
@@ -324,6 +337,11 @@ public byte[] SendReceive(byte[] message)
324337
HandleCommunicationError(_socket, ex);
325338
return null;
326339
}
340+
catch (ObjectDisposedException)
341+
{
342+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
343+
return null;
344+
}
327345

328346
// Wait for our message to go ahead from the receive callback
329347
multiplexerData.ManualResetEvent.WaitOne();
@@ -370,6 +388,11 @@ public void Reply(byte[] message, ReceivedMessage receivedMessage)
370388
HandleCommunicationError(receivedMessage.Socket, ex);
371389
return;
372390
}
391+
catch (ObjectDisposedException)
392+
{
393+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
394+
return;
395+
}
373396

374397
// Put received message back in the pool
375398
_receiveMessagePool.Push(receivedMessage);
@@ -409,6 +432,12 @@ private void AcceptCallback(IAsyncResult asyncResult)
409432
HandleCommunicationError(_socket, ex);
410433
return;
411434
}
435+
catch (ObjectDisposedException)
436+
{
437+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
438+
return;
439+
}
440+
412441
// Turn on or off Nagle algorithm
413442
handler.NoDelay = !_useNagleAlgorithm;
414443

@@ -422,6 +451,11 @@ private void AcceptCallback(IAsyncResult asyncResult)
422451
HandleCommunicationError(_socket, ex);
423452
return;
424453
}
454+
catch (ObjectDisposedException)
455+
{
456+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
457+
return;
458+
}
425459

426460
// Do not proceed until we have room to do so
427461
_maxConnectionsSemaphore.WaitOne();
@@ -442,6 +476,11 @@ private void AcceptCallback(IAsyncResult asyncResult)
442476
HandleCommunicationError(handler, ex);
443477
return;
444478
}
479+
catch (ObjectDisposedException)
480+
{
481+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
482+
return;
483+
}
445484

446485
// Create receive queue for this client
447486
_receiveBufferQueueLock.EnterWriteLock();
@@ -473,6 +512,11 @@ private void SendCallback(IAsyncResult asyncResult)
473512
HandleCommunicationError(socket, ex);
474513
return;
475514
}
515+
catch (ObjectDisposedException)
516+
{
517+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
518+
return;
519+
}
476520
}
477521

478522
private void ReceiveCallback(IAsyncResult asyncResult)
@@ -493,6 +537,11 @@ private void ReceiveCallback(IAsyncResult asyncResult)
493537
HandleCommunicationError(messageState.Handler, ex);
494538
return;
495539
}
540+
catch (ObjectDisposedException)
541+
{
542+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
543+
return;
544+
}
496545

497546
if (bytesRead > 0)
498547
{
@@ -522,7 +571,10 @@ private void ReceiveCallback(IAsyncResult asyncResult)
522571
catch (SocketException ex)
523572
{
524573
HandleCommunicationError(messageState.Handler, ex);
525-
return;
574+
}
575+
catch (ObjectDisposedException)
576+
{
577+
// If disposed, handle communication error was already done and we're just catching up on other threads. Supress it.
526578
}
527579
}
528580
}
@@ -664,17 +716,26 @@ private void ProcessReceivedMessage(MessageState messageState)
664716
/// <param name="ex">The exception that the socket raised.</param>
665717
private void HandleCommunicationError(Socket socket, Exception ex)
666718
{
667-
// Close the socket
668-
try
719+
lock (socket)
669720
{
670-
socket.Shutdown(SocketShutdown.Both);
671-
}
672-
catch
673-
{
674-
// Ignore
675-
}
721+
// Close the socket
722+
try
723+
{
724+
socket.Shutdown(SocketShutdown.Both);
725+
}
726+
catch (ObjectDisposedException)
727+
{
728+
// Socket was already closed/disposed, so return out to prevent raising the Error event multiple times
729+
// This is most likely to happen when an error occurs during heavily multithreaded use
730+
return;
731+
}
732+
catch
733+
{
734+
// Ignore
735+
}
676736

677-
socket.Close();
737+
socket.Close();
738+
}
678739

679740
// Release all multiplexer clients by signalling them
680741
_clientMultiplexerLock.EnterReadLock();

0 commit comments

Comments
 (0)