From adb1e6b97a0392f9885102699602c4cf80b5c5db Mon Sep 17 00:00:00 2001 From: dudu Date: Fri, 31 Aug 2018 22:07:40 +0800 Subject: [PATCH 1/3] Update pacakges --- Enyim.Caching.Tests/Enyim.Caching.Tests.csproj | 15 +++++++++------ Enyim.Caching/Enyim.Caching.csproj | 12 ++++++------ MemcachedTest/MemcachedTest.csproj | 15 +++++++++------ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Enyim.Caching.Tests/Enyim.Caching.Tests.csproj b/Enyim.Caching.Tests/Enyim.Caching.Tests.csproj index 530ae806..7ae269d9 100644 --- a/Enyim.Caching.Tests/Enyim.Caching.Tests.csproj +++ b/Enyim.Caching.Tests/Enyim.Caching.Tests.csproj @@ -4,12 +4,15 @@ false - - - - - - + + + + + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/Enyim.Caching/Enyim.Caching.csproj b/Enyim.Caching/Enyim.Caching.csproj index 92618039..4b1af76a 100755 --- a/Enyim.Caching/Enyim.Caching.csproj +++ b/Enyim.Caching/Enyim.Caching.csproj @@ -16,13 +16,13 @@ - - - + + + - - - + + + diff --git a/MemcachedTest/MemcachedTest.csproj b/MemcachedTest/MemcachedTest.csproj index 530ae806..7ae269d9 100755 --- a/MemcachedTest/MemcachedTest.csproj +++ b/MemcachedTest/MemcachedTest.csproj @@ -4,12 +4,15 @@ false - - - - - - + + + + + + + all + runtime; build; native; contentfiles; analyzers + From f67d7fd388a49306298d477d3060ac01370b17db Mon Sep 17 00:00:00 2001 From: dudu Date: Thu, 13 Sep 2018 16:18:10 +0800 Subject: [PATCH 2/3] Use new sockets api introduced in .net core 2.1 --- Enyim.Caching/Memcached/AsyncPooledSocket.cs | 52 ++-- Enyim.Caching/Memcached/MemcachedNode.cs | 46 +-- Enyim.Caching/Memcached/PooledSocket.cs | 55 ++-- .../Memcached/Socket/BlockingBufferManager.cs | 221 ------------- .../Memcached/Socket/SocketAwaitable.cs | 234 -------------- .../Memcached/Socket/SocketAwaitablePool.cs | 259 ---------------- .../Memcached/Socket/SocketAwaiter.cs | 192 ------------ Enyim.Caching/Memcached/Socket/SocketEx.cs | 292 ------------------ SampleWebApp/appsettings.json | 7 +- 9 files changed, 68 insertions(+), 1290 deletions(-) delete mode 100644 Enyim.Caching/Memcached/Socket/BlockingBufferManager.cs delete mode 100644 Enyim.Caching/Memcached/Socket/SocketAwaitable.cs delete mode 100644 Enyim.Caching/Memcached/Socket/SocketAwaitablePool.cs delete mode 100644 Enyim.Caching/Memcached/Socket/SocketAwaiter.cs delete mode 100644 Enyim.Caching/Memcached/Socket/SocketEx.cs diff --git a/Enyim.Caching/Memcached/AsyncPooledSocket.cs b/Enyim.Caching/Memcached/AsyncPooledSocket.cs index 86e70962..aa48de57 100644 --- a/Enyim.Caching/Memcached/AsyncPooledSocket.cs +++ b/Enyim.Caching/Memcached/AsyncPooledSocket.cs @@ -1,19 +1,18 @@ //#define DEBUG_IO +using Microsoft.Extensions.Logging; using System; -using System.Linq; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net; using System.Net.Sockets; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Dawn.Net.Sockets; -using System.Runtime.InteropServices; -using System.Reflection; -using System.Runtime.CompilerServices; namespace Enyim.Caching.Memcached { @@ -29,7 +28,7 @@ public partial class AsyncPooledSocket : IDisposable public AsyncPooledSocket(ILogger logger) { _logger = logger; - _isAlive = true; + _isAlive = true; } private async Task CreateSocketAsync(DnsEndPoint endpoint, TimeSpan connectionTimeout, TimeSpan receiveTimeout) @@ -65,7 +64,7 @@ private async Task CreateSocketAsync(DnsEndPoint endpoint, TimeSpan connectionTi _socket.NoDelay = true; _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); - _inputStream = new NetworkStream(_socket, ownsSocket: true); + _inputStream = new NetworkStream(_socket, ownsSocket: true); } //From https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs @@ -78,7 +77,7 @@ public void Initialize(CancellationToken cancellationToken) { CancellationToken = cancellationToken; var b = new AsyncTaskMethodBuilder(); - var ignored = b.Task; + var ignored = b.Task; Builder = b; } @@ -104,7 +103,7 @@ protected override void OnCompleted(SocketAsyncEventArgs _) break; } } - } + } public Action CleanupCallback { get; set; } @@ -230,12 +229,9 @@ public int ReadByte() public async Task ReadBytesAsync(int count) { - using (var awaitable = new SocketAwaitable()) - { - awaitable.Buffer = new ArraySegment(new byte[count], 0, count); - await _socket.ReceiveAsync(awaitable); - return awaitable.Transferred.Array; - } + var buffer = new ArraySegment(new byte[count], 0, count); + await _socket.ReceiveAsync(buffer, SocketFlags.None); + return buffer.Array; } /// @@ -315,24 +311,14 @@ public void Write(IList> buffers) public async Task WriteSync(IList> buffers) { - using (var awaitable = new SocketAwaitable()) + try { - awaitable.Arguments.BufferList = buffers; - try - { - await _socket.SendAsync(awaitable); - } - catch - { - _isAlive = false; - ThrowHelper.ThrowSocketWriteError(_socket.RemoteEndPoint, awaitable.Arguments.SocketError); - } - - if (awaitable.Arguments.SocketError != SocketError.Success) - { - _isAlive = false; - ThrowHelper.ThrowSocketWriteError(_socket.RemoteEndPoint, awaitable.Arguments.SocketError); - } + await _socket.SendAsync(buffers, SocketFlags.None); + } + catch (Exception ex) + { + _isAlive = false; + _logger.LogError(ex, nameof(PooledSocket.WriteSync)); } } diff --git a/Enyim.Caching/Memcached/MemcachedNode.cs b/Enyim.Caching/Memcached/MemcachedNode.cs index d5ed4ae8..1185194a 100755 --- a/Enyim.Caching/Memcached/MemcachedNode.cs +++ b/Enyim.Caching/Memcached/MemcachedNode.cs @@ -1,18 +1,18 @@ +using Enyim.Caching.Configuration; +using Enyim.Caching.Memcached.Protocol.Binary; +using Enyim.Caching.Memcached.Results; +using Enyim.Caching.Memcached.Results.Extensions; +using Enyim.Collections; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Net; -using System.Threading; -using Enyim.Caching.Configuration; -using Enyim.Collections; -using System.Security; -using Enyim.Caching.Memcached.Protocol.Binary; using System.Runtime.Serialization; -using System.IO; -using Enyim.Caching.Memcached.Results; -using Enyim.Caching.Memcached.Results.Extensions; +using System.Security; +using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; namespace Enyim.Caching.Memcached { @@ -27,13 +27,13 @@ public class MemcachedNode : IMemcachedNode private bool isDisposed; - private DnsEndPoint endPoint; - private ISocketPoolConfiguration config; + private readonly DnsEndPoint endPoint; + private readonly ISocketPoolConfiguration config; private InternalPoolImpl internalPoolImpl; private bool isInitialized; public MemcachedNode( - DnsEndPoint endpoint, + DnsEndPoint endpoint, ISocketPoolConfiguration socketPoolConfig, ILogger logger) { @@ -186,7 +186,7 @@ void IDisposable.Dispose() private class InternalPoolImpl : IDisposable { private readonly ILogger _logger; - private bool _isDebugEnabled; + private readonly bool _isDebugEnabled; /// /// A list of already connected but free to use sockets @@ -197,18 +197,18 @@ private class InternalPoolImpl : IDisposable private bool isAlive; private DateTime markedAsDeadUtc; - private int minItems; - private int maxItems; + private readonly int minItems; + private readonly int maxItems; private MemcachedNode ownerNode; - private EndPoint endPoint; - private TimeSpan queueTimeout; + private readonly EndPoint endPoint; + private readonly TimeSpan queueTimeout; private Semaphore semaphore; - private object initLock = new Object(); + private readonly object initLock = new Object(); internal InternalPoolImpl( - MemcachedNode ownerNode, + MemcachedNode ownerNode, ISocketPoolConfiguration config, ILogger logger) { @@ -519,9 +519,9 @@ protected internal virtual PooledSocket CreateSocket() { return new PooledSocket(this.endPoint, this.config.ConnectionTimeout, this.config.ReceiveTimeout, _logger); } - catch(Exception ex) + catch (Exception ex) { - _logger.LogError(new EventId (this.GetHashCode(), nameof(MemcachedNode) ), ex, $"Create {nameof(PooledSocket)}"); + _logger.LogError(new EventId(this.GetHashCode(), nameof(MemcachedNode)), ex, $"Create {nameof(PooledSocket)}"); throw; } } @@ -582,12 +582,16 @@ protected virtual IPooledSocketResult ExecuteOperation(IOperation op) protected async virtual Task ExecuteOperationAsync(IOperation op) { + _logger.LogDebug($"ExecuteOperationAsync({op})"); + var result = this.Acquire(); if (result.Success && result.HasValue) { try { var pooledSocket = result.Value; + + //if Get, call BinaryRequest.CreateBuffer() var b = op.GetBuffer(); diff --git a/Enyim.Caching/Memcached/PooledSocket.cs b/Enyim.Caching/Memcached/PooledSocket.cs index a1aa6237..96e0e67c 100755 --- a/Enyim.Caching/Memcached/PooledSocket.cs +++ b/Enyim.Caching/Memcached/PooledSocket.cs @@ -1,18 +1,16 @@ -//#define DEBUG_IO +using Microsoft.Extensions.Logging; using System; -using System.Linq; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net; using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Dawn.Net.Sockets; -using System.Runtime.InteropServices; -using System.Reflection; namespace Enyim.Caching.Memcached { @@ -34,7 +32,7 @@ public PooledSocket(DnsEndPoint endpoint, TimeSpan connectionTimeout, TimeSpan r this.isAlive = true; - var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.NoDelay = true; var timeout = connectionTimeout == TimeSpan.MaxValue @@ -46,14 +44,14 @@ public PooledSocket(DnsEndPoint endpoint, TimeSpan connectionTimeout, TimeSpan r : (int)receiveTimeout.TotalMilliseconds; socket.ReceiveTimeout = rcv; - socket.SendTimeout = rcv; + socket.SendTimeout = rcv; ConnectWithTimeout(socket, endpoint, timeout); this.socket = socket; this.endpoint = endpoint; - this.inputStream = new NetworkStream(socket); + this.inputStream = new NetworkStream(socket); } private void ConnectWithTimeout(Socket socket, DnsEndPoint endpoint, int timeout) @@ -80,13 +78,13 @@ private void ConnectWithTimeout(Socket socket, DnsEndPoint endpoint, int timeout args.Completed += (s, e) => mres.Set(); if (socket.ConnectAsync(args)) { - if(!mres.Wait(timeout)) + if (!mres.Wait(timeout)) { throw new TimeoutException("Could not connect to " + endpoint); } } - } - } + } + } public Action CleanupCallback { get; set; } @@ -212,12 +210,9 @@ public int ReadByte() public async Task ReadBytesAsync(int count) { - using (var awaitable = new SocketAwaitable()) - { - awaitable.Buffer = new ArraySegment(new byte[count], 0, count); - await this.socket.ReceiveAsync(awaitable); - return awaitable.Transferred.Array; - } + var buffer = new ArraySegment(new byte[count], 0, count); + await this.socket.ReceiveAsync(buffer, SocketFlags.None); + return buffer.Array; } /// @@ -297,24 +292,14 @@ public void Write(IList> buffers) public async Task WriteSync(IList> buffers) { - using (var awaitable = new SocketAwaitable()) + try { - awaitable.Arguments.BufferList = buffers; - try - { - await this.socket.SendAsync(awaitable); - } - catch - { - this.isAlive = false; - ThrowHelper.ThrowSocketWriteError(this.endpoint, awaitable.Arguments.SocketError); - } - - if (awaitable.Arguments.SocketError != SocketError.Success) - { - this.isAlive = false; - ThrowHelper.ThrowSocketWriteError(this.endpoint, awaitable.Arguments.SocketError); - } + await socket.SendAsync(buffers, SocketFlags.None); + } + catch (Exception ex) + { + isAlive = false; + _logger.LogError(ex, nameof(PooledSocket.WriteSync)); } } diff --git a/Enyim.Caching/Memcached/Socket/BlockingBufferManager.cs b/Enyim.Caching/Memcached/Socket/BlockingBufferManager.cs deleted file mode 100644 index 25249bda..00000000 --- a/Enyim.Caching/Memcached/Socket/BlockingBufferManager.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright © 2013 Şafak Gür. All rights reserved. -// Use of this source code is governed by the MIT License (MIT). - -namespace Dawn.Net.Sockets -{ - using System; - using System.Collections.Concurrent; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - - /// - /// Represents a buffer manager that when a buffer is requested, blocks the calling thread - /// until a buffer is available. - /// - [DebuggerDisplay("Available: {AvailableBuffers} * {BufferSize}B | Disposed: {IsDisposed}")] - public sealed class BlockingBufferManager : IDisposable - { - #region Fields - /// - /// The full name of the type. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private static readonly string typeName = typeof(BlockingBufferManager).FullName; - - /// - /// Size of the buffers provided by the buffer manager. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly int bufferSize; - - /// - /// Data block that provides the underlying storage for the buffers provided by the - /// buffer manager. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly byte[] data; - - /// - /// Zero-based starting indices in , of the available segments. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly BlockingCollection availableIndices; - - /// - /// Zero-based starting indices in , of the unavailable segments. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly ConcurrentDictionary usedIndices; - - /// - /// A value indicating whether the has - /// been called. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private bool isDisposed; - #endregion - - #region Constructors - /// - /// Initializes a new instance of the class. - /// - /// - /// Size of the buffers that will be provided by the buffer manager. - /// - /// - /// Maximum amount of the buffers that will be concurrently used. - /// - /// - /// or is less than one. - /// - public BlockingBufferManager(int bufferSize, int bufferCount) - { - if (bufferSize < 1) - throw new ArgumentOutOfRangeException( - "bufferSize", - bufferSize, - "Buffer size must not be less than one."); - - if (bufferCount < 1) - throw new ArgumentOutOfRangeException( - "bufferCount", - bufferCount, - "Buffer count must not be less than one."); - - this.bufferSize = bufferSize; - this.data = new byte[bufferSize * bufferCount]; - this.availableIndices = new BlockingCollection(bufferCount); - for (int i = 0; i < bufferCount; i++) - this.availableIndices.Add(bufferSize * i); - - this.usedIndices = new ConcurrentDictionary(bufferCount, bufferCount); - } - #endregion - - #region Properties - /// - /// Gets the size of the buffers provided by the buffer manager. - /// - public int BufferSize - { - get { return this.bufferSize; } - } - - /// - /// Gets the number of available buffers provided by the buffer manager. - /// - public int AvailableBuffers - { - get - { - lock (this.availableIndices) - return !this.isDisposed ? this.availableIndices.Count : 0; - } - } - - /// - /// Gets a value indicating whether the is - /// disposed. - /// - public bool IsDisposed - { - get { return this.isDisposed; } - } - #endregion - - #region Methods - /// - /// Gets an available buffer. This method blocks the calling thread until a buffer - /// becomes available. - /// - /// - /// An with as its - /// count. - /// - /// - /// The has been disposed. - /// - public ArraySegment GetBuffer() - { - lock (this.availableIndices) - if (this.isDisposed) - throw new ObjectDisposedException(typeName); - - int index; - try - { - index = this.availableIndices.Take(); - } - catch (InvalidOperationException) - { - throw new ObjectDisposedException(typeName); - } - - this.usedIndices[index] = index; - return new ArraySegment(this.data, index, this.BufferSize); - } - - /// - /// Releases the specified buffer and makes it available for future use. - /// - /// - /// Buffer to release. - /// - /// - /// 's array is null, count is not , - /// or the offset is invalid; i.e. not taken from the current buffer manager. - /// - /// - /// The has been disposed. - /// - public void ReleaseBuffer(ArraySegment buffer) - { - lock (this.availableIndices) - if (this.isDisposed) - throw new ObjectDisposedException(typeName); - - int offset; - if (buffer.Array != this.data - || buffer.Count != this.BufferSize - || !this.usedIndices.TryRemove(buffer.Offset, out offset)) - throw new ArgumentException( - "Buffer is not taken from the current buffer manager.", - "buffer"); - - try - { - this.availableIndices.Add(offset); - } - catch (InvalidOperationException) - { - throw new ObjectDisposedException(typeName); - } - } - - /// - /// Releases all resources used by the current instance of - /// . Underlying data block is an exception if it's - /// used in unmanaged operations that require pinning the buffer (e.g. - /// ). - /// - [SuppressMessage( - "Microsoft.Usage", - "CA2213:DisposableFieldsShouldBeDisposed", - Justification = "BlockingCollection.Dispose is not thread-safe.", - MessageId = "availableIndices")] - public void Dispose() - { - lock (this.availableIndices) - if (!this.isDisposed) - { - this.availableIndices.CompleteAdding(); - int i; - while (this.availableIndices.TryTake(out i)) - this.usedIndices[i] = i; - - this.isDisposed = true; - } - } - #endregion - } -} \ No newline at end of file diff --git a/Enyim.Caching/Memcached/Socket/SocketAwaitable.cs b/Enyim.Caching/Memcached/Socket/SocketAwaitable.cs deleted file mode 100644 index b4e74bfa..00000000 --- a/Enyim.Caching/Memcached/Socket/SocketAwaitable.cs +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright © 2013 Şafak Gür. All rights reserved. -// Use of this source code is governed by the MIT License (MIT). - -namespace Dawn.Net.Sockets -{ - using System; - using System.Diagnostics; - using System.Net; - using System.Net.Sockets; - - /// - /// Represents awaitable and re-usable socket arguments. - /// - public sealed class SocketAwaitable : IDisposable - { - #region Fields - /// - /// A cached, empty array of bytes. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - internal static readonly byte[] EmptyArray = new byte[0]; - - /// - /// Asynchronous socket arguments for internal use. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly SocketAsyncEventArgs arguments = new SocketAsyncEventArgs(); - - /// - /// An object that can be used to synchronize access to the - /// . - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly object syncRoot = new object(); - - /// - /// An awaiter that waits the completions of asynchronous socket operations. - /// - private readonly SocketAwaiter awaiter; - - /// - /// The data buffer segment that holds the transferred bytes. - /// - private ArraySegment transferred; - - /// - /// A value indicating whether the is disposed. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private bool isDisposed; - - /// - /// A value that indicates whether the socket operations using the - /// should capture the current synchronization context - /// and attempt to marshall their continuations back to the captured context. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private bool shouldCaptureContext; - #endregion - - #region Constructors - /// - /// Initializes a new instance of the class. - /// - public SocketAwaitable() - { - this.awaiter = new SocketAwaiter(this); - this.transferred = new ArraySegment(EmptyArray); - } - #endregion - - #region Properties - /// - /// Gets the socket created for accepting a connection with an asynchronous socket - /// method. - /// - public Socket AcceptSocket - { - get { return this.Arguments.AcceptSocket; } - } - - /// - /// Gets or sets the data buffer to use with the asynchronous socket methods. - /// - public ArraySegment Buffer - { - get - { - lock (this.syncRoot) - return new ArraySegment( - this.Arguments.Buffer ?? EmptyArray, - this.Arguments.Offset, - this.Arguments.Count); - } - - set - { - lock (this.syncRoot) - this.Arguments.SetBuffer(value.Array ?? EmptyArray, value.Offset, value.Count); - } - } - - /// - /// Gets the data buffer segment that holds the transferred bytes. - /// - public ArraySegment Transferred - { - get { return this.transferred; } - internal set { this.transferred = value; } - } - - /// - /// Gets the exception in the case of a connection failure when a - /// was used. - /// - public Exception ConnectByNameError - { - get { return this.Arguments.ConnectByNameError; } - } - - /// - /// Gets the type of socket operation most recently performed with this context object. - /// - public SocketAsyncOperation LastOperation - { - get { return this.Arguments.LastOperation; } - } - - /// - /// Gets or sets the remote IP endpoint for an asynchronous operation. - /// - public EndPoint RemoteEndPoint - { - get { return this.Arguments.RemoteEndPoint; } - set { this.Arguments.RemoteEndPoint = value; } - } - - /// - /// Gets or sets the behavior of an asynchronous operation. - /// - public SocketFlags SocketFlags - { - get { return this.Arguments.SocketFlags; } - set { this.Arguments.SocketFlags = value; } - } - - /// - /// Gets or sets a value indicating whether the socket operations using the - /// should capture the current synchronization context - /// and attempt to marshall their continuations back to the captured context. - /// - /// - /// A socket operation was already in progress using the current - /// . - /// - public bool ShouldCaptureContext - { - get - { - return this.shouldCaptureContext; - } - - set - { - lock (this.awaiter.SyncRoot) - if (this.awaiter.IsCompleted) - this.shouldCaptureContext = value; - else - throw new InvalidOperationException( - "A socket operation is already in progress" - + " using the same awaitable arguments."); - } - } - - /// - /// Gets a value indicating whether the is disposed. - /// - public bool IsDisposed - { - get { return this.isDisposed; } - } - - /// - /// Gets the asynchronous socket arguments for internal use. - /// - internal SocketAsyncEventArgs Arguments - { - get { return this.arguments; } - } - #endregion - - #region Methods - /// - /// Clears the buffer, accepted socket, remote endpoint and socket flags to prepare - /// for pooling. - /// - public void Clear() - { - this.Arguments.AcceptSocket = null; - this.Arguments.SetBuffer(EmptyArray, 0, 0); - this.RemoteEndPoint = null; - this.SocketFlags = SocketFlags.None; - this.Transferred = new ArraySegment(EmptyArray); - - // TODO: Remove with SocketAwaitable.UserToken. - this.Arguments.UserToken = null; - } - - /// - /// Gets the awaitable object to await a socket operation. - /// - /// - /// A used to await this . - /// - public SocketAwaiter GetAwaiter() - { - return this.awaiter; - } - - /// - /// Releases all resources used by . - /// - public void Dispose() - { - lock (this.syncRoot) - if (!this.IsDisposed) - { - this.arguments.Dispose(); - this.isDisposed = true; - } - } - #endregion - } -} \ No newline at end of file diff --git a/Enyim.Caching/Memcached/Socket/SocketAwaitablePool.cs b/Enyim.Caching/Memcached/Socket/SocketAwaitablePool.cs deleted file mode 100644 index dfcf6250..00000000 --- a/Enyim.Caching/Memcached/Socket/SocketAwaitablePool.cs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright © 2013 Şafak Gür. All rights reserved. -// Use of this source code is governed by the MIT License (MIT). - -namespace Dawn.Net.Sockets -{ - using System; - using System.Collections; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Diagnostics; - - /// - /// Represents a thread-safe pool of awaitable socket arguments. - /// - [DebuggerDisplay("Count: {Count}")] - public sealed class SocketAwaitablePool - : ICollection, IDisposable, IEnumerable - { - #region Fields - /// - /// The full name of the type. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private static readonly string typeName = typeof(SocketAwaitablePool).FullName; - - /// - /// A thread-safe, unordered collection of awaitable socket arguments. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly ConcurrentBag bag; - - /// - /// A value indicating whether the is disposed. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private bool isDisposed; - #endregion - - #region Constructors - /// - /// Initializes a new instance of the class. - /// - /// - /// The initial size of the pool. - /// - /// - /// is less than zero. - /// - public SocketAwaitablePool(int initialCount = 0) - { - if (initialCount < 0) - throw new ArgumentOutOfRangeException( - "initialCount", - initialCount, - "Initial count must not be less than zero."); - - this.bag = new ConcurrentBag(); - for (int i = 0; i < initialCount; i++) - this.Add(new SocketAwaitable()); - } - #endregion - - #region Properties - /// - /// Gets the number of awaitable socket arguments in the - /// . - /// - public int Count - { - get - { - lock (this.bag) - return !this.IsDisposed ? this.bag.Count : 0; - } - } - - /// - /// Gets a value indicating whether the is disposed. - /// - public bool IsDisposed - { - get - { - lock (this.bag) - return this.isDisposed; - } - } - - /// - /// Gets a value indicating whether access to the is - /// synchronized with the property. - /// This property always returns false. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - bool ICollection.IsSynchronized - { - get { return false; } - } - - /// - /// Gets an object that can be used to synchronize access to the - /// . This property is not supported. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - object ICollection.SyncRoot - { - get { throw new NotSupportedException( - "Synchronization using SyncRoot is not supported."); } - } - #endregion - - #region Methods - /// - /// Adds a instance to the pool. - /// - /// - /// Awaitable socket arguments to add. - /// - /// - /// is null. - /// - public void Add(SocketAwaitable awaitable) - { - if (awaitable == null) - throw new ArgumentNullException( - "awaitable", - "Awaitable socket arguments to pull must not be null."); - - lock (this.bag) - if (!this.IsDisposed) - this.bag.Add(awaitable); - else - awaitable.Dispose(); - } - - /// - /// Removes and returns a instance from the pool, if the - /// pool has one; otherwise, returns a new instance. - /// - /// - /// A instance from the pool, if the pool has one; - /// otherwise, a new instance. - /// - /// - /// The has been disposed. - /// - public SocketAwaitable Take() - { - SocketAwaitable awaitable; - lock (this.bag) - if (!this.IsDisposed) - return this.bag.TryTake(out awaitable) ? awaitable : new SocketAwaitable(); - else - throw new ObjectDisposedException(typeName); - } - - /// - /// Copies the pool elements to an existing one-dimensional array, starting at the - /// specified offset. - /// - /// - /// The one-dimensional array of awaitable socket arguments that is the destination of - /// the arguments copied from the pool. Array must have zero-based indexing. - /// - /// - /// The zero-based index in of which copying begins. - /// - /// - /// is null. - /// - /// - /// is less than zero. - /// - /// - /// is not a single-dimensional array of - /// instances. - /// -or- - /// is equal to or greater than the length of - /// - /// -or- - /// The number of elements in the source pool is greater than the available space from - /// to the end of . - /// - /// - /// The has been disposed. - /// - void ICollection.CopyTo(Array array, int offset) - { - if (array == null) - throw new ArgumentNullException("array", "Array must not be null."); - - if (offset < 0) - throw new ArgumentOutOfRangeException("offset", offset, "Index must not be null."); - - if (!(array is SocketAwaitable[])) - { - var message = string.Format( - "Array must be a single-dimensional array of `{0}`.", - typeof(SocketAwaitable).FullName); - - throw new ArgumentException(message, "array"); - } - - lock (this.bag) - if (!this.IsDisposed) - this.bag.CopyTo(array as SocketAwaitable[], offset); - else - throw new ObjectDisposedException(typeName); - } - - /// - /// Returns an enumerator that iterates through the . - /// - /// - /// An enumerator for the contents of the . - /// - /// - /// The has been disposed. - /// - public IEnumerator GetEnumerator() - { - if (!this.IsDisposed) - return this.bag.GetEnumerator(); - else - throw new ObjectDisposedException(typeName); - } - - /// - /// Returns a non-generic enumerator that iterates through the - /// . - /// - /// - /// An enumerator for the contents of the . - /// - /// - /// The has been disposed. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - - /// - /// Release all resources used by the . - /// - public void Dispose() - { - lock (this.bag) - if (!this.IsDisposed) - { - for (int i = 0; i < this.Count; i++) - this.Take().Dispose(); - - this.isDisposed = true; - } - } - #endregion - } -} diff --git a/Enyim.Caching/Memcached/Socket/SocketAwaiter.cs b/Enyim.Caching/Memcached/Socket/SocketAwaiter.cs deleted file mode 100644 index ca83b45c..00000000 --- a/Enyim.Caching/Memcached/Socket/SocketAwaiter.cs +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright © 2013 Şafak Gür. All rights reserved. -// Use of this source code is governed by the MIT License (MIT). - -namespace Dawn.Net.Sockets -{ - using System; - using System.Diagnostics; - using System.Net.Sockets; - using System.Runtime.CompilerServices; - using System.Threading; - using System.Threading.Tasks; - - /// - /// Provides an object that waits for the completion of a . - /// This class is not thread-safe: It doesn't support multiple concurrent awaiters. - /// - [DebuggerDisplay("Completed: {IsCompleted}")] - public sealed class SocketAwaiter : INotifyCompletion - { - #region Fields - /// - /// A sentinel delegate that does nothing. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private static readonly Action sentinel = delegate { }; - - /// - /// The asynchronous socket arguments to await. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly SocketAwaitable awaitable; - - /// - /// An object to synchronize access to the awaiter for validations. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly object syncRoot = new object(); - - /// - /// The continuation delegate that will be called after the current operation is - /// awaited. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private Action continuation; - - /// - /// A value indicating whether the asynchronous operation is completed. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private bool isCompleted = true; - - /// - /// A synchronization context for marshaling the continuation delegate to. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private SynchronizationContext syncContext; - #endregion - - #region Constructors - /// - /// Initializes a new instance of the class. - /// - /// - /// The asynchronous socket arguments to await. - /// - internal SocketAwaiter(SocketAwaitable awaitable) - { - this.awaitable = awaitable; - this.awaitable.Arguments.Completed += delegate - { - var c = this.continuation - ?? Interlocked.CompareExchange(ref this.continuation, sentinel, null); - - if (c != null) - { - var syncContext = this.awaitable.ShouldCaptureContext - ? this.SyncContext - : null; - - this.Complete(); - if (syncContext != null) - syncContext.Post(s => c.Invoke(), null); - else - c.Invoke(); - } - }; - } - #endregion - - #region Properties - /// - /// Gets a value indicating whether the asynchronous operation is completed. - /// - public bool IsCompleted - { - get { return this.isCompleted; } - } - - /// - /// Gets an object to synchronize access to the awaiter for validations. - /// - internal object SyncRoot - { - get { return this.syncRoot; } - } - - /// - /// Gets or sets a synchronization context for marshaling the continuation delegate to. - /// - internal SynchronizationContext SyncContext - { - get { return this.syncContext; } - set { this.syncContext = value; } - } - #endregion - - #region Methods - /// - /// Gets the result of the asynchronous socket operation. - /// - /// - /// A that represents the result of the socket operations. - /// - public SocketError GetResult() - { - return this.awaitable.Arguments.SocketError; - } - - /// - /// Gets invoked when the asynchronous operation is completed and runs the specified - /// delegate as continuation. - /// - /// - /// Continuation to run. - /// - void INotifyCompletion.OnCompleted(Action continuation) - { - if (this.continuation == sentinel - || Interlocked.CompareExchange( - ref this.continuation, - continuation, - null) == sentinel) - { - this.Complete(); - if (!this.awaitable.ShouldCaptureContext) - Task.Run(continuation); - else - Task.Factory.StartNew( - continuation, - CancellationToken.None, - TaskCreationOptions.DenyChildAttach, - TaskScheduler.FromCurrentSynchronizationContext()); - } - } - - /// - /// Resets this awaiter for re-use. - /// - internal void Reset() - { - this.awaitable.Arguments.AcceptSocket = null; - this.awaitable.Arguments.SocketError = SocketError.AlreadyInProgress; - this.awaitable.Transferred = new ArraySegment(SocketAwaitable.EmptyArray); - this.isCompleted = false; - this.continuation = null; - } - - /// - /// Sets to true, nullifies the - /// and updates . - /// - internal void Complete() - { - if (!this.IsCompleted) - { - var buffer = this.awaitable.Buffer; - this.awaitable.Transferred = buffer.Count == 0 - ? buffer - : new ArraySegment( - buffer.Array, - buffer.Offset, - this.awaitable.Arguments.BytesTransferred); - - if (this.awaitable.ShouldCaptureContext) - this.syncContext = null; - - this.isCompleted = true; - } - } - #endregion - } -} \ No newline at end of file diff --git a/Enyim.Caching/Memcached/Socket/SocketEx.cs b/Enyim.Caching/Memcached/Socket/SocketEx.cs deleted file mode 100644 index db047075..00000000 --- a/Enyim.Caching/Memcached/Socket/SocketEx.cs +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright © 2013 Şafak Gür. All rights reserved. -// Use of this source code is governed by the MIT License (MIT). - -namespace Dawn.Net.Sockets -{ - using System; - using System.Diagnostics; - using System.Net.Sockets; - using System.Security; - using System.Threading; - - /// - /// Provides socket extensions for easier asynchronous operations. - /// - public static class SocketEx - { - #region Fields - /// - /// Holds a delegate of 's accept operation. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private static readonly Func acceptOp = (s, a) => - s.AcceptAsync(a.Arguments); - - /// - /// Holds a delegate of 's connect operation. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private static readonly Func connectOp = (s, a) => - s.ConnectAsync(a.Arguments); - - /// - /// Holds a delegate of 's receive operation. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private static readonly Func receiveOp = (s, a) => - s.ReceiveAsync(a.Arguments); - - /// - /// Holds a delegate of 's send operation. - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private static readonly Func sendOp = (s, a) => - s.SendAsync(a.Arguments); - #endregion - - #region Methods - /// - /// Begins an awaitable operation to accept an incoming connection attempt. - /// - /// - /// Socket that will accept the connection. - /// - /// - /// The object to use for this asynchronous socket - /// operation. - /// - /// - /// , when awaited, will have the accepted socket in its - /// property. Awaiter of the result returns - /// a that corresponds to the result of this asynchronous - /// operation. - /// - /// - /// or is null. - /// - /// - /// of the is not - /// large enough. The buffer must be at least 2 * (sizeof(SOCKADDR_STORAGE + 16) bytes. - /// - /// - /// is not bound, is not listening for connections, or is - /// already connected. - /// -or- - /// A socket operation was already in progress using - /// - /// - /// Windows XP or later is required for this method. - /// - /// - /// has been disposed. - /// - public static SocketAwaitable AcceptAsync(this Socket socket, SocketAwaitable awaitable) - { - return OperateAsync(socket, awaitable, acceptOp); - } - - /// - /// Begins an awaitable request for a connection to a remote host. - /// - /// - /// Socket that will connect to a remote host. - /// - /// - /// The object to use for this asynchronous socket - /// operation. - /// - /// - /// The specified which, when awaited, returns a - /// object that corresponds to the result of the connection - /// attempt. - /// - /// - /// , , or - /// is null. - /// - /// - /// is listening or a socket operation was already in - /// progress using . - /// - /// - /// Windows XP or later is required for this method. This exception also occurs if the - /// local endpoint and the are not the - /// same address family. - /// -or- - /// Address family of is different than the address - /// family of . - /// - /// - /// has been disposed. - /// - /// - /// A caller higher in the call stack does not have permission for the requested - /// operation. - /// - public static SocketAwaitable ConnectAsync(this Socket socket, SocketAwaitable awaitable) - { - return OperateAsync(socket, awaitable, connectOp); - } - - /// - /// Begins an awaitable request to receive data from a connected - /// object. - /// - /// - /// Socket that will receive data. - /// - /// - /// The object to use for this asynchronous socket - /// operation. - /// - /// - /// The specified which, when awaited, will hold the - /// received data in its property. Awaiter - /// of returns a object that - /// corresponds to the result of the asynchronous operation. - /// - /// - /// or is null. - /// - /// - /// A socket operation was already in progress using . - /// - /// - /// Windows XP or later is required for this method. - /// - /// - /// has been disposed. - /// - public static SocketAwaitable ReceiveAsync(this Socket socket, SocketAwaitable awaitable) - { - return OperateAsync(socket, awaitable, receiveOp); - } - - /// - /// Sends data asynchronously to a connected object and returns a - /// to await. - /// - /// - /// Socket to send the data to. - /// - /// - /// The object to use for this asynchronous socket - /// operation. - /// - /// - /// The specified which, when awaited, will return a - /// object that corresponds to the result of the send - /// operation. - /// - /// - /// or is null. - /// - /// - /// A socket operation was already in progress using . - /// - /// - /// Windows XP or later is required for this method. - /// - /// - /// has been disposed. - /// - public static SocketAwaitable SendAsync(this Socket socket, SocketAwaitable awaitable) - { - return OperateAsync(socket, awaitable, sendOp); - } - - /// - /// Calls the specified asynchronous method of a and returns an - /// awaitable object that provides the operation result when awaited. - /// - /// - /// to run an asynchronous operation. - /// - /// - /// The object to use for this asynchronous socket - /// operation. - /// - /// - /// Socket operation to perform. - /// - /// - /// A which, when awaited, returns a - /// object that corresponds to the result of - /// . - /// - /// - /// or is null. - /// - /// - /// A socket operation was already in progress using . - /// -or- - /// For accept operations: - /// is not bound, is not listening for connections, or is - /// already connected. - /// -or- - /// For connect operations: - /// is listening. - /// - /// - /// Windows XP or later is required for this method. - /// -or- - /// For connect operations: - /// Address family of is different than the address - /// family of . - /// - /// - /// has been disposed. - /// - /// - /// For connection operations: - /// A caller higher in the call stack does not have permission for the requested - /// operation. - /// - private static SocketAwaitable OperateAsync( - Socket socket, - SocketAwaitable awaitable, - Func operation) - { - if (socket == null) - throw new ArgumentNullException("socket", "Socket must not be null."); - - if (awaitable == null) - throw new ArgumentNullException("awaitable", "Awaitable must not be null."); - - var a = awaitable.GetAwaiter(); - lock (a.SyncRoot) - { - if (!a.IsCompleted) - throw new InvalidOperationException( - "A socket operation is already in progress" - + " using the same awaitable arguments."); - - a.Reset(); - if (awaitable.ShouldCaptureContext) - a.SyncContext = SynchronizationContext.Current; - } - - try - { - if (!operation.Invoke(socket, awaitable)) - a.Complete(); - } - catch (SocketException x) - { - a.Complete(); - awaitable.Arguments.SocketError = x.SocketErrorCode != SocketError.Success - ? x.SocketErrorCode - : SocketError.SocketError; - } - catch (Exception) - { - a.Complete(); - awaitable.Arguments.SocketError = SocketError.Success; - throw; - } - - return awaitable; - } - #endregion - } -} \ No newline at end of file diff --git a/SampleWebApp/appsettings.json b/SampleWebApp/appsettings.json index bd107408..9211fe45 100644 --- a/SampleWebApp/appsettings.json +++ b/SampleWebApp/appsettings.json @@ -13,7 +13,7 @@ "receiveTimeout": "00:00:15", "deadTimeout": "00:00:15", "queueTimeout": "00:00:00.150" - }//, + } //, //"Transcoder": "BinaryFormatterTranscoder" //, //"KeyTransformer": "Enyim.Caching.Memcached.SHA1KeyTransformer" @@ -31,9 +31,10 @@ "Logging": { "IncludeScopes": false, "LogLevel": { - "Default": "Debug", + "Default": "Warning", "System": "Warning", - "Microsoft": "Warning" + "Microsoft": "Warning", + "Enyim": "Debug" } } } \ No newline at end of file From 1c693725f1fbf330b95014a564facdf61be7995b Mon Sep 17 00:00:00 2001 From: dudu Date: Fri, 14 Sep 2018 12:15:38 +0800 Subject: [PATCH 3/3] Use Span and stackalloc to remove unsafe/fixed --- Enyim.Caching/Enyim.Caching.csproj | 1 + .../Protocol/Binary/BinaryRequest.cs | 107 +++++++----------- 2 files changed, 44 insertions(+), 64 deletions(-) diff --git a/Enyim.Caching/Enyim.Caching.csproj b/Enyim.Caching/Enyim.Caching.csproj index 4b1af76a..6fff766f 100755 --- a/Enyim.Caching/Enyim.Caching.csproj +++ b/Enyim.Caching/Enyim.Caching.csproj @@ -13,6 +13,7 @@ git https://github.com/cnblogs/EnyimMemcachedCore true + latest diff --git a/Enyim.Caching/Memcached/Protocol/Binary/BinaryRequest.cs b/Enyim.Caching/Memcached/Protocol/Binary/BinaryRequest.cs index d9f80de0..0ce4f9c0 100644 --- a/Enyim.Caching/Memcached/Protocol/Binary/BinaryRequest.cs +++ b/Enyim.Caching/Memcached/Protocol/Binary/BinaryRequest.cs @@ -25,12 +25,12 @@ public BinaryRequest(byte commandCode) this.CorrelationId = Interlocked.Increment(ref InstanceCounter); } - public unsafe IList> CreateBuffer() + public IList> CreateBuffer() { return CreateBuffer(null); } - public unsafe IList> CreateBuffer(IList> appendTo) + public IList> CreateBuffer(IList> appendTo) { // key size byte[] keyData = BinaryConverter.EncodeKey(this.Key); @@ -51,56 +51,53 @@ public unsafe IList> CreateBuffer(IList> a int totalLength = extraLength + keyLength + bodyLength; //build the header - byte[] header = new byte[24]; + Span header = stackalloc byte[24]; - fixed (byte* buffer = header) + header[0x00] = 0x80; // magic + header[0x01] = this.Operation; + + // key length + header[0x02] = (byte)(keyLength >> 8); + header[0x03] = (byte)(keyLength & 255); + + // extra length + header[0x04] = (byte)(extraLength); + + // 5 -- data type, 0 (RAW) + // 6,7 -- reserved, always 0 + + header[0x06] = (byte)(this.Reserved >> 8); + header[0x07] = (byte)(this.Reserved & 255); + + // body length + header[0x08] = (byte)(totalLength >> 24); + header[0x09] = (byte)(totalLength >> 16); + header[0x0a] = (byte)(totalLength >> 8); + header[0x0b] = (byte)(totalLength & 255); + + header[0x0c] = (byte)(this.CorrelationId >> 24); + header[0x0d] = (byte)(this.CorrelationId >> 16); + header[0x0e] = (byte)(this.CorrelationId >> 8); + header[0x0f] = (byte)(this.CorrelationId & 255); + + ulong cas = this.Cas; + // CAS + if (cas > 0) { - buffer[0x00] = 0x80; // magic - buffer[0x01] = this.Operation; - - // key length - buffer[0x02] = (byte)(keyLength >> 8); - buffer[0x03] = (byte)(keyLength & 255); - - // extra length - buffer[0x04] = (byte)(extraLength); - - // 5 -- data type, 0 (RAW) - // 6,7 -- reserved, always 0 - - buffer[0x06] = (byte)(this.Reserved >> 8); - buffer[0x07] = (byte)(this.Reserved & 255); - - // body length - buffer[0x08] = (byte)(totalLength >> 24); - buffer[0x09] = (byte)(totalLength >> 16); - buffer[0x0a] = (byte)(totalLength >> 8); - buffer[0x0b] = (byte)(totalLength & 255); - - buffer[0x0c] = (byte)(this.CorrelationId >> 24); - buffer[0x0d] = (byte)(this.CorrelationId >> 16); - buffer[0x0e] = (byte)(this.CorrelationId >> 8); - buffer[0x0f] = (byte)(this.CorrelationId & 255); - - ulong cas = this.Cas; - // CAS - if (cas > 0) - { - // skip this if no cas is specfied - buffer[0x10] = (byte)(cas >> 56); - buffer[0x11] = (byte)(cas >> 48); - buffer[0x12] = (byte)(cas >> 40); - buffer[0x13] = (byte)(cas >> 32); - buffer[0x14] = (byte)(cas >> 24); - buffer[0x15] = (byte)(cas >> 16); - buffer[0x16] = (byte)(cas >> 8); - buffer[0x17] = (byte)(cas & 255); - } + // skip this if no cas is specfied + header[0x10] = (byte)(cas >> 56); + header[0x11] = (byte)(cas >> 48); + header[0x12] = (byte)(cas >> 40); + header[0x13] = (byte)(cas >> 32); + header[0x14] = (byte)(cas >> 24); + header[0x15] = (byte)(cas >> 16); + header[0x16] = (byte)(cas >> 8); + header[0x17] = (byte)(cas & 255); } var retval = appendTo ?? new List>(4); - retval.Add(new ArraySegment(header)); + retval.Add(new ArraySegment(header.ToArray())); if (extraLength > 0) retval.Add(extras); @@ -108,24 +105,6 @@ public unsafe IList> CreateBuffer(IList> a if (keyLength > 0) retval.Add(new ArraySegment(keyData)); if (bodyLength > 0) retval.Add(body); -#if DEBUG_PROTOCOL - if (log.IsDebugEnabled) - { - log.Debug("Building binary request"); - StringBuilder sb = new StringBuilder(128).AppendLine(); - - for (int i = 0; i < header.Length; i++) - { - byte value = header[i]; - sb.Append(value < 16 ? "0x0" : "0x").Append(value.ToString("X")); - - if (i % 4 == 3) sb.AppendLine(); else sb.Append(" "); - } - - log.Debug(sb.ToString()); - } -#endif - return retval; }