Drain requests in native instead of managed for IIS In-process (#6816)

This commit is contained in:
Justin Kotalik 2019-01-28 15:20:28 -08:00 committed by GitHub
parent 9f71e60283
commit 7a98a04c53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 173 additions and 89 deletions

View File

@ -7,6 +7,7 @@
#include "iapplication.h"
#include "ntassert.h"
#include "SRWExclusiveLock.h"
#include "SRWSharedLock.h"
#include "exceptions.h"
class APPLICATION : public IAPPLICATION
@ -21,14 +22,17 @@ public:
_In_ IHttpContext *pHttpContext,
_Outptr_result_maybenull_ IREQUEST_HANDLER **pRequestHandler) override
{
TraceContextScope traceScope(pHttpContext->GetTraceContext());
*pRequestHandler = nullptr;
SRWSharedLock stopLock(m_stateLock);
if (m_fStopCalled)
{
return S_FALSE;
}
TraceContextScope traceScope(pHttpContext->GetTraceContext());
return CreateHandler(pHttpContext, pRequestHandler);
}

View File

@ -26,7 +26,8 @@ IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
m_Initialized(false),
m_blockManagedCallbacks(true),
m_waitForShutdown(true),
m_pConfig(std::move(pConfig))
m_pConfig(std::move(pConfig)),
m_requestCount(0)
{
DBG_ASSERT(m_pConfig);
@ -67,6 +68,13 @@ IN_PROCESS_APPLICATION::StopClr()
{
shutdownHandler(m_ShutdownHandlerContext);
}
auto requestCount = m_requestCount.load();
if (requestCount == 0)
{
LOG_INFO(L"Drained all requests, notifying managed.");
m_RequestsDrainedHandler(m_ShutdownHandlerContext);
}
}
// Signal shutdown
@ -90,6 +98,7 @@ IN_PROCESS_APPLICATION::SetCallbackHandles(
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
_In_ PFN_DISCONNECT_HANDLER disconnect_callback,
_In_ PFN_ASYNC_COMPLETION_HANDLER async_completion_handler,
_In_ PFN_REQUESTS_DRAINED_HANDLER requestsDrainedHandler,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
)
@ -102,6 +111,7 @@ IN_PROCESS_APPLICATION::SetCallbackHandles(
m_ShutdownHandler = shutdown_handler;
m_ShutdownHandlerContext = pvShutdownHandlerContext;
m_AsyncCompletionHandler = async_completion_handler;
m_RequestsDrainedHandler = requestsDrainedHandler;
m_blockManagedCallbacks = false;
m_Initialized = true;
@ -131,6 +141,12 @@ IN_PROCESS_APPLICATION::LoadManagedApplication()
FALSE, // not set
nullptr)); // name
THROW_LAST_ERROR_IF_NULL(m_pRequestDrainEvent = CreateEvent(
nullptr, // default security attributes
TRUE, // manual reset event
FALSE, // not set
nullptr)); // name
LOG_INFO(L"Waiting for initialization");
m_workerThread = std::thread([](std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> application)
@ -167,7 +183,6 @@ IN_PROCESS_APPLICATION::LoadManagedApplication()
return S_OK;
}
void
IN_PROCESS_APPLICATION::ExecuteApplication()
{
@ -259,7 +274,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
if (m_waitForShutdown)
{
const auto clrWaitResult = WaitForSingleObject(m_clrThread.native_handle(), m_pConfig->QueryShutdownTimeLimitInMS());
THROW_LAST_ERROR_IF(waitResult == WAIT_FAILED);
THROW_LAST_ERROR_IF(clrWaitResult == WAIT_FAILED);
clrThreadExited = clrWaitResult != WAIT_TIMEOUT;
}
@ -517,9 +532,23 @@ IN_PROCESS_APPLICATION::CreateHandler(
{
try
{
DBG_ASSERT(!m_fStopCalled);
m_requestCount++;
*pRequestHandler = new IN_PROCESS_HANDLER(::ReferenceApplication(this), pHttpContext, m_RequestHandler, m_RequestHandlerContext, m_DisconnectHandler, m_AsyncCompletionHandler);
}
CATCH_RETURN();
return S_OK;
}
void
IN_PROCESS_APPLICATION::HandleRequestCompletion()
{
SRWSharedLock lock(m_stateLock);
auto requestCount = m_requestCount--;
if (m_fStopCalled && requestCount == 0)
{
LOG_INFO(L"Drained all requests, notifying managed.");
m_RequestsDrainedHandler(m_ShutdownHandlerContext);
}
}

View File

@ -13,6 +13,7 @@ typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_REQUEST_HANDLER) (IN_PROCESS_HA
typedef VOID(WINAPI * PFN_DISCONNECT_HANDLER) (void *pvManagedHttpContext);
typedef BOOL(WINAPI * PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_ASYNC_COMPLETION_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
typedef void(WINAPI * PFN_REQUESTS_DRAINED_HANDLER) (void* pvShutdownHandlerContext);
class IN_PROCESS_APPLICATION : public InProcessApplicationBase
{
@ -36,6 +37,7 @@ public:
_In_ PFN_SHUTDOWN_HANDLER shutdown_callback,
_In_ PFN_DISCONNECT_HANDLER disconnect_callback,
_In_ PFN_ASYNC_COMPLETION_HANDLER managed_context_callback,
_In_ PFN_REQUESTS_DRAINED_HANDLER requestsDrainedHandler,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
);
@ -47,6 +49,8 @@ public:
_Out_ IREQUEST_HANDLER **pRequestHandler)
override;
void HandleRequestCompletion();
// Executes the .NET Core process
void
ExecuteApplication();
@ -62,6 +66,8 @@ public:
StopIncomingRequests()
{
QueueStop();
LOG_INFOF(L"Waiting for %d requests to drain", m_requestCount.load());
}
void
@ -144,6 +150,8 @@ private:
// The event that gets triggered when worker thread should exit
HandleWrapper<NullHandleTraits> m_pShutdownEvent;
HandleWrapper<NullHandleTraits> m_pRequestDrainEvent;
// The request handler callback from managed code
PFN_REQUEST_HANDLER m_RequestHandler;
VOID* m_RequestHandlerContext;
@ -154,12 +162,14 @@ private:
PFN_ASYNC_COMPLETION_HANDLER m_AsyncCompletionHandler;
PFN_DISCONNECT_HANDLER m_DisconnectHandler;
PFN_REQUESTS_DRAINED_HANDLER m_RequestsDrainedHandler;
std::wstring m_dotnetExeKnownLocation;
std::atomic_bool m_blockManagedCallbacks;
bool m_Initialized;
bool m_waitForShutdown;
std::atomic<int> m_requestCount;
std::unique_ptr<InProcessOptions> m_pConfig;

View File

@ -58,7 +58,6 @@ IN_PROCESS_HANDLER::AsyncCompletion(
HRESULT hrCompletionStatus
)
{
::RaiseEvent<ANCMEvents::ANCM_INPROC_ASYNC_COMPLETION_START>(m_pW3Context, nullptr);
if (m_fManagedRequestComplete)

View File

@ -21,7 +21,10 @@ public:
_In_ PFN_DISCONNECT_HANDLER m_DisconnectHandler,
_In_ PFN_ASYNC_COMPLETION_HANDLER pAsyncCompletion);
~IN_PROCESS_HANDLER() override = default;
~IN_PROCESS_HANDLER()
{
m_pApplication->HandleRequestCompletion();
}
__override
REQUEST_NOTIFICATION_STATUS

View File

@ -18,6 +18,7 @@ register_callbacks(
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
_In_ PFN_DISCONNECT_HANDLER disconnect_handler,
_In_ PFN_ASYNC_COMPLETION_HANDLER async_completion_handler,
_In_ PFN_REQUESTS_DRAINED_HANDLER requestsDrainedHandler,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
)
@ -32,6 +33,7 @@ register_callbacks(
shutdown_handler,
disconnect_handler,
async_completion_handler,
requestsDrainedHandler,
pvRequstHandlerContext,
pvShutdownHandlerContext
);

View File

@ -563,7 +563,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
// Post completion after completing the request to resume the state machine
PostCompletion(ConvertRequestCompletionResults(successfulRequest));
Server.DecrementRequests();
// Dispose the context
Dispose();

View File

@ -1,3 +1,5 @@
// 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.Buffers;
@ -23,6 +25,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
private static readonly NativeMethods.PFN_SHUTDOWN_HANDLER _shutdownHandler = HandleShutdown;
private static readonly NativeMethods.PFN_DISCONNECT_HANDLER _onDisconnect = OnDisconnect;
private static readonly NativeMethods.PFN_ASYNC_COMPLETION _onAsyncCompletion = OnAsyncCompletion;
private static readonly NativeMethods.PFN_REQUESTS_DRAINED_HANDLER _requestsDrainedHandler = OnRequestsDrained;
private IISContextFactory _iisContextFactory;
private readonly MemoryPool<byte> _memoryPool = new SlabMemoryPool();
@ -33,11 +36,9 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
private readonly IISNativeApplication _nativeApplication;
private readonly ServerAddressesFeature _serverAddressesFeature;
private volatile int _stopping;
private bool Stopping => _stopping == 1;
private int _outstandingRequests;
private readonly TaskCompletionSource<object> _shutdownSignal = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private bool? _websocketAvailable;
private CancellationTokenRegistration _cancellationTokenRegistration;
public IFeatureCollection Features { get; } = new FeatureCollection();
@ -86,7 +87,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
_httpServerHandle = GCHandle.Alloc(this);
_iisContextFactory = new IISContextFactory<TContext>(_memoryPool, application, _options, this, _logger);
_nativeApplication.RegisterCallbacks(_requestHandler, _shutdownHandler, _onDisconnect, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle);
_nativeApplication.RegisterCallbacks(_requestHandler, _shutdownHandler, _onDisconnect, _onAsyncCompletion, _requestsDrainedHandler, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle);
_serverAddressesFeature.Addresses = _options.ServerAddresses;
@ -95,50 +96,18 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
public Task StopAsync(CancellationToken cancellationToken)
{
void RegisterCancelation()
{
cancellationToken.Register(() =>
{
_nativeApplication.StopCallsIntoManaged();
_shutdownSignal.TrySetResult(null);
});
}
if (Interlocked.Exchange(ref _stopping, 1) == 1)
{
RegisterCancelation();
return _shutdownSignal.Task;
}
// First call back into native saying "DON'T SEND ME ANY MORE REQUESTS"
_nativeApplication.StopIncomingRequests();
try
_cancellationTokenRegistration = cancellationToken.Register((shutdownSignal) =>
{
// Wait for active requests to drain
if (_outstandingRequests > 0)
{
RegisterCancelation();
}
else
{
// We have drained all requests. Block any callbacks into managed at this point.
_nativeApplication.StopCallsIntoManaged();
_shutdownSignal.TrySetResult(null);
}
}
catch (Exception ex)
{
_shutdownSignal.TrySetException(ex);
}
((TaskCompletionSource<object>)shutdownSignal).TrySetResult(null);
},
_shutdownSignal);
return _shutdownSignal.Task;
}
public void Dispose()
{
_stopping = 1;
// Block any more calls into managed from native as we are unloading.
_nativeApplication.StopCallsIntoManaged();
_shutdownSignal.TrySetResult(null);
@ -152,21 +121,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
_nativeApplication.Dispose();
}
private void IncrementRequests()
{
Interlocked.Increment(ref _outstandingRequests);
}
internal void DecrementRequests()
{
if (Interlocked.Decrement(ref _outstandingRequests) == 0 && Stopping)
{
// All requests have been drained.
_nativeApplication.StopCallsIntoManaged();
_shutdownSignal.TrySetResult(null);
}
}
private static NativeMethods.REQUEST_NOTIFICATION_STATUS HandleRequest(IntPtr pInProcessHandler, IntPtr pvRequestContext)
{
IISHttpServer server = null;
@ -174,7 +128,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
{
// Unwrap the server so we can create an http context and process the request
server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
server.IncrementRequests();
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
@ -205,7 +158,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
return true;
}
private static void OnDisconnect(IntPtr pvManagedHttpContext)
{
IISHttpContext context = null;
@ -237,6 +189,23 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
}
}
private static void OnRequestsDrained(IntPtr serverContext)
{
IISHttpServer server = null;
try
{
server = (IISHttpServer)GCHandle.FromIntPtr(serverContext).Target;
server._nativeApplication.StopCallsIntoManaged();
server._shutdownSignal.TrySetResult(null);
server._cancellationTokenRegistration.Dispose();
}
catch (Exception ex)
{
server?._logger.LogError(0, ex, $"Unexpected exception in {nameof(IISHttpServer)}.{nameof(OnRequestsDrained)}.");
}
}
private class IISContextFactory<T> : IISContextFactory
{
private readonly IHttpApplication<T> _application;

View File

@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
NativeMethods.PFN_SHUTDOWN_HANDLER shutdownHandler,
NativeMethods.PFN_DISCONNECT_HANDLER disconnectHandler,
NativeMethods.PFN_ASYNC_COMPLETION onAsyncCompletion,
NativeMethods.PFN_REQUESTS_DRAINED_HANDLER requestDrainedHandler,
IntPtr requestContext,
IntPtr shutdownContext)
{
@ -39,6 +40,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
shutdownHandler,
disconnectHandler,
onAsyncCompletion,
requestDrainedHandler,
requestContext,
shutdownContext);
}

