[Bug]: ConcurrencyMode does not work in Client side in Duplex Service
- 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
Assessment
This issue has not been assessed yet.