From 9980cc987ed817dcdafb2344e2646b0a5fd0df45 Mon Sep 17 00:00:00 2001 From: Alessio Parma Date: Sat, 24 Jun 2017 10:32:39 +0200 Subject: [PATCH] further refinements for validaobject --- CHANGELOG.md | 1 + .../Core/CannotResetStateException.cs | 43 -------------- src/CodeProject.ObjectPool/PooledObject.cs | 57 ++++++++++--------- .../Specialized/PooledMemoryStream.cs | 44 ++++++++++++-- .../Specialized/PooledStringBuilder.cs | 34 ++++++++++- 5 files changed, 102 insertions(+), 77 deletions(-) delete mode 100644 src/CodeProject.ObjectPool/Core/CannotResetStateException.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index d642a6c..814a25a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### v3.0.4 (2017-06-24) ### * Removed dependency on Thrower. +* Removed CannotResetStateException class, not needed with new validation step. ### v3.0.3 (2017-04-08) ### diff --git a/src/CodeProject.ObjectPool/Core/CannotResetStateException.cs b/src/CodeProject.ObjectPool/Core/CannotResetStateException.cs deleted file mode 100644 index a30310e..0000000 --- a/src/CodeProject.ObjectPool/Core/CannotResetStateException.cs +++ /dev/null @@ -1,43 +0,0 @@ -// File name: CannotResetStateException.cs -// -// Author(s): Alessio Parma -// -// The MIT License (MIT) -// -// Copyright (c) 2013-2018 Alessio Parma -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -// associated documentation files (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, merge, publish, distribute, -// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT -// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -using System; - -namespace CodeProject.ObjectPool.Core -{ - /// - /// This exception can be used to signal that the object whose state is being reset cannot be - /// added back to the pool for some reason. - /// - public sealed class CannotResetStateException : Exception - { - /// - /// Builds the exception using given message. - /// - /// The message. - public CannotResetStateException(string message) - : base(message) - { - } - } -} \ No newline at end of file diff --git a/src/CodeProject.ObjectPool/PooledObject.cs b/src/CodeProject.ObjectPool/PooledObject.cs index 4351041..c498e32 100644 --- a/src/CodeProject.ObjectPool/PooledObject.cs +++ b/src/CodeProject.ObjectPool/PooledObject.cs @@ -48,11 +48,31 @@ public abstract class PooledObject : IDisposable, IEquatable #region Internal Methods - resource and state management /// - /// Validates pooled object state. + /// Validates pooled object state. An invalid object will not get into the pool and it will + /// not be returned to consumers. /// /// The validation context. /// True if current pooled object is valid, false otherwise. - protected internal virtual bool ValidateObject(PooledObjectValidationContext validationContext) => true; + internal bool ValidateObject(PooledObjectValidationContext validationContext) + { + if (OnValidateObject != null) + { + try + { + return OnValidateObject(validationContext); + } + catch (Exception ex) + { +#if !NET35 + if (Log.IsWarnEnabled()) Log.WarnException("[ObjectPool] An unexpected error occurred while validating an object", ex); +#else + System.Diagnostics.Debug.Assert(ex != null); // Placeholder to avoid warnings +#endif + return false; + } + } + return true; + } /// /// Releases the object resources. This method will be called by the pool manager when @@ -70,10 +90,7 @@ internal bool ReleaseResources() catch (Exception ex) { #if !NET35 - if (Log.IsWarnEnabled()) - { - Log.WarnException("[ObjectPool] An unexpected error occurred while releasing resources", ex); - } + if (Log.IsWarnEnabled()) Log.WarnException("[ObjectPool] An unexpected error occurred while releasing resources", ex); #else System.Diagnostics.Debug.Assert(ex != null); // Placeholder to avoid warnings #endif @@ -99,25 +116,10 @@ internal bool ResetState() { OnResetState(); } - catch (CannotResetStateException crsex) - { -#if !NET35 - if (Log.IsDebugEnabled()) - { - Log.DebugException("[ObjectPool] Object state could not be reset", crsex); - } -#else - System.Diagnostics.Debug.Assert(crsex != null); // Placeholder to avoid warnings -#endif - return false; - } catch (Exception ex) { #if !NET35 - if (Log.IsWarnEnabled()) - { - Log.WarnException("[ObjectPool] An unexpected error occurred while resetting state", ex); - } + if (Log.IsWarnEnabled()) Log.WarnException("[ObjectPool] An unexpected error occurred while resetting state", ex); #else System.Diagnostics.Debug.Assert(ex != null); // Placeholder to avoid warnings #endif @@ -131,6 +133,12 @@ internal bool ResetState() #region Events - extending resource and state management + /// + /// Validates pooled object state. An invalid object will not get into the pool and it will + /// not be returned to consumers. + /// + public Func OnValidateObject { get; set; } + /// /// Reset the object state to allow this object to be re-used by other parts of the application. /// @@ -171,10 +179,7 @@ private void HandleReAddingToPool(bool reRegisterForFinalization) catch (Exception ex) { #if !NET35 - if (Log.IsWarnEnabled()) - { - Log.WarnException("[ObjectPool] An error occurred while re-adding to pool", ex); - } + if (Log.IsWarnEnabled()) Log.WarnException("[ObjectPool] An error occurred while re-adding to pool", ex); #else System.Diagnostics.Debug.Assert(ex != null); // Placeholder to avoid warnings #endif diff --git a/src/CodeProject.ObjectPool/Specialized/PooledMemoryStream.cs b/src/CodeProject.ObjectPool/Specialized/PooledMemoryStream.cs index bde80d6..6ae3a8a 100644 --- a/src/CodeProject.ObjectPool/Specialized/PooledMemoryStream.cs +++ b/src/CodeProject.ObjectPool/Specialized/PooledMemoryStream.cs @@ -22,9 +22,14 @@ // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using CodeProject.ObjectPool.Core; -using System; using System.IO; +#if !NET35 + +using CodeProject.ObjectPool.Logging; + +#endif + namespace CodeProject.ObjectPool.Specialized { /// @@ -32,6 +37,14 @@ namespace CodeProject.ObjectPool.Specialized /// public class PooledMemoryStream : PooledObject { + #region Logging + +#if !NET35 + private static readonly ILog Log = LogProvider.GetLogger(typeof(PooledMemoryStream)); +#endif + + #endregion Logging + /// /// The tracked memory stream. /// @@ -48,23 +61,44 @@ public PooledMemoryStream(int capacity) Parent = this }; - OnResetState += () => + OnValidateObject += (ctx) => { + if (ctx.Direction == PooledObjectDirection.Outbound) + { + // We validate only inbound objects, because when they are in the pool they + // cannot change their state. + return true; + } + if (!_trackedMemoryStream.CanRead || !_trackedMemoryStream.CanWrite || !_trackedMemoryStream.CanSeek) { - throw new CannotResetStateException($"Memory stream has already been disposed"); +#if !NET35 + if (Log.IsWarnEnabled()) Log.Warn("[ObjectPool] Memory stream has already been disposed"); +#endif + return false; } var memoryStreamPool = PooledObjectInfo.Handle as IMemoryStreamPool; if (_trackedMemoryStream.Capacity < memoryStreamPool.MinimumMemoryStreamCapacity) { - throw new CannotResetStateException($"Memory stream capacity is {_trackedMemoryStream.Capacity}, while minimum required capacity is {memoryStreamPool.MinimumMemoryStreamCapacity}"); +#if !NET35 + if (Log.IsWarnEnabled()) Log.Warn($"[ObjectPool] Memory stream capacity is {_trackedMemoryStream.Capacity}, while minimum required capacity is {memoryStreamPool.MinimumMemoryStreamCapacity}"); +#endif + return false; } if (_trackedMemoryStream.Capacity > memoryStreamPool.MaximumMemoryStreamCapacity) { - throw new CannotResetStateException($"Memory stream capacity is {_trackedMemoryStream.Capacity}, while maximum allowed capacity is {memoryStreamPool.MaximumMemoryStreamCapacity}"); +#if !NET35 + if (Log.IsWarnEnabled()) Log.Warn($"[ObjectPool] Memory stream capacity is {_trackedMemoryStream.Capacity}, while maximum allowed capacity is {memoryStreamPool.MaximumMemoryStreamCapacity}"); +#endif + return false; } + return true; // Object is valid. + }; + + OnResetState += () => + { _trackedMemoryStream.Position = 0L; _trackedMemoryStream.SetLength(0L); }; diff --git a/src/CodeProject.ObjectPool/Specialized/PooledStringBuilder.cs b/src/CodeProject.ObjectPool/Specialized/PooledStringBuilder.cs index 755fbb6..e28e6c4 100644 --- a/src/CodeProject.ObjectPool/Specialized/PooledStringBuilder.cs +++ b/src/CodeProject.ObjectPool/Specialized/PooledStringBuilder.cs @@ -22,9 +22,14 @@ // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using CodeProject.ObjectPool.Core; -using System; using System.Text; +#if !NET35 + +using CodeProject.ObjectPool.Logging; + +#endif + namespace CodeProject.ObjectPool.Specialized { /// @@ -32,6 +37,14 @@ namespace CodeProject.ObjectPool.Specialized /// public class PooledStringBuilder : PooledObject { + #region Logging + +#if !NET35 + private static readonly ILog Log = LogProvider.GetLogger(typeof(PooledStringBuilder)); +#endif + + #endregion Logging + /// /// The string builder. /// @@ -45,14 +58,29 @@ public PooledStringBuilder(int capacity) { StringBuilder = new StringBuilder(capacity); - OnResetState += () => + OnValidateObject += (ctx) => { + if (ctx.Direction == PooledObjectDirection.Outbound) + { + // We validate only inbound objects, because when they are in the pool they + // cannot change their state. + return true; + } + var stringBuilderPool = PooledObjectInfo.Handle as IStringBuilderPool; if (StringBuilder.Capacity > stringBuilderPool.MaximumStringBuilderCapacity) { - throw new CannotResetStateException($"String builder capacity is {StringBuilder.Capacity}, while maximum allowed capacity is {stringBuilderPool.MaximumStringBuilderCapacity}"); +#if !NET35 + if (Log.IsWarnEnabled()) Log.Warn($"[ObjectPool] String builder capacity is {StringBuilder.Capacity}, while maximum allowed capacity is {stringBuilderPool.MaximumStringBuilderCapacity}"); +#endif + return false; } + return true; // Object is valid. + }; + + OnResetState += () => + { ClearStringBuilder(); };