View File

@ -44,6 +44,7 @@ namespace Microsoft.AspNetCore.Server.IIS
public delegate bool PFN_SHUTDOWN_HANDLER(IntPtr pvRequestContext);
public delegate REQUEST_NOTIFICATION_STATUS PFN_ASYNC_COMPLETION(IntPtr pvManagedHttpContext, int hr, int bytes);
public delegate REQUEST_NOTIFICATION_STATUS PFN_WEBSOCKET_ASYNC_COMPLETION(IntPtr pInProcessHandler, IntPtr completionInfo, IntPtr pvCompletionContext);
public delegate void PFN_REQUESTS_DRAINED_HANDLER(IntPtr pvRequestContext);
[DllImport(AspNetCoreModuleDll)]
private static extern int http_post_completion(IntPtr pInProcessHandler, int cbBytes);
@ -60,6 +61,7 @@ namespace Microsoft.AspNetCore.Server.IIS
PFN_SHUTDOWN_HANDLER shutdownCallback,
PFN_DISCONNECT_HANDLER disconnectCallback,
PFN_ASYNC_COMPLETION asyncCallback,
PFN_REQUESTS_DRAINED_HANDLER requestsDrainedHandler,
IntPtr pvRequestContext,
IntPtr pvShutdownContext);
@ -160,10 +162,11 @@ namespace Microsoft.AspNetCore.Server.IIS
PFN_SHUTDOWN_HANDLER shutdownCallback,
PFN_DISCONNECT_HANDLER disconnectCallback,
PFN_ASYNC_COMPLETION asyncCallback,
PFN_REQUESTS_DRAINED_HANDLER requestsDrainedHandler,
IntPtr pvRequestContext,
IntPtr pvShutdownContext)
{
Validate(register_callbacks(pInProcessApplication, requestCallback, shutdownCallback, disconnectCallback, asyncCallback, pvRequestContext, pvShutdownContext));
Validate(register_callbacks(pInProcessApplication, requestCallback, shutdownCallback, disconnectCallback, asyncCallback, requestsDrainedHandler, pvRequestContext, pvShutdownContext));
}
public static unsafe int HttpWriteResponseBytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected)

