Commit c54c0671 authored by Nick Craver's avatar Nick Craver

PoC: Dropping netstandard1.x support

A demo of how much this saves in complexity.
parent d7c72b05
......@@ -2,7 +2,7 @@
<PropertyGroup>
<Description>StackExchange.Redis.BasicTest .NET Core</Description>
<TargetFrameworks>netcoreapp1.1;netcoreapp2.0</TargetFrameworks>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<AssemblyName>BasicTest</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>BasicTest</PackageId>
......@@ -14,8 +14,4 @@
<ProjectReference Include="..\StackExchange.Redis\StackExchange.Redis.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">
<PackageReference Include="System.Console" Version="$(CoreFxVersion)" />
</ItemGroup>
</Project>
......@@ -22,7 +22,7 @@
<DefaultLanguage>en-US</DefaultLanguage>
<IncludeSymbols>false</IncludeSymbols>
<LibraryTargetFrameworks>net45;net46;netstandard1.5;netstandard2.0</LibraryTargetFrameworks>
<LibraryTargetFrameworks>net45;net46;netstandard2.0</LibraryTargetFrameworks>
<CoreFxVersion>4.3.0</CoreFxVersion>
<xUnitVersion>2.4.0-beta.2.build3981</xUnitVersion>
</PropertyGroup>
......
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFrameworks>netcoreapp1.1;netcoreapp2.0</TargetFrameworks>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
......
......@@ -21,27 +21,16 @@
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'net46'">
<DefineConstants>$(DefineConstants);FEATURE_SERIALIZATION;FEATURE_SOCKET_MODE_POLL;FEATURE_PERFCOUNTER;FEATURE_THREADPOOL</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_SOCKET_MODE_POLL;FEATURE_PERFCOUNTER;FEATURE_THREADPOOL</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.Compression" Version="$(CoreFxVersion)" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);FEATURE_SERIALIZATION;FEATURE_THREADPOOL</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_THREADPOOL</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
<PackageReference Include="System.Collections.NonGeneric" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Net.NameResolution" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Net.Security" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.Emit" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Threading.Thread" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Threading.ThreadPool" Version="$(CoreFxVersion)" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="$(CoreFxVersion)" />
......
......@@ -4,9 +4,7 @@
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
#if !NETCOREAPP1_0
using System.Security.Authentication;
#endif
namespace StackExchange.Redis.Tests.Booksleeve
{
......@@ -106,8 +104,7 @@ public void CreateDisconnectedNonsenseConnection_DNS()
Output.WriteLine(log.ToString());
}
}
#if !NETCOREAPP1_0
[Fact]
public void SslProtocols_SingleValue()
{
......@@ -143,7 +140,6 @@ public void SslProtocols_InvalidValue()
var log = new StringWriter();
Assert.Throws<ArgumentOutOfRangeException>(() => ConfigurationOptions.Parse("myhost,sslProtocols=InvalidSslProtocol"));
}
#endif
[Fact]
public void ConfigurationOptionsDefaultForAzure()
......@@ -202,4 +198,4 @@ public void ConfigurationOptionsDefaultWhenNoEndpointsSpecifiedYet()
Assert.True(options.AbortOnConnectFail);
}
}
}
\ No newline at end of file
}
......@@ -161,12 +161,8 @@ public void ReadConfig()
var conn = GetAnyMaster(muxer);
var all = conn.ConfigGet();
Assert.True(all.Length > 0, "any");
#if !NETCOREAPP1_0
var pairs = all.ToDictionary(x => (string)x.Key, x => (string)x.Value, StringComparer.InvariantCultureIgnoreCase);
#else
var pairs = all.ToDictionary(x => (string)x.Key, x => (string)x.Value, StringComparer.OrdinalIgnoreCase);
#endif
Assert.Equal(all.Length, pairs.Count);
Assert.True(pairs.ContainsKey("timeout"), "timeout");
......
......@@ -12,8 +12,6 @@ static Extensions()
{
#if NET462
VersionInfo = "Compiled under .NET 4.6.2";
#elif NETCOREAPP1_0
VersionInfo = "Compiled under .NETCoreApp1.0";
#elif NETCOREAPP2_0
VersionInfo = "Compiled under .NETCoreApp2.0";
#else
......
......@@ -2,9 +2,6 @@
using Jil;
using System;
using System.Collections.Generic;
#if NETCOREAPP1_0
using System.Reflection;
#endif
namespace StackExchange.Redis.Tests
{
......@@ -19,7 +16,7 @@ static TestConfig()
Current = new Config();
try
{
using (var stream = typeof(TestConfig).GetTypeInfo().Assembly.GetManifestResourceStream("StackExchange.Redis.Tests." + FileName))
using (var stream = typeof(TestConfig).Assembly.GetManifestResourceStream("StackExchange.Redis.Tests." + FileName))
{
if (stream != null)
{
......
......@@ -28,19 +28,9 @@ public async Task MassiveBulkOpsAsync(bool preserveOrder, bool withContinuation)
RedisKey key = "MBOA";
var conn = muxer.GetDatabase();
await conn.PingAsync().ForAwait();
#if NETCOREAPP1_0
int number = 0;
#endif
Action<Task> nonTrivial = delegate
{
#if NETCOREAPP1_0
for (int i = 0; i < 50; i++)
{
number++;
}
#else
Thread.SpinWait(5);
#endif
};
var watch = Stopwatch.StartNew();
for (int i = 0; i <= AsyncOpsQty; i++)
......
......@@ -20,7 +20,7 @@ public class Naming
public void CheckSignatures(Type type, bool isAsync)
{
// check that all methods and interfaces look appropriate for their sync/async nature
CheckName(type.GetTypeInfo(), isAsync);
CheckName(type, isAsync);
var members = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var member in members)
{
......@@ -32,9 +32,9 @@ public void CheckSignatures(Type type, bool isAsync)
[Fact]
public void ShowReadOnlyOperations()
{
var msg = typeof(ConnectionMultiplexer).GetTypeInfo().Assembly.GetType("StackExchange.Redis.Message");
var msg = typeof(ConnectionMultiplexer).Assembly.GetType("StackExchange.Redis.Message");
Assert.NotNull(msg);
var cmd = typeof(ConnectionMultiplexer).GetTypeInfo().Assembly.GetType("StackExchange.Redis.RedisCommand");
var cmd = typeof(ConnectionMultiplexer).Assembly.GetType("StackExchange.Redis.RedisCommand");
Assert.NotNull(cmd);
var masterOnlyMethod = msg.GetMethod(nameof(Message.IsMasterOnly), BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
Assert.NotNull(masterOnlyMethod);
......@@ -103,7 +103,7 @@ private static bool UsesKey(Type type)
{
if (UsesKey(type.GetElementType())) return true;
}
if (type.GetTypeInfo().IsGenericType) // KVP, etc
if (type.IsGenericType) // KVP, etc
{
var args = type.GetGenericArguments();
if (args.Any(UsesKey)) return true;
......@@ -156,7 +156,7 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to)
{
huntType = null;
}
else if (method.ReturnType.GetTypeInfo().IsSubclassOf(typeof(Task)))
else if (method.ReturnType.IsSubclassOf(typeof(Task)))
{
huntType = method.ReturnType.GetGenericArguments()[0];
}
......@@ -168,12 +168,7 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to)
Type[] args = pFrom.Select(x => x.ParameterType).ToArray();
Output.WriteLine("Checking: {0}.{1}", from.Name, method.Name);
Assert.Equal(typeof(CommandFlags), args.Last());
#if !NETCOREAPP1_0
var found = to.GetMethod(huntName, flags, null, method.CallingConvention, args, null);
#else
var found = to.GetMethods(flags)
.SingleOrDefault(m => m.Name == huntName && m.HasMatchingParameterTypes(args));
#endif
Assert.NotNull(found); // "Found " + name + ", no " + huntName
var pTo = found.GetParameters();
......@@ -188,22 +183,14 @@ public void CheckSyncAsyncMethodsMatch(Type from, Type to)
Output.WriteLine("Validated: {0} ({1} methods)", from.Name, count);
}
private static readonly Type ignoreType = typeof(ConnectionMultiplexer).GetTypeInfo().Assembly.GetType("StackExchange.Redis.IgnoreNamePrefixAttribute");
private static readonly Type ignoreType = typeof(ConnectionMultiplexer).Assembly.GetType("StackExchange.Redis.IgnoreNamePrefixAttribute");
private void CheckMethod(MethodInfo method, bool isAsync)
{
#if DEBUG
#if !NETCOREAPP1_0
bool ignorePrefix = ignoreType != null && Attribute.IsDefined(method, ignoreType);
#else
bool ignorePrefix = ignoreType != null && method.IsDefined(ignoreType);
#endif
if (ignorePrefix)
{
#if !NETCOREAPP1_0
Attribute attrib = Attribute.GetCustomAttribute(method, ignoreType);
#else
Attribute attrib = method.GetCustomAttribute(ignoreType);
#endif
if ((bool)attrib.GetType().GetProperty("IgnoreEntireMethod").GetValue(attrib))
{
return;
......@@ -250,34 +237,4 @@ private void CheckName(MemberInfo member, bool isAsync)
else Assert.False(member.Name.EndsWith("Async"), member.Name + ":Name - don't end *Async");
}
}
public static class ReflectionExtensions
{
#if !NETCOREAPP1_0
public static Type GetTypeInfo(this Type type)
{
return type;
}
#else
public static bool HasMatchingParameterTypes(this MethodInfo method, Type[] paramTypes)
{
var types = method.GetParameters().Select(pi => pi.ParameterType).ToArray();
if (types.Length != paramTypes.Length)
{
return false;
}
for (int i = 0; i < types.Length; i++)
{
if (types[i] != paramTypes[i])
{
return false;
}
}
return true;
}
#endif
}
}
using System;
using System.Collections.Generic;
using System.Linq;
#if NETCOREAPP1_0
using System.Reflection;
#endif
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;
......@@ -430,11 +427,11 @@ public void LowAllocationEnumerable()
conn.WaitAll(allTasks.ToArray());
var res = conn.FinishProfiling(profiler.MyContext);
Assert.True(res.GetType().GetTypeInfo().IsValueType);
Assert.True(res.GetType().IsValueType);
using (var e = res.GetEnumerator())
{
Assert.True(e.GetType().GetTypeInfo().IsValueType);
Assert.True(e.GetType().IsValueType);
Assert.True(e.MoveNext());
var i = e.Current;
......@@ -557,4 +554,4 @@ public void ProfilingMD_Ex2()
}
}
}
}
\ No newline at end of file
}
......@@ -27,17 +27,17 @@ public void ExplicitPublishMode()
Thread.Sleep(1000);
pub.Publish("abcd", "efg");
Thread.Sleep(500);
Assert.Equal(0, VolatileWrapper.Read(ref a));
Assert.Equal(1, VolatileWrapper.Read(ref b));
Assert.Equal(1, VolatileWrapper.Read(ref c));
Assert.Equal(1, VolatileWrapper.Read(ref d));
Assert.Equal(0, Thread.VolatileRead(ref a));
Assert.Equal(1, Thread.VolatileRead(ref b));
Assert.Equal(1, Thread.VolatileRead(ref c));
Assert.Equal(1, Thread.VolatileRead(ref d));
pub.Publish("*bcd", "efg");
Thread.Sleep(500);
Assert.Equal(1, VolatileWrapper.Read(ref a));
//Assert.Equal(1, VolatileWrapper.Read(ref b));
//Assert.Equal(1, VolatileWrapper.Read(ref c));
//Assert.Equal(1, VolatileWrapper.Read(ref d));
Assert.Equal(1, Thread.VolatileRead(ref a));
//Assert.Equal(1, Thread.VolatileRead(ref b));
//Assert.Equal(1, Thread.VolatileRead(ref c));
//Assert.Equal(1, Thread.VolatileRead(ref d));
}
}
......@@ -89,7 +89,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC
{
Assert.Empty(received);
}
Assert.Equal(0, VolatileWrapper.Read(ref secondHandler));
Assert.Equal(0, Thread.VolatileRead(ref secondHandler));
var count = sub.Publish(pubChannel, "def");
Ping(muxer, pub, sub, 3);
......@@ -98,7 +98,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC
{
Assert.Single(received);
}
Assert.Equal(1, VolatileWrapper.Read(ref secondHandler));
Assert.Equal(1, Thread.VolatileRead(ref secondHandler));
// unsubscribe from first; should still see second
sub.Unsubscribe(subChannel, handler1);
......@@ -108,7 +108,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC
{
Assert.Single(received);
}
Assert.Equal(2, VolatileWrapper.Read(ref secondHandler));
Assert.Equal(2, Thread.VolatileRead(ref secondHandler));
Assert.Equal(1, count);
// unsubscribe from second; should see nothing this time
......@@ -119,7 +119,7 @@ public void TestBasicPubSub(bool preserveOrder, string channelPrefix, bool wildC
{
Assert.Single(received);
}
Assert.Equal(2, VolatileWrapper.Read(ref secondHandler));
Assert.Equal(2, Thread.VolatileRead(ref secondHandler));
Assert.Equal(0, count);
}
}
......@@ -156,7 +156,7 @@ public void TestBasicPubSubFireAndForget(bool preserveOrder)
{
Assert.Empty(received);
}
Assert.Equal(0, VolatileWrapper.Read(ref secondHandler));
Assert.Equal(0, Thread.VolatileRead(ref secondHandler));
Ping(muxer, pub, sub);
var count = sub.Publish(key, "def", CommandFlags.FireAndForget);
Ping(muxer, pub, sub);
......@@ -165,7 +165,7 @@ public void TestBasicPubSubFireAndForget(bool preserveOrder)
{
Assert.Single(received);
}
Assert.Equal(1, VolatileWrapper.Read(ref secondHandler));
Assert.Equal(1, Thread.VolatileRead(ref secondHandler));
sub.Unsubscribe(key);
count = sub.Publish(key, "ghi", CommandFlags.FireAndForget);
......@@ -228,7 +228,7 @@ public void TestPatternPubSub(bool preserveOrder)
{
Assert.Empty(received);
}
Assert.Equal(0, VolatileWrapper.Read(ref secondHandler));
Assert.Equal(0, Thread.VolatileRead(ref secondHandler));
var count = sub.Publish("abc", "def");
Ping(muxer, pub, sub);
......@@ -237,7 +237,7 @@ public void TestPatternPubSub(bool preserveOrder)
{
Assert.Single(received);
}
Assert.Equal(1, VolatileWrapper.Read(ref secondHandler));
Assert.Equal(1, Thread.VolatileRead(ref secondHandler));
sub.Unsubscribe("a*c");
count = sub.Publish("abc", "ghi");
......@@ -268,7 +268,7 @@ public async Task SubscriptionsSurviveConnectionFailureAsync()
await sub.PublishAsync(channel, "abc").ConfigureAwait(false);
sub.Ping();
await Task.Delay(200).ConfigureAwait(false);
Assert.Equal(1, VolatileWrapper.Read(ref counter));
Assert.Equal(1, Thread.VolatileRead(ref counter));
var server = GetServer(muxer);
Assert.Equal(1, server.GetCounters().Subscription.SocketCount);
......@@ -280,21 +280,9 @@ public async Task SubscriptionsSurviveConnectionFailureAsync()
await sub.PublishAsync(channel, "abc").ConfigureAwait(false);
await Task.Delay(200).ConfigureAwait(false);
sub.Ping();
Assert.Equal(2, VolatileWrapper.Read(ref counter));
Assert.Equal(2, Thread.VolatileRead(ref counter));
}
}
#endif
}
internal static class VolatileWrapper
{
public static int Read(ref int location)
{
#if !NETCOREAPP1_0
return Thread.VolatileRead(ref location);
#else
return Volatile.Read(ref location);
#endif
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>StackExchange.Redis.Tests</Description>
<TargetFrameworks>net462;netcoreapp1.0;netcoreapp2.0</TargetFrameworks>
<TargetFrameworks>net462;netcoreapp2.0</TargetFrameworks>
<AssemblyName>StackExchange.Redis.Tests</AssemblyName>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<DebugType>full</DebugType>
</PropertyGroup>
<ItemGroup>
<None Update="*.json" CopyToOutputDirectory="Always" />
<EmbeddedResource Include="*Config.json" />
......@@ -28,13 +28,4 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="System.Console" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Diagnostics.Debug" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Linq.Expressions" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.Extensions" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Runtime.InteropServices" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Threading.Tasks.Parallel" Version="$(CoreFxVersion)" />
<PackageReference Include="Microsoft.CSharp" Version="$(CoreFxVersion)" />
</ItemGroup>
</Project>
......@@ -9,7 +9,6 @@ public class TaskTests
{
#if DEBUG
#if !PLAT_SAFE_CONTINUATIONS // IsSyncSafe doesn't exist if PLAT_SAFE_CONTINUATIONS is defined
[Theory]
[InlineData(SourceOrign.NewTCS)]
[InlineData(SourceOrign.Create)]
......@@ -20,13 +19,10 @@ public void VerifyIsSyncSafe(SourceOrign origin)
// ...and if we're dropping NET45 support, we can just nuke it all.
#if NET462
Assert.True(TaskSource.IsSyncSafe(source.Task));
#elif NETCOREAPP1_0
Assert.True(TaskSource.IsSyncSafe(source.Task));
#elif NETCOREAPP2_0
Assert.True(TaskSource.IsSyncSafe(source.Task));
#endif
}
#endif
private static TaskCompletionSource<T> Create<T>(SourceOrign origin)
{
switch (origin)
......@@ -123,4 +119,4 @@ private async void DoAwait(Task task)
}
#endif
}
}
\ No newline at end of file
}
......@@ -59,9 +59,6 @@ static TestBase()
{
Console.WriteLine("Unobserved: " + args.Exception);
args.SetObserved();
#if NETCOREAPP1_0
if (IgnorableExceptionPredicates.Any(predicate => predicate(args.Exception.InnerException))) return;
#endif
lock (sharedFailCount)
{
if (sharedFailCount != null)
......@@ -76,14 +73,6 @@ static TestBase()
};
}
#if NETCOREAPP1_0
private static readonly Func<Exception, bool>[] IgnorableExceptionPredicates = new Func<Exception, bool>[]
{
e => e != null && e is ObjectDisposedException && e.Message.Equals("Cannot access a disposed object.\r\nObject name: 'System.Net.Sockets.NetworkStream'."),
e => e != null && e is IOException && e.Message.StartsWith("Unable to read data from the transport connection:")
};
#endif
protected void OnConnectionFailed(object sender, ConnectionFailedEventArgs e)
{
Interlocked.Increment(ref privateFailCount);
......@@ -318,13 +307,11 @@ protected static TimeSpan RunConcurrent(Action work, int threads, int timeout =
}
if (!allDone.WaitOne(timeout))
{
#if !NETCOREAPP1_0
for (int i = 0; i < threads; i++)
{
var thd = threadArr[i];
if (thd.IsAlive) thd.Abort();
}
#endif
throw new TimeoutException();
}
......
......@@ -27,17 +27,6 @@
<DefineConstants>$(DefineConstants);FEATURE_SERIALIZATION;FEATURE_THREADPOOL</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
<PackageReference Include="System.Collections.NonGeneric" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Net.NameResolution" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Net.Security" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.Emit" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Threading.Thread" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Threading.ThreadPool" Version="$(CoreFxVersion)" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="$(CoreFxVersion)" />
......
#if NETSTANDARD1_5
using System;
namespace StackExchange.Redis
{
/// <summary>
/// Strictly for compat and less #if defs on netstandard1.5
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
internal class BrowsableAttribute : Attribute
{
public BrowsableAttribute(bool _) { }
}
}
#endif
\ No newline at end of file
using System;
namespace StackExchange.Redis
{
/// <summary>
/// Helper for Array.ConvertAll() as it's missing on .Net Core.
/// </summary>
public static class ConvertHelper
{
/// <summary>
/// Converts array of one type to an array of another type.
/// </summary>
/// <typeparam name="TInput">Input type</typeparam>
/// <typeparam name="TOutput">Output type</typeparam>
/// <param name="source">source</param>
/// <param name="selector">selector</param>
/// <returns></returns>
public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] source, Func<TInput, TOutput> selector)
{
#if NETSTANDARD1_5
TOutput[] arr = new TOutput[source.Length];
for(int i = 0 ; i < arr.Length ; i++)
arr[i] = selector(source[i]);
return arr;
#else
return Array.ConvertAll(source, item => selector(item));
#endif
}
}
}
namespace StackExchange.Redis
{
internal static class VolatileWrapper
{
public static int Read(ref int location)
{
#if NETSTANDARD1_5
return System.Threading.Volatile.Read(ref location);
#else
return System.Threading.Thread.VolatileRead(ref location);
#endif
}
public static void Write(ref int address, int value)
{
#if NETSTANDARD1_5
System.Threading.Volatile.Write(ref address, value);
#else
System.Threading.Thread.VolatileWrite(ref address, value);
#endif
}
}
}
......@@ -14,10 +14,7 @@ namespace StackExchange.Redis
/// <summary>
/// The options relevant to a set of redis connections
/// </summary>
public sealed class ConfigurationOptions
#if !NETSTANDARD1_5
: ICloneable
#endif
public sealed class ConfigurationOptions : ICloneable
{
internal const string DefaultTieBreaker = "__Booksleeve_TieBreak", DefaultConfigurationChannel = "__Booksleeve_MasterChanged";
......@@ -398,9 +395,7 @@ public ConfigurationOptions Clone()
DefaultDatabase = DefaultDatabase,
ReconnectRetryPolicy = reconnectRetryPolicy,
preserveAsyncOrder = preserveAsyncOrder,
#if !NETSTANDARD1_5
SslProtocols = SslProtocols,
#endif
};
foreach (var item in EndPoints)
options.EndPoints.Add(item);
......@@ -547,9 +542,7 @@ private void Clear()
SocketManager = null;
}
#if !NETSTANDARD1_5
object ICloneable.Clone() { return Clone(); }
#endif
object ICloneable.Clone() => Clone();
private void DoParse(string configuration, bool ignoreUnknown)
{
......@@ -652,11 +645,9 @@ private void DoParse(string configuration, bool ignoreUnknown)
case OptionKeys.PreserveAsyncOrder:
PreserveAsyncOrder = OptionKeys.ParseBoolean(key, value);
break;
#if !NETSTANDARD1_5
case OptionKeys.SslProtocols:
SslProtocols = OptionKeys.ParseSslProtocols(key, value);
break;
#endif
default:
if (!string.IsNullOrEmpty(key) && key[0] == '$')
{
......@@ -731,4 +722,4 @@ private string InferSslHostFromEndpoints()
return null;
}
}
}
\ No newline at end of file
}
......@@ -69,9 +69,6 @@ private static string GetDefaultClientName()
/// </summary>
internal static string TryGetAzureRoleInstanceIdNoThrow()
{
#if NETSTANDARD1_5
return null;
#else
string roleInstanceId = null;
// TODO: CoreCLR port pending https://github.com/dotnet/coreclr/issues/919
try
......@@ -109,7 +106,6 @@ internal static string TryGetAzureRoleInstanceIdNoThrow()
roleInstanceId = null;
}
return roleInstanceId;
#endif
}
/// <summary>
......@@ -504,7 +500,7 @@ public EndPoint[] GetEndPoints(bool configuredOnly = false)
{
if (configuredOnly) return configuration.EndPoints.ToArray();
return ConvertHelper.ConvertAll(serverSnapshot, x => x.EndPoint);
return Array.ConvertAll(serverSnapshot, x => x.EndPoint);
}
private readonly ConfigurationOptions configuration;
......@@ -581,7 +577,6 @@ private static bool WaitAllIgnoreErrors(Task[] tasks, int timeout)
return false;
}
#if FEATURE_THREADPOOL
private void LogLockedWithThreadPoolStats(TextWriter log, string message, out int busyWorkerCount)
{
busyWorkerCount = 0;
......@@ -594,7 +589,6 @@ private void LogLockedWithThreadPoolStats(TextWriter log, string message, out in
LogLocked(log, sb.ToString());
}
}
#endif
private static bool AllComplete(Task[] tasks)
{
......@@ -623,27 +617,21 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli
}
var watch = Stopwatch.StartNew();
#if FEATURE_THREADPOOL
LogLockedWithThreadPoolStats(log, "Awaiting task completion", out int busyWorkerCount);
#endif
try
{
// if none error, great
var remaining = timeoutMilliseconds - checked((int)watch.ElapsedMilliseconds);
if (remaining <= 0)
{
#if FEATURE_THREADPOOL
LogLockedWithThreadPoolStats(log, "Timeout before awaiting for tasks", out busyWorkerCount);
#endif
return false;
}
var allTasks = Task.WhenAll(tasks).ObserveErrors();
var any = Task.WhenAny(allTasks, Task.Delay(remaining)).ObserveErrors();
bool all = await any.ForAwait() == allTasks;
#if FEATURE_THREADPOOL
LogLockedWithThreadPoolStats(log, all ? "All tasks completed cleanly" : "Not all tasks completed cleanly", out busyWorkerCount);
#endif
return all;
}
catch
......@@ -659,9 +647,7 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli
var remaining = timeoutMilliseconds - checked((int)watch.ElapsedMilliseconds);
if (remaining <= 0)
{
#if FEATURE_THREADPOOL
LogLockedWithThreadPoolStats(log, "Timeout awaiting tasks", out busyWorkerCount);
#endif
return false;
}
try
......@@ -672,9 +658,7 @@ private async Task<bool> WaitAllIgnoreErrorsAsync(Task[] tasks, int timeoutMilli
{ }
}
}
#if FEATURE_THREADPOOL
LogLockedWithThreadPoolStats(log, "Finished awaiting tasks", out busyWorkerCount);
#endif
return false;
}
......@@ -978,13 +962,13 @@ internal long LastHeartbeatSecondsAgo
get
{
if (pulse == null) return -1;
return unchecked(Environment.TickCount - VolatileWrapper.Read(ref lastHeartbeatTicks)) / 1000;
return unchecked(Environment.TickCount - Thread.VolatileRead(ref lastHeartbeatTicks)) / 1000;
}
}
internal Exception LastException { get; set; }
internal static long LastGlobalHeartbeatSecondsAgo => unchecked(Environment.TickCount - VolatileWrapper.Read(ref lastGlobalHeartbeatTicks)) / 1000;
internal static long LastGlobalHeartbeatSecondsAgo => unchecked(Environment.TickCount - Thread.VolatileRead(ref lastGlobalHeartbeatTicks)) / 1000;
internal CompletionManager UnprocessableCompletionManager { get; }
......@@ -2063,12 +2047,10 @@ void add(string lk, string sk, string v)
{
add("Key-HashSlot", "keyHashSlot", message.GetHashSlot(this.ServerSelectionStrategy).ToString());
}
#if FEATURE_THREADPOOL
int busyWorkerCount = GetThreadPoolStats(out string iocp, out string worker);
add("ThreadPool-IO-Completion", "IOCP", iocp);
add("ThreadPool-Workers", "WORKER", worker);
data.Add(Tuple.Create("Busy-Workers", busyWorkerCount.ToString()));
#endif
#if FEATURE_PERFCOUNTER
if (IncludePerformanceCountersInExceptions)
{
......@@ -2123,7 +2105,6 @@ private static string GetSystemCpuPercent()
: "unavailable";
}
#endif
#if FEATURE_THREADPOOL
private static int GetThreadPoolStats(out string iocp, out string worker)
{
ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxIoThreads);
......@@ -2137,7 +2118,6 @@ private static int GetThreadPoolStats(out string iocp, out string worker)
worker = $"(Busy={busyWorkerThreads},Free={freeWorkerThreads},Min={minWorkerThreads},Max={maxWorkerThreads})";
return busyWorkerThreads;
}
#endif
/// <summary>
/// Should exceptions include identifiable details? (key names, additional .Data annotations)
......
using System;
#if FEATURE_SERIALIZATION
using System.Runtime.Serialization;
#endif
#pragma warning disable RCS1194 // Implement exception constructors.
namespace StackExchange.Redis
{
#if FEATURE_SERIALIZATION
[Serializable]
public sealed partial class RedisCommandException : Exception
{
private RedisCommandException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
}
[Serializable]
public sealed partial class RedisTimeoutException : TimeoutException
{
private RedisTimeoutException(SerializationInfo info, StreamingContext ctx) : base(info, ctx)
{
Commandstatus = (CommandStatus)info.GetValue("commandStatus", typeof(CommandStatus));
}
/// <summary>
/// Serialization implementation; not intended for general usage
/// </summary>
/// <param name="info">Serialization info.</param>
/// <param name="context">Serialization context.</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("commandStatus", Commandstatus);
}
}
[Serializable]
public sealed partial class RedisConnectionException : RedisException
{
private RedisConnectionException(SerializationInfo info, StreamingContext ctx) : base(info, ctx)
{
FailureType = (ConnectionFailureType)info.GetInt32("failureType");
CommandStatus = (CommandStatus)info.GetValue("commandStatus", typeof(CommandStatus));
}
/// <summary>
/// Serialization implementation; not intended for general usage
/// </summary>
/// <param name="info">Serialization info.</param>
/// <param name="context">Serialization context.</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("failureType", (int)FailureType);
info.AddValue("commandStatus", CommandStatus);
}
}
[Serializable]
public partial class RedisException : Exception
{
/// <summary>
/// Deserialization constructor; not intended for general usage
/// </summary>
/// <param name="info">Serialization info.</param>
/// <param name="ctx">Serialization context.</param>
protected RedisException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
}
[Serializable]
public sealed partial class RedisServerException : RedisException
{
private RedisServerException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
}
#endif
/// <summary>
/// Indicates that a command was illegal and was not sent to the server
/// </summary>
[Serializable]
public sealed partial class RedisCommandException : Exception
{
/// <summary>
......@@ -88,11 +22,14 @@ public sealed partial class RedisCommandException : Exception
/// <param name="message">The message for the exception.</param>
/// <param name="innerException">The inner exception.</param>
public RedisCommandException(string message, Exception innerException) : base(message, innerException) { }
private RedisCommandException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
}
/// <summary>
/// Indicates the time allotted for a command or operation has expired.
/// </summary>
[Serializable]
public sealed partial class RedisTimeoutException : TimeoutException
{
/// <summary>
......@@ -109,11 +46,27 @@ public RedisTimeoutException(string message, CommandStatus commandStatus) : base
/// status of the command while communicating with Redis
/// </summary>
public CommandStatus Commandstatus { get; }
private RedisTimeoutException(SerializationInfo info, StreamingContext ctx) : base(info, ctx)
{
Commandstatus = (CommandStatus)info.GetValue("commandStatus", typeof(CommandStatus));
}
/// <summary>
/// Serialization implementation; not intended for general usage
/// </summary>
/// <param name="info">Serialization info.</param>
/// <param name="context">Serialization context.</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("commandStatus", Commandstatus);
}
}
/// <summary>
/// Indicates a connection fault when communicating with redis
/// </summary>
[Serializable]
public sealed partial class RedisConnectionException : RedisException
{
/// <summary>
......@@ -153,11 +106,29 @@ public RedisConnectionException(ConnectionFailureType failureType, string messag
/// status of the command while communicating with Redis
/// </summary>
public CommandStatus CommandStatus { get; }
private RedisConnectionException(SerializationInfo info, StreamingContext ctx) : base(info, ctx)
{
FailureType = (ConnectionFailureType)info.GetInt32("failureType");
CommandStatus = (CommandStatus)info.GetValue("commandStatus", typeof(CommandStatus));
}
/// <summary>
/// Serialization implementation; not intended for general usage
/// </summary>
/// <param name="info">Serialization info.</param>
/// <param name="context">Serialization context.</param>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("failureType", (int)FailureType);
info.AddValue("commandStatus", CommandStatus);
}
}
/// <summary>
/// Indicates an issue communicating with redis
/// </summary>
[Serializable]
public partial class RedisException : Exception
{
/// <summary>
......@@ -172,11 +143,19 @@ public partial class RedisException : Exception
/// <param name="message">The message for the exception.</param>
/// <param name="innerException">The inner exception.</param>
public RedisException(string message, Exception innerException) : base(message, innerException) { }
/// <summary>
/// Deserialization constructor; not intended for general usage
/// </summary>
/// <param name="info">Serialization info.</param>
/// <param name="ctx">Serialization context.</param>
protected RedisException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
}
/// <summary>
/// Indicates an exception raised by a redis server
/// </summary>
[Serializable]
public sealed partial class RedisServerException : RedisException
{
/// <summary>
......@@ -184,5 +163,7 @@ public sealed partial class RedisServerException : RedisException
/// </summary>
/// <param name="message">The message for the exception.</param>
public RedisServerException(string message) : base(message) { }
private RedisServerException(SerializationInfo info, StreamingContext ctx) : base(info, ctx) { }
}
}
......@@ -131,7 +131,7 @@ public static string[] ToStringArray(this RedisValue[] values)
{
if (values == null) return null;
if (values.Length == 0) return nix;
return ConvertHelper.ConvertAll(values, x => (string)x);
return Array.ConvertAll(values, x => (string)x);
}
internal static void AuthenticateAsClient(this SslStream ssl, string host, SslProtocols? allowedProtocols)
......@@ -145,21 +145,12 @@ internal static void AuthenticateAsClient(this SslStream ssl, string host, SslPr
var certificateCollection = new X509CertificateCollection();
const bool checkCertRevocation = true;
#if NETSTANDARD1_5
ssl.AuthenticateAsClientAsync(host, certificateCollection, allowedProtocols.Value, checkCertRevocation)
.GetAwaiter().GetResult();
#else
ssl.AuthenticateAsClient(host, certificateCollection, allowedProtocols.Value, checkCertRevocation);
#endif
}
private static void AuthenticateAsClientUsingDefaultProtocols(SslStream ssl, string host)
{
#if NETSTANDARD1_5
ssl.AuthenticateAsClientAsync(host).GetAwaiter().GetResult();
#else
ssl.AuthenticateAsClient(host);
#endif
}
}
}
......@@ -377,7 +377,7 @@ internal void OnHeartbeat(bool ifConnectedOnly)
switch (state)
{
case (int)State.Connecting:
int connectTimeMilliseconds = unchecked(Environment.TickCount - VolatileWrapper.Read(ref connectStartTicks));
int connectTimeMilliseconds = unchecked(Environment.TickCount - Thread.VolatileRead(ref connectStartTicks));
bool shouldRetry = Multiplexer.RawConfig.ReconnectRetryPolicy.ShouldRetry(Interlocked.Read(ref connectTimeoutRetryCount), connectTimeMilliseconds);
if (shouldRetry)
{
......
......@@ -10,9 +10,6 @@
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
#if NETSTANDARD1_5
using System.Threading.Tasks;
#endif
namespace StackExchange.Redis
{
......@@ -24,28 +21,6 @@ internal sealed partial class PhysicalConnection : IDisposable, ISocketCallback
private static readonly byte[] Crlf = Encoding.ASCII.GetBytes("\r\n");
#if NETSTANDARD1_5
private readonly Action<Task<int>> endRead;
private static Action<Task<int>> EndReadFactory(PhysicalConnection physical)
{
return result =>
{ // can't capture AsyncState on SocketRead, so we'll do it once per physical instead
if (result.IsFaulted)
{
GC.KeepAlive(result.Exception);
}
try
{
physical.Multiplexer.Trace("Completed asynchronously: processing in callback", physical.physicalName);
if (physical.EndReading(result)) physical.BeginReading();
}
catch (Exception ex)
{
physical.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
}
};
}
#else
private static readonly AsyncCallback endRead = result =>
{
PhysicalConnection physical;
......@@ -60,7 +35,6 @@ private static Action<Task<int>> EndReadFactory(PhysicalConnection physical)
physical.RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
}
};
#endif
private static readonly byte[] message = Encoding.UTF8.GetBytes("message"), pmessage = Encoding.UTF8.GetBytes("pmessage");
......@@ -108,15 +82,12 @@ public PhysicalConnection(PhysicalBridge bridge)
var endpoint = bridge.ServerEndPoint.EndPoint;
physicalName = connectionType + "#" + Interlocked.Increment(ref totalCount) + "@" + Format.ToString(endpoint);
Bridge = bridge;
#if NETSTANDARD1_5
endRead = EndReadFactory(this);
#endif
OnCreateEcho();
}
public void BeginConnect(TextWriter log)
{
VolatileWrapper.Write(ref firstUnansweredWriteTickCount, 0);
Thread.VolatileWrite(ref firstUnansweredWriteTickCount, 0);
var endpoint = Bridge.ServerEndPoint.EndPoint;
Multiplexer.Trace("Connecting...", physicalName);
......@@ -132,7 +103,7 @@ private enum ReadMode : byte
public PhysicalBridge Bridge { get; }
public long LastWriteSecondsAgo => unchecked(Environment.TickCount - VolatileWrapper.Read(ref lastWriteTickCount)) / 1000;
public long LastWriteSecondsAgo => unchecked(Environment.TickCount - Thread.VolatileRead(ref lastWriteTickCount)) / 1000;
public ConnectionMultiplexer Multiplexer { get; }
......@@ -145,17 +116,13 @@ public void Dispose()
if (outStream != null)
{
Multiplexer.Trace("Disconnecting...", physicalName);
#if !NETSTANDARD1_5
try { outStream.Close(); } catch { }
#endif
try { outStream.Dispose(); } catch { }
outStream = null;
}
if (netStream != null)
{
#if !NETSTANDARD1_5
try { netStream.Close(); } catch { }
#endif
try { netStream.Dispose(); } catch { }
netStream = null;
}
......@@ -209,9 +176,9 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, ref Socket
if (isCurrent && Interlocked.CompareExchange(ref failureReported, 1, 0) == 0)
{
managerState = SocketManager.ManagerState.RecordConnectionFailed_ReportFailure;
int now = Environment.TickCount, lastRead = VolatileWrapper.Read(ref lastReadTickCount), lastWrite = VolatileWrapper.Read(ref lastWriteTickCount),
lastBeat = VolatileWrapper.Read(ref lastBeatTickCount);
int unansweredRead = VolatileWrapper.Read(ref firstUnansweredWriteTickCount);
int now = Environment.TickCount, lastRead = Thread.VolatileRead(ref lastReadTickCount), lastWrite = Thread.VolatileRead(ref lastWriteTickCount),
lastBeat = Thread.VolatileRead(ref lastBeatTickCount);
int unansweredRead = Thread.VolatileRead(ref firstUnansweredWriteTickCount);
var exMessage = new StringBuilder(failureType.ToString());
......@@ -646,19 +613,6 @@ private unsafe void WriteRaw(Stream stream, string value, int encodedLength)
}
else
{
#if NETSTANDARD1_5
int charsRemaining = value.Length, charOffset = 0, bytesWritten;
var valueCharArray = value.ToCharArray();
while (charsRemaining > Scratch_CharsPerBlock)
{
bytesWritten = outEncoder.GetBytes(valueCharArray, charOffset, Scratch_CharsPerBlock, outScratch, 0, false);
stream.Write(outScratch, 0, bytesWritten);
charOffset += Scratch_CharsPerBlock;
charsRemaining -= Scratch_CharsPerBlock;
}
bytesWritten = outEncoder.GetBytes(valueCharArray, charOffset, charsRemaining, outScratch, 0, true);
if (bytesWritten != 0) stream.Write(outScratch, 0, bytesWritten);
#else
fixed (char* c = value)
fixed (byte* b = outScratch)
{
......@@ -673,7 +627,6 @@ private unsafe void WriteRaw(Stream stream, string value, int encodedLength)
bytesWritten = outEncoder.GetBytes(c + charOffset, charsRemaining, b, ScratchSize, true);
if (bytesWritten != 0) stream.Write(outScratch, 0, bytesWritten);
}
#endif
}
}
......@@ -721,36 +674,15 @@ private void BeginReading()
keepReading = false;
int space = EnsureSpaceAndComputeBytesToRead();
Multiplexer.Trace("Beginning async read...", physicalName);
#if NETSTANDARD1_5
var result = netStream.ReadAsync(ioBuffer, ioBufferBytes, space);
switch (result.Status)
{
case TaskStatus.RanToCompletion:
case TaskStatus.Faulted:
Multiplexer.Trace("Completed synchronously: processing immediately", physicalName);
keepReading = EndReading(result);
break;
default:
result.ContinueWith(endRead);
break;
}
#else
var result = netStream.BeginRead(ioBuffer, ioBufferBytes, space, endRead, this);
if (result.CompletedSynchronously)
{
Multiplexer.Trace("Completed synchronously: processing immediately", physicalName);
keepReading = EndReading(result);
}
#endif
} while (keepReading);
}
#if NETSTANDARD1_5
catch (AggregateException ex)
{
throw ex.InnerException;
}
#endif
catch (System.IO.IOException ex)
catch (IOException ex)
{
Multiplexer.Trace("Could not connect: " + ex.Message, physicalName);
}
......@@ -846,22 +778,6 @@ SocketMode ISocketCallback.Connected(Stream stream, TextWriter log)
}
}
#if NETSTANDARD1_5
private bool EndReading(Task<int> result)
{
try
{
var tmp = netStream;
int bytesRead = tmp == null ? 0 : result.Result; // note we expect this to be completed
return ProcessReadBytes(bytesRead);
}
catch (Exception ex)
{
RecordConnectionFailed(ConnectionFailureType.InternalFailure, ex);
return false;
}
}
#else
private bool EndReading(IAsyncResult result)
{
try
......@@ -875,7 +791,7 @@ private bool EndReading(IAsyncResult result)
return false;
}
}
#endif
private int EnsureSpaceAndComputeBytesToRead()
{
int space = ioBuffer.Length - ioBufferBytes;
......@@ -1007,7 +923,7 @@ private bool ProcessReadBytes(int bytesRead)
Interlocked.Exchange(ref lastReadTickCount, Environment.TickCount);
// reset unanswered write timestamp
VolatileWrapper.Write(ref firstUnansweredWriteTickCount, 0);
Thread.VolatileWrite(ref firstUnansweredWriteTickCount, 0);
ioBufferBytes += bytesRead;
Multiplexer.Trace("More bytes available: " + bytesRead + " (" + ioBufferBytes + ")", physicalName);
......@@ -1168,7 +1084,7 @@ private RawResult TryParseResult(byte[] buffer, ref int offset, ref int count)
public void CheckForStaleConnection(ref SocketManager.ManagerState state)
{
int firstUnansweredWrite = VolatileWrapper.Read(ref firstUnansweredWriteTickCount);
int firstUnansweredWrite = Thread.VolatileRead(ref firstUnansweredWriteTickCount);
DebugEmulateStaleConnection(ref firstUnansweredWrite);
......
......@@ -198,7 +198,7 @@ internal override bool AsBoolean()
throw new InvalidCastException();
}
internal override bool[] AsBooleanArray() => ConvertHelper.ConvertAll(value, x => x.AsBoolean());
internal override bool[] AsBooleanArray() => Array.ConvertAll(value, x => x.AsBoolean());
internal override byte[] AsByteArray()
{
......@@ -206,7 +206,7 @@ internal override byte[] AsByteArray()
throw new InvalidCastException();
}
internal override byte[][] AsByteArrayArray() => ConvertHelper.ConvertAll(value, x => x.AsByteArray());
internal override byte[][] AsByteArrayArray() => Array.ConvertAll(value, x => x.AsByteArray());
internal override double AsDouble()
{
......@@ -214,7 +214,7 @@ internal override double AsDouble()
throw new InvalidCastException();
}
internal override double[] AsDoubleArray() => ConvertHelper.ConvertAll(value, x => x.AsDouble());
internal override double[] AsDoubleArray() => Array.ConvertAll(value, x => x.AsDouble());
internal override int AsInt32()
{
......@@ -222,7 +222,7 @@ internal override int AsInt32()
throw new InvalidCastException();
}
internal override int[] AsInt32Array() => ConvertHelper.ConvertAll(value, x => x.AsInt32());
internal override int[] AsInt32Array() => Array.ConvertAll(value, x => x.AsInt32());
internal override long AsInt64()
{
......@@ -230,7 +230,7 @@ internal override long AsInt64()
throw new InvalidCastException();
}
internal override long[] AsInt64Array() => ConvertHelper.ConvertAll(value, x => x.AsInt64());
internal override long[] AsInt64Array() => Array.ConvertAll(value, x => x.AsInt64());
internal override bool? AsNullableBoolean()
{
......@@ -262,7 +262,7 @@ internal override RedisKey AsRedisKey()
throw new InvalidCastException();
}
internal override RedisKey[] AsRedisKeyArray() => ConvertHelper.ConvertAll(value, x => x.AsRedisKey());
internal override RedisKey[] AsRedisKeyArray() => Array.ConvertAll(value, x => x.AsRedisKey());
internal override RedisResult[] AsRedisResultArray() => value;
......@@ -272,7 +272,7 @@ internal override RedisValue AsRedisValue()
throw new InvalidCastException();
}
internal override RedisValue[] AsRedisValueArray() => ConvertHelper.ConvertAll(value, x => x.AsRedisValue());
internal override RedisValue[] AsRedisValueArray() => Array.ConvertAll(value, x => x.AsRedisValue());
internal override string AsString()
{
......@@ -280,7 +280,7 @@ internal override string AsString()
throw new InvalidCastException();
}
internal override string[] AsStringArray() => ConvertHelper.ConvertAll(value, x => x.AsString());
internal override string[] AsStringArray() => Array.ConvertAll(value, x => x.AsString());
}
private sealed class ErrorRedisResult : RedisResult
......
using System;
#if NETSTANDARD1_5
using System.Collections.Generic;
using System.Reflection;
#endif
using System;
using System.Text;
namespace StackExchange.Redis
......@@ -295,12 +291,7 @@ public int CompareTo(RedisValue other)
if (otherType == CompareType.Double) return thisDouble.CompareTo(otherDouble);
}
// otherwise, compare as strings
#if NETSTANDARD1_5
var compareInfo = System.Globalization.CultureInfo.InvariantCulture.CompareInfo;
return compareInfo.Compare((string)this, (string)other, System.Globalization.CompareOptions.Ordinal);
#else
return StringComparer.InvariantCulture.Compare((string)this, (string)other);
#endif
}
catch (Exception ex)
{
......@@ -575,7 +566,7 @@ object IConvertible.ToType(Type conversionType, IFormatProvider provider)
if (conversionType == null) throw new ArgumentNullException(nameof(conversionType));
if (conversionType == typeof(byte[])) return (byte[])this;
if (conversionType == typeof(RedisValue)) return this;
switch (conversionType.GetTypeCode())
switch (Type.GetTypeCode(conversionType))
{
case TypeCode.Boolean: return (bool)this;
case TypeCode.Byte: return (byte)this;
......@@ -669,47 +660,4 @@ public bool TryParse(out double val)
return TryParseDouble(blob, out val);
}
}
internal static class ReflectionExtensions
{
#if NETSTANDARD1_5
internal static TypeCode GetTypeCode(this Type type)
{
if (type == null) return TypeCode.Empty;
if (typeCodeLookup.TryGetValue(type, out TypeCode result)) return result;
if (type.GetTypeInfo().IsEnum)
{
type = Enum.GetUnderlyingType(type);
if (typeCodeLookup.TryGetValue(type, out result)) return result;
}
return TypeCode.Object;
}
private static readonly Dictionary<Type, TypeCode> typeCodeLookup = new Dictionary<Type, TypeCode>
{
{typeof(bool), TypeCode.Boolean },
{typeof(byte), TypeCode.Byte },
{typeof(char), TypeCode.Char},
{typeof(DateTime), TypeCode.DateTime},
{typeof(decimal), TypeCode.Decimal},
{typeof(double), TypeCode.Double },
{typeof(short), TypeCode.Int16 },
{typeof(int), TypeCode.Int32 },
{typeof(long), TypeCode.Int64 },
{typeof(object), TypeCode.Object},
{typeof(sbyte), TypeCode.SByte },
{typeof(float), TypeCode.Single },
{typeof(string), TypeCode.String },
{typeof(ushort), TypeCode.UInt16 },
{typeof(uint), TypeCode.UInt32 },
{typeof(ulong), TypeCode.UInt64 },
};
#else
internal static TypeCode GetTypeCode(this Type type)
{
return Type.GetTypeCode(type);
}
#endif
}
}
\ No newline at end of file
}
......@@ -97,9 +97,7 @@ public override bool TryComplete(bool isAsync)
{
if (stateOrCompletionSource is TaskCompletionSource<T> tcs)
{
#if !PLAT_SAFE_CONTINUATIONS // we don't need to check in this scenario
if (isAsync || TaskSource.IsSyncSafe(tcs.Task))
#endif
{
UnwrapAndRecycle(this, true, out T val, out Exception ex);
......@@ -117,12 +115,10 @@ public override bool TryComplete(bool isAsync)
}
return true;
}
#if !PLAT_SAFE_CONTINUATIONS
else
{ // looks like continuations; push to async to preserve the reader thread
return false;
}
#endif
}
else
{
......
......@@ -316,11 +316,7 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
LocalBuilder redisKeyLoc = null;
var loc = il.DeclareLocal(t);
il.Emit(OpCodes.Ldarg_0); // object
#if NETSTANDARD1_5
if (t.GetTypeInfo().IsValueType)
#else
if (t.IsValueType)
#endif
{
il.Emit(OpCodes.Unbox_Any, t); // T
}
......@@ -351,11 +347,7 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
{
il.Emit(OpCodes.Dup); // RedisKey[] RedisKey[]
il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisKey[] int
#if NETSTANDARD1_5
if (t.GetTypeInfo().IsValueType)
#else
if (t.IsValueType)
#endif
{
il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisKey[] int T*
}
......@@ -383,11 +375,7 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
{
il.Emit(OpCodes.Dup); // RedisKey[] RedisValue[] RedisValue[]
il.Emit(OpCodes.Ldc_I4, i); // RedisKey[] RedisValue[] RedisValue[] int
#if NETSTANDARD1_5
if (t.GetTypeInfo().IsValueType)
#else
if (t.IsValueType)
#endif
{
il.Emit(OpCodes.Ldloca, loc); // RedisKey[] RedisValue[] RedisValue[] int T*
}
......
......@@ -483,7 +483,7 @@ internal void OnFullyEstablished(PhysicalConnection connection)
internal int LastInfoReplicationCheckSecondsAgo
{
get { return unchecked(Environment.TickCount - VolatileWrapper.Read(ref lastInfoReplicationCheckTicks)) / 1000; }
get { return unchecked(Environment.TickCount - Thread.VolatileRead(ref lastInfoReplicationCheckTicks)) / 1000; }
}
private EndPoint masterEndPoint;
......
......@@ -4,10 +4,6 @@
using System.Net;
using System.Net.Sockets;
using System.Threading;
#if NETSTANDARD1_5
using System.Runtime.InteropServices;
using System.Threading.Tasks;
#endif
namespace StackExchange.Redis
{
......@@ -146,12 +142,8 @@ public SocketManager(string name, bool useHighPrioritySocketThreads)
// we need a dedicated writer, because when under heavy ambient load
// (a busy asp.net site, for example), workers are not reliable enough
#if NETSTANDARD1_5
var dedicatedWriter = new Thread(writeAllQueues);
#else
var dedicatedWriter = new Thread(writeAllQueues, 32 * 1024); // don't need a huge stack;
dedicatedWriter.Priority = useHighPrioritySocketThreads ? ThreadPriority.AboveNormal : ThreadPriority.Normal;
#endif
dedicatedWriter.Name = name + ":Write";
dedicatedWriter.IsBackground = true; // should not keep process alive
dedicatedWriter.Start(this); // will self-exit when disposed
......@@ -179,7 +171,6 @@ public void Dispose()
internal SocketToken BeginConnect(EndPoint endpoint, ISocketCallback callback, ConnectionMultiplexer multiplexer, TextWriter log)
{
#if !NETSTANDARD1_5
void RunWithCompletionType(Func<AsyncCallback, IAsyncResult> beginAsync, AsyncCallback asyncCallback)
{
void proxyCallback(IAsyncResult ar)
......@@ -197,7 +188,6 @@ void proxyCallback(IAsyncResult ar)
asyncCallback(result);
}
}
#endif
var addressFamily = endpoint.AddressFamily == AddressFamily.Unspecified ? AddressFamily.InterNetwork : endpoint.AddressFamily;
var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
......@@ -211,14 +201,6 @@ void proxyCallback(IAsyncResult ar)
// A work-around for a Mono bug in BeginConnect(EndPoint endpoint, AsyncCallback callback, object state)
if (endpoint is DnsEndPoint dnsEndpoint)
{
#if NETSTANDARD1_5 // No BeginConnect there, because everything was an Async push...we should drop this
socket.ConnectAsync(dnsEndpoint.Host, dnsEndpoint.Port).ContinueWith(t =>
{
multiplexer.LogLocked(log, "EndConnect: {0}", formattedEndpoint);
EndConnectImpl(t, multiplexer, log, tuple);
multiplexer.LogLocked(log, "Connect complete: {0}", formattedEndpoint);
});
#else
RunWithCompletionType(
cb => socket.BeginConnect(dnsEndpoint.Host, dnsEndpoint.Port, cb, tuple),
ar => {
......@@ -226,17 +208,9 @@ void proxyCallback(IAsyncResult ar)
EndConnectImpl(ar, multiplexer, log, tuple);
multiplexer.LogLocked(log, "Connect complete: {0}", formattedEndpoint);
});
#endif
}
else
{
#if NETSTANDARD1_5 // No BeginConnect there, because everything was an Async push...we should drop this
socket.ConnectAsync(endpoint).ContinueWith(t =>
{
multiplexer.LogLocked(log, "EndConnect: {0}", formattedEndpoint);
EndConnectImpl(t, multiplexer, log, tuple);
});
#else
RunWithCompletionType(
cb => socket.BeginConnect(endpoint, cb, tuple),
ar => {
......@@ -244,7 +218,6 @@ void proxyCallback(IAsyncResult ar)
EndConnectImpl(ar, multiplexer, log, tuple);
multiplexer.LogLocked(log, "Connect complete: {0}", formattedEndpoint);
});
#endif
}
}
catch (NotImplementedException ex)
......@@ -266,25 +239,6 @@ internal void SetFastLoopbackOption(Socket socket)
// or will be subject to WFP filtering.
const int SIO_LOOPBACK_FAST_PATH = -1744830448;
#if NETSTANDARD1_5
try
{
// Ioctl is not supported on other platforms at the moment
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
byte[] optionInValue = BitConverter.GetBytes(1);
socket.IOControl(SIO_LOOPBACK_FAST_PATH, optionInValue, null);
}
}
catch (SocketException) { }
catch (PlatformNotSupportedException)
{
// Fix for https://github.com/StackExchange/StackExchange.Redis/issues/582
// Checking the platform can fail on some platforms. However, we don't
// care if the platform check fails because this is for a Windows
// optimization, and checking the platform will not fail on Windows.
}
#else
// windows only
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
......@@ -296,7 +250,6 @@ internal void SetFastLoopbackOption(Socket socket)
socket.IOControl(SIO_LOOPBACK_FAST_PATH, optionInValue, null);
}
}
#endif
}
internal void RequestWrite(PhysicalBridge bridge, bool forced)
......@@ -332,11 +285,7 @@ private void EndConnectImpl(IAsyncResult ar, ConnectionMultiplexer multiplexer,
if (ignoreConnect) return;
var socket = tuple.Item1;
var callback = tuple.Item2;
#if NETSTANDARD1_5
multiplexer.Wait((Task)ar); // make it explode if invalid (note: already complete at this point)
#else
socket.EndConnect(ar);
#endif
var netStream = new NetworkStream(socket, false);
var socketMode = callback?.Connected(netStream, log) ?? SocketMode.Abort;
switch (socketMode)
......@@ -401,9 +350,7 @@ private void Shutdown(Socket socket)
{
OnShutdown(socket);
try { socket.Shutdown(SocketShutdown.Both); } catch { }
#if !NETSTANDARD1_5
try { socket.Close(); } catch { }
#endif
try { socket.Dispose(); } catch { }
}
}
......
using System.Threading.Tasks;
#if !PLAT_SAFE_CONTINUATIONS
using System;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
#endif
namespace StackExchange.Redis
{
......@@ -15,7 +12,6 @@ namespace StackExchange.Redis
/// </summary>
internal static class TaskSource
{
#if !PLAT_SAFE_CONTINUATIONS
// on .NET < 4.6, it was possible to have threads hijacked; this is no longer a problem in 4.6 and core-clr 5,
// thanks to the new TaskCreationOptions.RunContinuationsAsynchronously, however we still need to be a little
// "test and react", as we could be targeting 4.5 but running on a 4.6 machine, in which case *it can still
......@@ -80,7 +76,7 @@ static TaskSource()
IsSyncSafe = _ => false; // assume: not
}
}
#endif
/// <summary>
/// Create a new TaskCompletion source
/// </summary>
......@@ -88,11 +84,7 @@ static TaskSource()
/// <param name="asyncState">The state for the created <see cref="TaskCompletionSource{TResult}"/>.</param>
public static TaskCompletionSource<T> Create<T>(object asyncState)
{
#if PLAT_SAFE_CONTINUATIONS
return new TaskCompletionSource<T>(asyncState, TaskCreationOptions.RunContinuationsAsynchronously);
#else
return new TaskCompletionSource<T>(asyncState, TaskCreationOptions.None);
#endif
}
}
}
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