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)
|
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
|
var work = new Work
|
||||||
{
|
{
|
||||||
CallbackAdapter = CallbackAdapter<T>.PostCallbackAdapter,
|
CallbackAdapter = CallbackAdapter<T>.PostCallbackAdapter,
|
||||||
Callback = callback,
|
Callback = callback,
|
||||||
//TODO: This boxes
|
// TODO: This boxes
|
||||||
State = state
|
State = state
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -189,7 +195,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
{
|
{
|
||||||
_workAdding.Enqueue(work);
|
_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)
|
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)
|
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 tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
var work = new Work
|
var work = new Work
|
||||||
{
|
{
|
||||||
|
|
@ -212,7 +232,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal
|
||||||
{
|
{
|
||||||
_workAdding.Enqueue(work);
|
_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;
|
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