diff --git a/Capnp.Net.Runtime.Tests/Capnp.Net.Runtime.Tests.csproj b/Capnp.Net.Runtime.Tests/Capnp.Net.Runtime.Tests.csproj index 66c9807..d5e3394 100644 --- a/Capnp.Net.Runtime.Tests/Capnp.Net.Runtime.Tests.csproj +++ b/Capnp.Net.Runtime.Tests/Capnp.Net.Runtime.Tests.csproj @@ -1,11 +1,13 @@ - + - netcoreapp2.1 + netcoreapp2.2 false 7.1 + + Library diff --git a/Capnp.Net.Runtime.Tests/FramePumpTests.cs b/Capnp.Net.Runtime.Tests/FramePumpTests.cs index 0f13efb..2ebef32 100644 --- a/Capnp.Net.Runtime.Tests/FramePumpTests.cs +++ b/Capnp.Net.Runtime.Tests/FramePumpTests.cs @@ -111,7 +111,9 @@ namespace Capnp.Net.Runtime.Tests Assert.AreEqual(expectedCount - i, length); for (int j = 0; j < length; j++) { - Assert.AreEqual((ulong)(length - j), frame.Segments[i].Span[j]); + var expected = (ulong) (length - j); + var actual = frame.Segments[i].Span[j]; + Assert.AreEqual(expected, actual); } } @@ -165,7 +167,7 @@ namespace Capnp.Net.Runtime.Tests txPump.Send(PackFrame(4)); txPump.Send(PackFrame(5)); - Assert.IsTrue(SpinWait.SpinUntil(() => bc.Count == 8, 500)); + Assert.IsTrue(SpinWait.SpinUntil(() => bc.Count == 8, 50000)); UnpackAndVerifyFrame(bc.Take(), 1); UnpackAndVerifyFrame(bc.Take(), 8); diff --git a/Capnp.Net.Runtime.Tests/ProvidedCapabilityMultiCallMock.cs b/Capnp.Net.Runtime.Tests/ProvidedCapabilityMultiCallMock.cs index 50cccae..292c678 100644 --- a/Capnp.Net.Runtime.Tests/ProvidedCapabilityMultiCallMock.cs +++ b/Capnp.Net.Runtime.Tests/ProvidedCapabilityMultiCallMock.cs @@ -1,8 +1,8 @@ using System.Threading; using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; using Capnp.Rpc; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Threading.Tasks.Dataflow; namespace Capnp.Net.Runtime.Tests { diff --git a/Capnp.Net.Runtime.Tests/TcpRpc.cs b/Capnp.Net.Runtime.Tests/TcpRpc.cs index 63b2fae..7239075 100644 --- a/Capnp.Net.Runtime.Tests/TcpRpc.cs +++ b/Capnp.Net.Runtime.Tests/TcpRpc.cs @@ -1,11 +1,13 @@ +using System; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using Capnp.Rpc; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Microsoft.Extensions.Log­ging; +using Microsoft.Extensions.Logging; using System.Diagnostics; +using Exception = Capnp.Rpc.Exception; namespace Capnp.Net.Runtime.Tests { @@ -41,7 +43,7 @@ namespace Capnp.Net.Runtime.Tests int MediumTimeout => Debugger.IsAttached ? Timeout.Infinite : 2000; - [TestMethod] + [TestMethod, Timeout(10000)] public void CreateAndDispose() { (var server, var client) = SetupClientServerPair(); @@ -52,7 +54,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void ConnectAndDispose() { (var server, var client) = SetupClientServerPair(); @@ -60,13 +62,21 @@ namespace Capnp.Net.Runtime.Tests using (server) using (client) { - Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); - SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); - Assert.AreEqual(1, server.ConnectionCount); + try + { + Assert.IsTrue(client.WhenConnected.Wait(MediumTimeout)); + SpinWait.SpinUntil(() => server.ConnectionCount > 0, MediumTimeout); + Assert.AreEqual(1, server.ConnectionCount); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } } } - [TestMethod] + [TestMethod, Timeout(10000)] public void ConnectNoServer() { using (var client = new TcpRpcClient("localhost", TcpPort)) @@ -75,7 +85,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void ConnectAndBootstrap() { (var server, var client) = SetupClientServerPair(); @@ -94,7 +104,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void ConnectNoBootstrap() { (var server, var client) = SetupClientServerPair(); @@ -112,7 +122,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void CallReturn() { (var server, var client) = SetupClientServerPair(); @@ -153,7 +163,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void CallCancelOnServer() { (var server, var client) = SetupClientServerPair(); @@ -188,7 +198,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void CallCancelOnClient() { ExpectingLogOutput = false; @@ -233,7 +243,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void CallReturnAfterClientSideCancel() { ExpectingLogOutput = false; @@ -295,7 +305,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void CallServerSideException() { (var server, var client) = SetupClientServerPair(); @@ -332,7 +342,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void PipelineBeforeReturn() { (var server, var client) = SetupClientServerPair(); @@ -405,7 +415,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void PipelineAfterReturn() { (var server, var client) = SetupClientServerPair(); @@ -481,7 +491,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void PipelineMultiple() { (var server, var client) = SetupClientServerPair(); @@ -598,7 +608,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void PipelineCallAfterDisposal() { (var server, var client) = SetupClientServerPair(); @@ -635,7 +645,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void PipelineCallDuringDisposal() { (var server, var client) = SetupClientServerPair(); diff --git a/Capnp.Net.Runtime.Tests/TcpRpcAdvancedStuff.cs b/Capnp.Net.Runtime.Tests/TcpRpcAdvancedStuff.cs index 9a7b6a6..ab161b1 100644 --- a/Capnp.Net.Runtime.Tests/TcpRpcAdvancedStuff.cs +++ b/Capnp.Net.Runtime.Tests/TcpRpcAdvancedStuff.cs @@ -12,7 +12,7 @@ namespace Capnp.Net.Runtime.Tests [TestClass] public class TcpRpcAdvancedStuff: TestBase { - [TestMethod] + [TestMethod, Timeout(10000)] public void MultiConnect() { using (var server = SetupServer()) @@ -50,7 +50,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void TwoClients() { using (var server = SetupServer()) diff --git a/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs b/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs index 8b20837..fea3474 100644 --- a/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs +++ b/Capnp.Net.Runtime.Tests/TcpRpcInterop.cs @@ -95,7 +95,7 @@ namespace Capnp.Net.Runtime.Tests Assert.AreEqual(expected, line.Result); } - [TestMethod] + [TestMethod, Timeout(10000)] public void BasicClient() { LaunchCompatTestProcess("server:Interface", stdout => @@ -126,7 +126,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void BasicServer() { using (var server = SetupServer()) @@ -143,7 +143,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void PipelineClient() { LaunchCompatTestProcess("server:Pipeline", stdout => @@ -180,7 +180,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void PipelineServer() { using (var server = SetupServer()) @@ -198,7 +198,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void ReleaseClient() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -231,7 +231,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void ReleaseServer() { using (var server = SetupServer()) @@ -259,7 +259,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void ReleaseOnCancelClient() { // Since we have a threaded model, there is no way to deterministically provoke the situation @@ -337,7 +337,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void ReleaseOnCancelServer() { using (var server = SetupServer()) @@ -354,7 +354,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void TestTailCallClient() { LaunchCompatTestProcess("server:TailCaller", stdout => @@ -389,7 +389,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void TestTailCallServer() { using (var server = SetupServer()) @@ -408,7 +408,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void CancelationServer() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -432,13 +432,13 @@ namespace Capnp.Net.Runtime.Tests cts.Cancel(); Assert.IsTrue(destroyed.Task.Wait(MediumNonDbgTimeout)); - Assert.IsFalse(cancelTask.IsCompletedSuccessfully); + Assert.IsFalse(cancelTask.IsCompleted && !cancelTask.IsCanceled); } } }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void CancelationClient() { using (var server = SetupServer()) @@ -455,7 +455,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void PromiseResolveServer() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -497,7 +497,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void PromiseResolveClient() { using (var server = SetupServer()) @@ -516,7 +516,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void RetainAndReleaseServer() { var destructionPromise = new TaskCompletionSource(); @@ -594,7 +594,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void RetainAndReleaseClient() { using (var server = SetupServer()) @@ -614,7 +614,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void CancelServer() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -655,7 +655,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void CancelClient() { using (var server = SetupServer()) @@ -672,7 +672,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void SendTwiceServer() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -716,7 +716,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void SendTwiceClient() { using (var server = SetupServer()) @@ -736,7 +736,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void EmbargoServer() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -803,7 +803,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void EmbargoClient() { using (var server = SetupServer()) @@ -873,13 +873,13 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void EmbargoErrorServer() { LaunchCompatTestProcess("server:MoreStuff", EmbargoErrorImpl); } - [TestMethod] + [TestMethod, Timeout(10000)] public void RepeatedEmbargoError() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -891,7 +891,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void EmbargoErrorClient() { using (var server = SetupServer()) @@ -907,7 +907,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void EmbargoNullServer() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -956,7 +956,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void EmbargoNullClient() { using (var server = SetupServer()) @@ -972,7 +972,7 @@ namespace Capnp.Net.Runtime.Tests } } - [TestMethod] + [TestMethod, Timeout(10000)] public void CallBrokenPromiseServer() { LaunchCompatTestProcess("server:MoreStuff", stdout => @@ -1009,7 +1009,7 @@ namespace Capnp.Net.Runtime.Tests }); } - [TestMethod] + [TestMethod, Timeout(10000)] public void CallBrokenPromiseClient() { using (var server = SetupServer()) diff --git a/Capnp.Net.Runtime.Tests/TcpRpcPorted.cs b/Capnp.Net.Runtime.Tests/TcpRpcPorted.cs index a13b4a5..bd1f269 100644 --- a/Capnp.Net.Runtime.Tests/TcpRpcPorted.cs +++ b/Capnp.Net.Runtime.Tests/TcpRpcPorted.cs @@ -212,7 +212,7 @@ namespace Capnp.Net.Runtime.Tests cts.Cancel(); Assert.IsTrue(destroyed.Task.Wait(MediumNonDbgTimeout)); - Assert.IsFalse(cancelTask.IsCompletedSuccessfully); + Assert.IsFalse(cancelTask.IsCompleted && !cancelTask.IsCanceled); } } diff --git a/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj b/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj index 3520379..2c53100 100644 --- a/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj +++ b/Capnp.Net.Runtime/Capnp.Net.Runtime.csproj @@ -1,7 +1,7 @@ - + - netcoreapp2.1 + netstandard2.0 Capnp 7.2 diff --git a/Capnp.Net.Runtime/FramePump.cs b/Capnp.Net.Runtime/FramePump.cs index 837648c..9781ae8 100644 --- a/Capnp.Net.Runtime/FramePump.cs +++ b/Capnp.Net.Runtime/FramePump.cs @@ -75,12 +75,14 @@ namespace Capnp if (frame.Segments.Count == 0) throw new ArgumentException("Expected at least one segment"); - + foreach (var segment in frame.Segments) { if (segment.Length == 0) throw new ArgumentException("Segment must not have zero length"); } + + lock (_writeLock) { @@ -99,8 +101,7 @@ namespace Capnp foreach (var segment in frame.Segments) { - var bytes = MemoryMarshal.Cast(segment.Span); - + var bytes = MemoryMarshal.Cast(segment.Span).ToArray(); _writer.Write(bytes); } } @@ -123,7 +124,7 @@ namespace Capnp /// to be part of normal operation. It does pass exceptions which arise due to I/O errors or invalid data. /// /// The underlying stream does not support reading or is already closed. - /// Received invalid data. + /// Encountered Invalid Framing Data /// Received a message with too many or too big segments, probably dues to invalid data. /// An I/O error occurs. public void Run() @@ -135,55 +136,19 @@ namespace Capnp while (true) { IsWaitingForData = true; - - uint scountm = reader.ReadUInt32(); - int scount = checked((int)(scountm + 1)); - var buffers = new Memory[scount]; - - for (uint i = 0; i < scount; i++) - { - int size = checked((int)reader.ReadUInt32()); - - // This implementation will never send empty segments. - // Other implementations should not. An empty segment may also - // indicate and end-of-stream (stream closed) condition. - if (size == 0) - { - Logger.LogInformation("Received zero-sized segment, stopping interaction"); - return; - } - - buffers[i] = new Memory(new ulong[size]); - } - - if ((scount & 1) == 0) - { - // Padding - reader.ReadUInt32(); - } - - for (uint i = 0; i < scount; i++) - { - var buffer = MemoryMarshal.Cast(buffers[i].Span); - - int got = reader.Read(buffer); - - if (got != buffer.Length) - { - Logger.LogWarning("Received incomplete frame"); - - throw new EndOfStreamException("Expected more bytes according to framing header"); - } - } - + var frame = reader.ReadWireFrame(); IsWaitingForData = false; - - FrameReceived?.Invoke(new WireFrame(new ArraySegment>(buffers, 0, scount))); + FrameReceived?.Invoke(frame); } } } catch (EndOfStreamException) { + Logger.LogWarning("Encountered End of Stream"); + } + catch (InvalidDataException e) + { + Logger.LogWarning(e.Message); } catch (ObjectDisposedException) { diff --git a/Capnp.Net.Runtime/Framing.cs b/Capnp.Net.Runtime/Framing.cs index 657ffc1..5a0d858 100644 --- a/Capnp.Net.Runtime/Framing.cs +++ b/Capnp.Net.Runtime/Framing.cs @@ -21,39 +21,57 @@ namespace Capnp /// The end of the stream is reached. /// The stream is closed. /// An I/O error occurs. - /// Encountered invalid framing data. + /// Encountered invalid framing data. /// Too many or too large segments, probably due to invalid framing data. public static WireFrame ReadSegments(Stream stream) { using (var reader = new BinaryReader(stream, Encoding.Default, true)) { - uint scountm = reader.ReadUInt32(); - uint scount = checked(scountm + 1); - var buffers = new Memory[scount]; + return reader.ReadWireFrame(); + } + } - for (uint i = 0; i < scount; i++) + public static WireFrame ReadWireFrame(this BinaryReader reader) + { + uint scount = reader.ReadUInt32(); + if(scount++ == UInt32.MaxValue) throw new InvalidDataException("Encountered Invalid Framing Data"); + var buffers = new Memory[scount]; + + for (uint i = 0; i < scount; i++) + { + uint size = reader.ReadUInt32(); + if(size==0) throw new EndOfStreamException("Stream Closed"); + buffers[i] = new Memory(new ulong[size]); + } + + if ((scount & 1) == 0) + { + // Padding + reader.ReadUInt32(); + } + + FillBuffersFromFrames(buffers, scount, reader); + + return new WireFrame(buffers); + } + + public static void FillBuffersFromFrames(Memory[] buffers, uint segmentCount, BinaryReader reader) + { + for (uint i = 0; i < segmentCount; i++) + { + var buffer = MemoryMarshal.Cast(buffers[i].Span.ToArray()); + var tmpBuffer = reader.ReadBytes(buffer.Length); + + if (tmpBuffer.Length != buffer.Length) { - uint size = reader.ReadUInt32(); - buffers[i] = new Memory(new ulong[size]); + throw new InvalidDataException("Expected more bytes according to framing header"); } - - if ((scount & 1) == 0) + + for (int j = 0; j < buffers[i].Length; j++) { - // Padding - reader.ReadUInt32(); + var value = BitConverter.ToUInt64(tmpBuffer, j*8); + buffers[i].Span[j] = value; } - - for (uint i = 0; i < scount; i++) - { - var buffer = MemoryMarshal.Cast(buffers[i].Span); - - if (reader.Read(buffer) != buffer.Length) - { - throw new EndOfStreamException("Expected more bytes according to framing header"); - } - } - - return new WireFrame(buffers); } } } diff --git a/Capnp.Net.Runtime/ListOfPrimitivesDeserializer.cs b/Capnp.Net.Runtime/ListOfPrimitivesDeserializer.cs index fc10145..2c9d887 100644 --- a/Capnp.Net.Runtime/ListOfPrimitivesDeserializer.cs +++ b/Capnp.Net.Runtime/ListOfPrimitivesDeserializer.cs @@ -208,7 +208,7 @@ namespace Capnp var utf8Bytes = PrimitiveCast().Data; if (utf8Bytes.Length == 0) return string.Empty; var utf8GytesNoZterm = utf8Bytes.Slice(0, utf8Bytes.Length - 1); - return Encoding.UTF8.GetString(utf8GytesNoZterm); + return Encoding.UTF8.GetString(utf8GytesNoZterm.ToArray()); } IEnumerable Enumerate() diff --git a/Capnp.Net.Runtime/PrimitiveCoder.cs b/Capnp.Net.Runtime/PrimitiveCoder.cs index ba03f80..6412199 100644 --- a/Capnp.Net.Runtime/PrimitiveCoder.cs +++ b/Capnp.Net.Runtime/PrimitiveCoder.cs @@ -24,10 +24,10 @@ namespace Capnp Coder.Fn = (x, y) => x ^ y; Coder.Fn = (x, y) => { - int xi = BitConverter.SingleToInt32Bits(x); - int yi = BitConverter.SingleToInt32Bits(y); + int xi = x.SingleToInt32(); + int yi = y.SingleToInt32(); int zi = xi ^ yi; - return BitConverter.Int32BitsToSingle(zi); + return BitConverter.ToSingle(BitConverter.GetBytes(zi), 0); }; Coder.Fn = (x, y) => { diff --git a/Capnp.Net.Runtime/Rpc/LazyCapability.cs b/Capnp.Net.Runtime/Rpc/LazyCapability.cs index b90c60a..607b4a6 100644 --- a/Capnp.Net.Runtime/Rpc/LazyCapability.cs +++ b/Capnp.Net.Runtime/Rpc/LazyCapability.cs @@ -52,7 +52,7 @@ namespace Capnp.Rpc internal override void Export(IRpcEndpoint endpoint, CapDescriptor.WRITER writer) { - if (WhenResolved.IsCompletedSuccessfully) + if (WhenResolved.ReplacementTaskIsCompletedSuccessfully()) { WhenResolved.Result.Export(endpoint, writer); } diff --git a/Capnp.Net.Runtime/Rpc/PromisedCapability.cs b/Capnp.Net.Runtime/Rpc/PromisedCapability.cs index 6a7ad73..7eefd0a 100644 --- a/Capnp.Net.Runtime/Rpc/PromisedCapability.cs +++ b/Capnp.Net.Runtime/Rpc/PromisedCapability.cs @@ -77,7 +77,8 @@ namespace Capnp.Rpc { lock (_reentrancyBlocker) { - if (_resolvedCap.Task.IsCompletedSuccessfully) + + if (_resolvedCap.Task.ReplacementTaskIsCompletedSuccessfully()) { _resolvedCap.Task.Result.Export(endpoint, writer); } diff --git a/Capnp.Net.Runtime/Rpc/RpcEngine.cs b/Capnp.Net.Runtime/Rpc/RpcEngine.cs index 0cb05bc..95f8bdf 100644 --- a/Capnp.Net.Runtime/Rpc/RpcEngine.cs +++ b/Capnp.Net.Runtime/Rpc/RpcEngine.cs @@ -179,13 +179,9 @@ namespace Capnp.Rpc uint RandId() { - uint id = 0; - var idSpan = MemoryMarshal.CreateSpan(ref id, 1); - var idBytes = MemoryMarshal.Cast(idSpan); - - _random.NextBytes(idBytes); - - return id; + var holder = new byte[4]; + _random.NextBytes(holder); + return BitConverter.ToUInt32(holder,0); } uint AllocateExport(Skeleton providedCapability, out bool first) @@ -237,7 +233,7 @@ namespace Capnp.Rpc lock (_reentrancyBlocker) { - while (!_questionTable.TryAdd(questionId, question)) + while (!_questionTable.ReplacementTryAdd(questionId, question)) { questionId = RandId(); var oldQuestion = question; @@ -277,7 +273,7 @@ namespace Capnp.Rpc { uint id = RandId(); - while (!_pendingDisembargos.TryAdd(id, tcs)) + while (!_pendingDisembargos.ReplacementTryAdd(id, tcs)) { id = RandId(); } @@ -323,7 +319,7 @@ namespace Capnp.Rpc bool added; lock (_reentrancyBlocker) { - added = _answerTable.TryAdd(req.QuestionId, pendingAnswer); + added = _answerTable.ReplacementTryAdd(req.QuestionId, pendingAnswer); } if (!added) @@ -382,7 +378,7 @@ namespace Capnp.Rpc bool added; lock (_reentrancyBlocker) { - added = _answerTable.TryAdd(req.QuestionId, pendingAnswer); + added = _answerTable.ReplacementTryAdd(req.QuestionId, pendingAnswer); } if (!added) @@ -876,7 +872,7 @@ namespace Capnp.Rpc lock (_reentrancyBlocker) { - exists = _pendingDisembargos.Remove(disembargo.Context.ReceiverLoopback, out tcs); + exists = _pendingDisembargos.ReplacementTryRemove(disembargo.Context.ReceiverLoopback, out tcs); } if (exists) @@ -950,7 +946,7 @@ namespace Capnp.Rpc lock (_reentrancyBlocker) { - exists = _answerTable.Remove(finish.QuestionId, out answer); + exists = _answerTable.ReplacementTryRemove(finish.QuestionId, out answer); } if (exists) @@ -988,7 +984,7 @@ namespace Capnp.Rpc if (rc.RefCount == 0) { _exportTable.Remove(id); - _revExportTable.Remove(rc.Cap, out uint _); + _revExportTable.ReplacementTryRemove(rc.Cap, out uint _); } } catch (System.Exception) diff --git a/Capnp.Net.Runtime/Rpc/TcpRpcClient.cs b/Capnp.Net.Runtime/Rpc/TcpRpcClient.cs index 89b2cbb..78c6482 100644 --- a/Capnp.Net.Runtime/Rpc/TcpRpcClient.cs +++ b/Capnp.Net.Runtime/Rpc/TcpRpcClient.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -62,6 +63,10 @@ namespace Capnp.Rpc { throw new RpcException("TcpRpcClient is unable to connect", exception); } + catch (Exception e) + { + Logger.LogError("UNHANDLED EXCEPTION"); + } } async Task Connect(string host, int port) @@ -103,6 +108,7 @@ namespace Capnp.Rpc { _rpcEngine = new RpcEngine(); _client = new TcpClient(); + _client.ExclusiveAddressUse = false; WhenConnected = Connect(host, port); } @@ -119,7 +125,7 @@ namespace Capnp.Rpc throw new InvalidOperationException("Connection not yet established"); } - if (!WhenConnected.IsCompletedSuccessfully) + if (!WhenConnected.ReplacementTaskIsCompletedSuccessfully()) { throw new InvalidOperationException("Connection not successfully established"); } @@ -143,8 +149,9 @@ namespace Capnp.Rpc Logger.LogError("Unable to join connection task within timeout"); } } - catch (System.Exception) + catch (System.Exception e) { + Logger.LogError(e, "Failure disposing client"); } if (_pumpThread != null && !_pumpThread.Join(500)) diff --git a/Capnp.Net.Runtime/Rpc/TcpRpcServer.cs b/Capnp.Net.Runtime/Rpc/TcpRpcServer.cs index e5dc638..e1f6586 100644 --- a/Capnp.Net.Runtime/Rpc/TcpRpcServer.cs +++ b/Capnp.Net.Runtime/Rpc/TcpRpcServer.cs @@ -188,12 +188,10 @@ namespace Capnp.Rpc { _rpcEngine = new RpcEngine(); _listener = new TcpListener(localAddr, port); + _listener.ExclusiveAddressUse = false; _listener.Start(); - _acceptorThread = new Thread(() => - { - AcceptClients(); - }); + _acceptorThread = new Thread(AcceptClients); _acceptorThread.Start(); } diff --git a/Capnp.Net.Runtime/Rpc/rpc.cs b/Capnp.Net.Runtime/Rpc/rpc.cs index 35b0269..b0e5f31 100644 --- a/Capnp.Net.Runtime/Rpc/rpc.cs +++ b/Capnp.Net.Runtime/Rpc/rpc.cs @@ -2528,7 +2528,7 @@ namespace Capnp.Rpc } } - public class Exception : ICapnpSerializable + public class Exception : System.Exception, ICapnpSerializable { void ICapnpSerializable.Deserialize(DeserializerState arg_) { diff --git a/Capnp.Net.Runtime/SerializerExtensions.cs b/Capnp.Net.Runtime/SerializerExtensions.cs index 97a2249..727f043 100644 --- a/Capnp.Net.Runtime/SerializerExtensions.cs +++ b/Capnp.Net.Runtime/SerializerExtensions.cs @@ -285,9 +285,9 @@ namespace Capnp public static float ReadDataFloat(this T d, ulong bitOffset, float defaultValue = 0) where T : IStructDeserializer { - int defaultBits = BitConverter.SingleToInt32Bits(defaultValue); + int defaultBits = defaultValue.SingleToInt32(); int bits = (int)d.StructReadData(bitOffset, 32) ^ defaultBits; - return BitConverter.Int32BitsToSingle(bits); + return bits.Int32ToSingle(); } /// @@ -301,8 +301,8 @@ namespace Capnp public static void WriteData(this T d, ulong bitOffset, float value, float defaultValue = 0.0f) where T : IStructSerializer { - int bits = BitConverter.SingleToInt32Bits(value); - int defaultBits = BitConverter.SingleToInt32Bits(defaultValue); + int bits = value.SingleToInt32(); + int defaultBits = defaultValue.SingleToInt32(); WriteData(d, bitOffset, bits, defaultBits); } diff --git a/Capnp.Net.Runtime/SerializerState.cs b/Capnp.Net.Runtime/SerializerState.cs index c5a1452..be50748 100644 --- a/Capnp.Net.Runtime/SerializerState.cs +++ b/Capnp.Net.Runtime/SerializerState.cs @@ -1068,7 +1068,7 @@ namespace Capnp { var bytes = ListGetBytes(); if (bytes.Length == 0) return string.Empty; - return Encoding.UTF8.GetString(bytes.Slice(0, bytes.Length - 1)); + return Encoding.UTF8.GetString(bytes.Slice(0, bytes.Length - 1).ToArray()); } /// @@ -1164,8 +1164,8 @@ namespace Capnp /// is out of bounds. public void ListWriteValue(int index, float value, float defaultValue = 0) { - int rcastValue = BitConverter.SingleToInt32Bits(value); - int rcastDefaultValue = BitConverter.SingleToInt32Bits(defaultValue); + int rcastValue = value.SingleToInt32(); + int rcastDefaultValue = defaultValue.SingleToInt32(); ListWriteValue(index, rcastValue, rcastDefaultValue); } diff --git a/Capnp.Net.Runtime/UtilityExtensions.cs b/Capnp.Net.Runtime/UtilityExtensions.cs new file mode 100644 index 0000000..cb880ac --- /dev/null +++ b/Capnp.Net.Runtime/UtilityExtensions.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Capnp +{ + public static class UtilityExtensions + { + /// + /// This method exists until NET Standard 2.1 is released + /// + /// + /// + /// + /// + /// + /// + public static bool ReplacementTryAdd(this Dictionary thisDict, K key, V value) + { + if (thisDict.ContainsKey(key)) + return false; + thisDict.Add(key, value); + return true; + } + + /// + /// This method exists until NET Standard 2.1 is released + /// + /// + /// + /// + /// + /// + /// + public static bool ReplacementTryRemove(this Dictionary thisDict, K key, out V value) + { + if (!thisDict.ContainsKey(key)) + { + value = default; + return false; + } + value = thisDict[key]; + return thisDict.Remove(key); + } + + /// + /// This method exists until NET Standard 2.1 is released + /// + /// + /// + /// + public static bool ReplacementTaskIsCompletedSuccessfully(this Task task) + { + return task.IsCompleted && !task.IsCanceled && !task.IsFaulted; + } + + public static int SingleToInt32(this float value) + { + var valueBytes = BitConverter.GetBytes(value); + return BitConverter.ToInt32(valueBytes,0); + } + + public static float Int32ToSingle(this int value) => + BitConverter.ToSingle(BitConverter.GetBytes(value),0); + } +} \ No newline at end of file diff --git a/Capnp.Net.sln b/Capnp.Net.sln index e6d4e04..080c747 100644 --- a/Capnp.Net.sln +++ b/Capnp.Net.sln @@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime", "Capnp. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "capnpc-csharp", "capnpc-csharp\capnpc-csharp.csproj", "{D19E5EA7-D35B-4A1F-99CB-ED136316B577}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "capnpc-binblob", "capnpc-binblob\capnpc-binblob.csproj", "{8C17F147-D784-4584-80FF-21BE03AC0D17}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests", "Capnp.Net.Runtime.Tests\Capnp.Net.Runtime.Tests.csproj", "{9ED38750-F83F-4B10-B3A3-4FD6183F9E86}" EndProject Global