View File

@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
await deploymentResult.AssertRecycledAsync(() => AssertAppOffline(deploymentResult));
}
[ConditionalFact(Skip = "https://github.com/aspnet/AspNetCore/issues/6555")]
[ConditionalFact]
[RequiresIIS(IISCapability.ShutdownToken)]
public async Task AppOfflineDroppedWhileSiteStarting_SiteShutsDown_InProcess()
{

View File

@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
await _fixture.Client.RetryRequestAsync("/WaitingRequestCount", async message => await message.Content.ReadAsStringAsync() == "0");
}
[ConditionalFact(Skip = "https://github.com/aspnet/AspNetCore/issues/4512")]
[ConditionalFact]
public async Task ClientDisconnectCallbackStress()
{
// Fixture initialization fails if inside of the Task.Run, so send an

View File

@ -0,0 +1,64 @@
// 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.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(PublishedSitesCollection.Name)]
public class ShutdownTests : IISFunctionalTestBase
{
private readonly PublishedSitesFixture _fixture;
public ShutdownTests(PublishedSitesFixture fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task ShutdownTimeoutIsApplied()
{
var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.InProcessTestSite, publish: true);
deploymentParameters.TransformArguments((a, _) => $"{a} HangOnStop");
deploymentParameters.WebConfigActionList.Add(
WebConfigHelpers.AddOrModifyAspNetCoreSection("shutdownTimeLimit", "1"));
var deploymentResult = await DeployAsync(deploymentParameters);
Assert.Equal("Hello World", await deploymentResult.HttpClient.GetStringAsync("/HelloWorld"));
StopServer();
EventLogHelpers.VerifyEventLogEvents(deploymentResult,
EventLogHelpers.InProcessStarted(deploymentResult),
EventLogHelpers.InProcessFailedToStop(deploymentResult, ""));
}
[ConditionalTheory]
[InlineData("/ShutdownStopAsync")]
[InlineData("/ShutdownStopAsyncWithCancelledToken")]
public async Task CallStopAsyncOnRequestThread_DoesNotHangIndefinitely(string path)
{
// Canceled token doesn't affect shutdown, in-proc doesn't handle ungraceful shutdown
// IIS's ShutdownTimeLimit will handle that.
var parameters = _fixture.GetBaseDeploymentParameters(publish: true);
var deploymentResult = await DeployAsync(parameters);
try
{
await deploymentResult.HttpClient.GetAsync(path);
}
catch (HttpRequestException ex) when (ex.InnerException is IOException)
{
// Server might close a connection before request completes
}
deploymentResult.AssertWorkerProcessStop();
}
}
}

