A task is sent from the server to the client, but it arrives to the client as null. The client accepts the first few tasks and they are not null, they are normally transmitted. But at some point it is null again. Here is the link to the repository with the full project: https://github.com/DarkByte2015/CoursedWork_v5 . I would be very grateful if someone can help. Minimally reproducible example:
namespace CWServer.ViewModels { public class MainViewModel : ViewModelBase { private JobService ServiceInstance; private ServiceHost ServiceHost; private Queue<ClientJob> Jobs = new Queue<ClientJob>(); private int JobCount; private List<double> Results = new List<double>(); public ObservableCollection<ClientViewModel> Clients { get; } = new ObservableCollection<ClientViewModel>(); public MainViewModel() { StartServer(); SolveAsync(); } private void StartServer() { ServiceInstance = new JobService(); ServiceInstance.ClientConnected += ClientConnected; ServiceInstance.ClientDisconnected += ClientDisconnected; ServiceInstance.ClientCalculated += ClientCalculated; ServiceHost = new ServiceHost(ServiceInstance); ServiceHost.Open(); } private void StopServer() { Clients.AsParallel().ForAll(c => c.DisconnectAsync()); ServiceHost.Close(); Dispatch(() => Clients.Clear()); } private async void SolveAsync() => await Task.Run(() => Solve()); private void Solve() { foreach (var job in ClientJob.Distribute()) Jobs.Enqueue(job); JobCount = Jobs.Count; Clients.AsParallel().ForAll(c => TryGiveJob(c)); while (LockingCall.Invoke(() => Results.Count != JobCount, JobCount)) ; Results.Clear(); } private void ClientConnected(object sender, ClientChangeStateEventArgs e) { var client = new ClientViewModel(e.Callback); client.ClientAborted += ClientAborted; Dispatch(() => Clients.Add(client)); TryGiveJob(client); } private void ClientDisconnected(object sender, ClientChangeStateEventArgs e) { var client = Clients.First(c => c.Callback == e.Callback); RemoveClient(client); } private void ClientAborted(object sender, ClientAbortedEventArgs e) { var client = sender as ClientViewModel; RemoveClient(client); } private void ClientCalculated(object sender, ClientCalculatedEventArgs e) { var client = Clients.First(c => c.Callback == e.Callback); client.EndCalculationAsync(); LockingCall.Invoke(() => Results.Add(1.0), sender); TryGiveJob(client); } private bool TryGiveJob(ClientViewModel client) { if (LockingCall.Invoke(() => Jobs.Count > 0, client)) { if (LockingCall.Invoke(() => Jobs.Peek() == null, client)) Debugger.Launch(); var job = LockingCall.Invoke(() => Jobs.Dequeue(), client); client.BeginCalculationAsync(job); return true; } else return false; } private void RemoveClient(ClientViewModel client) { if (client.IsCalculating) LockingCall.Invoke(() => Jobs.Enqueue(client.Job), JobCount); Dispatch(() => Clients.Remove(client)); } private void Dispatch(Action action) => Application.Current.Dispatcher.Invoke(action); } } namespace CWServer.ViewModels { public class ClientViewModel : ViewModelBase { public event EventHandler<ClientAbortedEventArgs> ClientAborted; public IClientCallback Callback { get; } public ClientJob Job { get; private set; } public bool IsCalculating { get { return Job != null; } } public ClientViewModel(IClientCallback callback) { Callback = callback; } public async void DisconnectAsync() { try { await Task.Run(() => Callback.OnServerIsStopped()); } catch (Exception e) { Debug.WriteLine(e.Message); } } public async void BeginCalculationAsync(ClientJob job) { if (job == null) Debug.WriteLine("BeginCalculationAsync: job == null"); Job = job; try { await Task.Run(() => Callback.OnGiveJob(Job)); } catch (Exception e) { Debug.WriteLine(e.Message); var args = new ClientAbortedEventArgs(e, Job); ClientAborted?.Invoke(this, args); } } public async void EndCalculationAsync() => await Task.Run((Action)EndCalculation); private void EndCalculation() { Job = null; } } } [DataContract] public class ClientJob { public static ClientJob[] Distribute() => Enumerable.Repeat<ClientJob>(null, 20000).Select(j => new ClientJob()).ToArray(); } namespace CWServer.ServiceContracts { [ServiceContract] public interface IClientCallback { [OperationContract(IsOneWay = true)] void OnGiveJob(ClientJob job); [OperationContract(IsOneWay = true)] void OnServerIsStopped(); } } namespace CWServer.ServiceContracts { [ServiceContract(CallbackContract = typeof(IClientCallback))] public interface IJobService { [OperationContract] void Connect(); [OperationContract] void SetResult(ClientJob job); [OperationContract] void Disconnect(); } } namespace CWServer.ServiceContracts { [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant, IncludeExceptionDetailInFaults = true)] public class JobService : IJobService { public event EventHandler<ClientChangeStateEventArgs> ClientConnected; public event EventHandler<ClientChangeStateEventArgs> ClientDisconnected; public event EventHandler<ClientCalculatedEventArgs> ClientCalculated; void IJobService.Connect() { var callback = OperationContext.Current.GetCallbackChannel<IClientCallback>(); var address = GetClientAddress(); var args = new ClientChangeStateEventArgs(callback, address); ClientConnected?.Invoke(this, args); } void IJobService.Disconnect() { var callback = OperationContext.Current.GetCallbackChannel<IClientCallback>(); var address = GetClientAddress(); var args = new ClientChangeStateEventArgs(callback, address); ClientDisconnected?.Invoke(this, args); } void IJobService.SetResult(ClientJob job) { var callback = OperationContext.Current.GetCallbackChannel<IClientCallback>(); var args = new ClientCalculatedEventArgs(callback, job); ClientCalculated?.Invoke(this, args); } private string GetClientAddress() { var context = OperationContext.Current; var prop = context.IncomingMessageProperties; var endpoint = prop[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty; return endpoint.Address; } } } namespace CWClient { class Program : IJobServiceCallback { private JobServiceClient client; public async void OnGiveJob(ClientJob job) { Debug.Assert(job != null); await Task.Delay(1000); await client.SetResultAsync(job); } public void OnServerIsStopped() { Console.WriteLine("Stopped."); } static void Main(string[] args) { var program = new Program(); var context = new InstanceContext(program); program.client = new JobServiceClient(context); // ΡΡΡΠΎΠΊΡ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ ΠΎΡΡΠ°Π²ΠΈΠΌ Π² ΠΊΠΎΠ½ΡΠΈΠ³-ΡΠ°ΠΉΠ»Π΅ program.client.Connect(); Console.ReadLine(); } } }