Handle posting to the libuv thread after StopAsync (#2388)
- Check if the post handle is disposed and noop if it is. We also catch an ObjectDisposedException because it's an inherent race condition.
This commit is contained in:
parent
f6108928d8
commit
572627e88c
|
|
@ -177,11 +177,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
|||
|
||||
public void Post<T>(Action<T> callback, T state)
|
||||
{
|
||||
// Handle is closed to don't bother scheduling anything
|
||||
if (_post.IsClosed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var work = new Work
|
||||
{
|
||||
CallbackAdapter = CallbackAdapter<T>.PostCallbackAdapter,
|
||||
Callback = callback,
|
||||
//TODO: This boxes
|
||||
// TODO: This boxes
|
||||
State = state
|
||||
};
|
||||
|
||||
|
|
@ -189,7 +195,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
|||
{
|
||||
_workAdding.Enqueue(work);
|
||||
}
|
||||
_post.Send();
|
||||
|
||||
try
|
||||
{
|
||||
_post.Send();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// There's an inherent race here where we're in the middle of shutdown
|
||||
}
|
||||
}
|
||||
|
||||
private void Post(Action<LibuvThread> callback)
|
||||
|
|
@ -199,6 +213,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
|||
|
||||
public Task PostAsync<T>(Action<T> callback, T state)
|
||||
{
|
||||
// Handle is closed to don't bother scheduling anything
|
||||
if (_post.IsClosed)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var work = new Work
|
||||
{
|
||||
|
|
@ -212,7 +232,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
|||
{
|
||||
_workAdding.Enqueue(work);
|
||||
}
|
||||
_post.Send();
|
||||
|
||||
try
|
||||
{
|
||||
_post.Send();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// There's an inherent race here where we're in the middle of shutdown
|
||||
}
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests
|
||||
{
|
||||
public class LibuvThreadTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task LibuvThreadDoesNotThrowIfPostingWorkAfterDispose()
|
||||
{
|
||||
var mockConnectionHandler = new MockConnectionHandler();
|
||||
var mockLibuv = new MockLibuv();
|
||||
var transportContext = new TestLibuvTransportContext() { ConnectionHandler = mockConnectionHandler };
|
||||
var transport = new LibuvTransport(mockLibuv, transportContext, null);
|
||||
var thread = new LibuvThread(transport);
|
||||
var ranOne = false;
|
||||
var ranTwo = false;
|
||||
var ranThree = false;
|
||||
var ranFour = false;
|
||||
|
||||
await thread.StartAsync();
|
||||
|
||||
await thread.PostAsync<object>(_ =>
|
||||
{
|
||||
ranOne = true;
|
||||
},
|
||||
null);
|
||||
|
||||
Assert.Equal(1, mockLibuv.PostCount);
|
||||
|
||||
// Shutdown the libuv thread
|
||||
await thread.StopAsync(TimeSpan.FromSeconds(5));
|
||||
|
||||
Assert.Equal(2, mockLibuv.PostCount);
|
||||
|
||||
var task = thread.PostAsync<object>(_ =>
|
||||
{
|
||||
ranTwo = true;
|
||||
},
|
||||
null);
|
||||
|
||||
Assert.Equal(2, mockLibuv.PostCount);
|
||||
|
||||
thread.Post<object>(_ =>
|
||||
{
|
||||
ranThree = true;
|
||||
},
|
||||
null);
|
||||
|
||||
Assert.Equal(2, mockLibuv.PostCount);
|
||||
|
||||
thread.Schedule(_ =>
|
||||
{
|
||||
ranFour = true;
|
||||
},
|
||||
(object)null);
|
||||
|
||||
Assert.Equal(2, mockLibuv.PostCount);
|
||||
|
||||
Assert.True(task.IsCompleted);
|
||||
Assert.True(ranOne);
|
||||
Assert.False(ranTwo);
|
||||
Assert.False(ranThree);
|
||||
Assert.False(ranFour);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue