CoreWCF / CoreWCF/CoreWCF

[Bug]: ConcurrencyMode does not work in Client side in Duplex Service

Open
#1,633 5 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C#
Stars
1.8k
Forks
320
PR merge metrics
No merged PRs in 30d

Description

### Duplicate ?

- [x] I have searched issues/discussions and did not find other issues/discussions reporting this bug.

### Product version

CoreWCF 1.6.0

### Describe expected behavior

I have a wpf application which I just converted to .net8 and where I used corewcf in a service replacing the service host and in client side I used used duplex channel. I used [CallbackBehavior(IncludeExceptionDetailInFaults = true, ConcurrencyMode = ConcurrencyMode.Single, UseSynchronizationContext = false)] in Client side and
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)] in server side. I could access the interface method concurrently but it is giving me Deadlock exception in .net 8.

In .netframework, it works fine. If I remove the CallbackBehavior attribute in .netframework I am getting the same error as mentioned in the actual behavior section.

### Describe actual behavior

Error:
This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on CallbackBehaviorAttribute.

### Which binding

NetNamedPipe

### security

None

### Which .NET version

.NET 8

### Which os platform

Windows

### Code snippet used to reproduce the issue

```c#
Implementation:

Service Interface:

public interface IMaintenanceServiceCallback
{
[OperationContract ]
void OnConnectionStatusChangedEvent(ServerStatusChangedEventArgs args);
}
[ServiceContract(CallbackContract = typeof(IMaintenanceServiceCallback))]
public interface IMaintenanceService : IDisposable
{
[OperationContract]
Task DoWork(IPaddress[] ips);
}

Server :

[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
internal class MaintenanceService : IMaintenanceService, IDisposable, IAnalyticsHandler
{
public WebApplication CreateHost()
{
try
{

var options = new WebApplicationOptions
{
ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? Directory.GetCurrentDirectory():default
};
var builder = WebApplication.CreateBuilder(options);
builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelMetadata();

builder.Services.AddSingleton(logger =>
{
return new LoggerService();
});
builder.Services.AddSingleton(storageService=>{

return new StorageService(new LoggerService(), false);
});
builder.Services.AddSingleton(winServiceHelper=>
{
return new WindowsServiceHelper(new LoggerService());
}
);
builder.Services.AddSingleton();
builder.Services.AddSingleton();
// Add Windows Service support
builder.WebHost.UseNetNamedPipe(options =>
{
options.Listen(new Uri(MaintenanceSettings.ADDRESS));
});

var app = builder.Build();
app.UseServiceModel(serviceBuilder =>
{
serviceBuilder.AddService();
serviceBuilder.AddServiceEndpoint(typeof(IMaintenanceService),
MaintenanceSettings.BINDING, MaintenanceSettings.ADDRESS);

LoggerService.Log(string.Format($"App Running: {app}"));
return app;
}
catch (Exception ex)
{
LoggerService.LogException(ex);
return null;
}
}
}

Client side:

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]

public class MaintenanceService : IMaintenanceServiceCallback
{
protected async Task GetChannel()
{
await _semaphore.WaitAsync();
try
{
if (_channel == null)
{
var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) { ReceiveTimeout = TimeSpan.MaxValue, CloseTimeout = TimeSpan.MaxValue, SendTimeout = TimeSpan.MaxValue };
var address = new EndpointAddress(MaintenanceSettings.ADDRESS);
var factory = new DuplexChannelFactory(typeof(IMaintenanceServiceCallback), binding, address);
var context = new InstanceContext(this);
_channel = factory.CreateChannel(context);

var co = _channel as ICommunicationObject;
co.Closed += OnClosed;
co.Faulted += OnFaulted;
#if DEBUG
await _channel.Start(AppConfiguration.IsProd, true, IpcQueueName);

#else
await _channel.Start(AppConfiguration.IsProd, false, IpcQueueName);
#endif
}
return _channel;
}
finally
{
_semaphore.Release();
}
}

public async Task AddWork(IPaddress[] ips)
{
LoggerService.Log("DoWork---------");
try
{
// Task.Factory.StartNew(() => );
var channel = await GetChannel();

return await channel.DoWork(appPaths); //Error while accessing the method.
}
catch (Exception ex)
{
LoggerService.LogException(ex);
}
return false;
}

}
```

### Stacktrace if any

System.InvalidOperationException: 'This operation would deadlock because the reply cannot be received until the current Message completes processing.
If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on CallbackBehaviorAttribute.'
at System.ServiceModel.Channels.ServiceChannel.PrepareCall(ProxyOperationRuntime operation, Boolean oneway, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.Begin()
at System.ServiceModel.Channels.ServiceChannel.BeginCall(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, TimeSpan timeout, AsyncCallback callback, Object asyncState)
at System.ServiceModel.Channels.ServiceChannel.BeginCall(ServiceChannel channel, ProxyOperationRuntime operation, Object[] ins, AsyncCallback callback, Object asyncState)
at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.CreateGenericTask(ServiceChannel channel, ProxyOperationRuntime operation, Object[] inputParameters)
--- End of stack trace from previous location ---

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.