View File

@ -337,25 +337,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
);
}
[ConditionalFact]
public async Task ShutdownTimeoutIsApplied()
{
var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.InProcessTestSite, publish: true);
deploymentParameters.TransformArguments((a, _) => $"{a} HangOnStop");
deploymentParameters.WebConfigActionList.Add(
WebConfigHelpers.AddOrModifyAspNetCoreSection("shutdownTimeLimit", "1"));
var deploymentResult = await DeployAsync(deploymentParameters);
Assert.Equal("Hello World", await deploymentResult.HttpClient.GetStringAsync("/HelloWorld"));
StopServer();
EventLogHelpers.VerifyEventLogEvents(deploymentResult,
EventLogHelpers.InProcessStarted(deploymentResult),
EventLogHelpers.InProcessFailedToStop(deploymentResult, ""));
}
[ConditionalFact]
public async Task CheckInvalidHostingModelParameter()
{

View File

@ -8,17 +8,18 @@ using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(PublishedSitesCollection.Name)]
public class ShutdownTests : IISFunctionalTestBase
public class IISExpressShutdownTests : IISFunctionalTestBase
{
private readonly PublishedSitesFixture _fixture;
public ShutdownTests(PublishedSitesFixture fixture)
public IISExpressShutdownTests(PublishedSitesFixture fixture)
{
_fixture = fixture;
}
@ -41,7 +42,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
}
[ConditionalFact(Skip = "https://github.com/aspnet/AspNetCore/issues/6605")]
[ConditionalFact]
public async Task ServerShutsDownWhenMainExitsStress()
{
var parameters = _fixture.GetBaseDeploymentParameters(publish: true);

View File

@ -6,10 +6,12 @@ using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.IISIntegration.FunctionalTests;
@ -656,6 +658,22 @@ namespace TestSite
ctx.RequestServices.GetService<IApplicationLifetime>().StopApplication();
}
private async Task ShutdownStopAsync(HttpContext ctx)
{
await ctx.Response.WriteAsync("Shutting down");
var server = ctx.RequestServices.GetService<IServer>();
await server.StopAsync(default);
}
private async Task ShutdownStopAsyncWithCancelledToken(HttpContext ctx)
{
await ctx.Response.WriteAsync("Shutting down");
var server = ctx.RequestServices.GetService<IServer>();
var cts = new CancellationTokenSource();
cts.Cancel();
await server.StopAsync(cts.Token);
}
private async Task GetServerVariableStress(HttpContext ctx)
{
// This test simulates the scenario where native Flush call is being