public Tuple <DuplexCallbackId, IDisposable> RegisterObservableCallbacks(int clientId, Action <object> onNext, Action <Exception> onError, Action onCompleted, Action <int> dispose) { var serverId = Interlocked.Increment(ref lastObservableId); var id = new DuplexCallbackId(clientId, serverId); var actions = Tuple.Create(onNext, onError, onCompleted, dispose); if (!observableCallbacks.TryAdd(id, actions)) { throw new InvalidOperationException(Errors.ProtocolDuplicateDuplexIdForObservable); } return(Tuple.Create( id, Disposable.Create(() => { lock (actions) { int?clientSubscriptionId = null; if (TryGetOrAddSubscriptionOneTime(id, ref clientSubscriptionId)) { Contract.Assume(clientSubscriptionId.HasValue); // Disposable.Create ensures that this code only runs once actions.Item4(clientSubscriptionId.Value); } } }))); }
protected void Subscribe(DuplexCallbackId id) { Func <int, IDisposable> subscribe; if (!obsevableCallbacks.TryGetValue(id.ClientId, out subscribe)) { throw new InvalidOperationException(Errors.ProtocolInvalidDuplexId); } var subscription = new SingleAssignmentDisposable(); var subscriptionId = RegisterSubscription(subscription); try { subscription.Disposable = subscribe(id.ServerId); } catch (Exception ex) { SendOnError(id, ex); return; } SendSubscribeResponse(id, subscriptionId); }
protected void MoveNext(DuplexCallbackId id) { var enumerator = GetEnumerator(id.ClientId); object current; bool result; try { if (enumerator != null && enumerator.MoveNext()) { result = true; current = enumerator.Current; } else { result = false; current = null; } } catch (Exception ex) { SendEnumeratorError(id, ex); return; } SendEnumeratorResponse(id, result, current); }
private Tuple <Action <object>, Action <Exception> > GetEnumeratorCallbacks(DuplexCallbackId id) { Tuple <Action <object>, Action <Exception> > actions; if (!enumeratorCallbacks.TryGetValue(id, out actions)) { throw new InvalidOperationException(Errors.ProtocolInvalidDuplexIdForEnumerator); } return(actions); }
public DuplexCallbackId RegisterEnumeratorCallback(int clientId, Action <object> callback, Action <Exception> onError) { var serverId = Interlocked.Increment(ref lastEnumeratorId); var id = new DuplexCallbackId(clientId, serverId); if (!enumeratorCallbacks.TryAdd(id, Tuple.Create(callback, onError))) { throw new InvalidOperationException(Errors.ProtocolDuplicateDuplexIdForEnumerator); } return(id); }
protected void ResetEnumerator(DuplexCallbackId id) { var enumerator = GetEnumerator(id.ClientId); try { enumerator.Reset(); } catch (Exception ex) { SendEnumeratorError(id, ex); } }
private static byte[] Serialize(DuplexCallbackId id, object value, QbservableProtocol protocol) { var idData = BitConverter.GetBytes(id); long serializedDataLength; var serializedData = protocol.Serialize(value, out serializedDataLength); var data = new byte[idData.Length + serializedDataLength]; Array.Copy(idData, data, idData.Length); Array.Copy(serializedData, 0, data, idData.Length, serializedDataLength); return(data); }
private void TryInvokeObservableCallback( DuplexCallbackId id, Action <Tuple <Action <object>, Action <Exception>, Action, Action <int> > > action) { Tuple <Action <object>, Action <Exception>, Action, Action <int> > callbacks; if (observableCallbacks.TryGetValue(id, out callbacks)) { action(callbacks); } /* It's acceptable for the callbacks to be missing due to a race condition between the * client sending notifications and the server disposing of the subscription, which causes * the callbacks to be removed. */ }
private bool TryGetOrAddSubscriptionOneTime(DuplexCallbackId id, ref int?clientSubscriptionId) { int?s; if (subscriptions.TryGetValue(id, out s)) { subscriptions.Remove(id); clientSubscriptionId = s; return(true); } else { subscriptions.Add(id, clientSubscriptionId); } return(false); }
protected void GetEnumerator(DuplexCallbackId id) { var enumerable = GetEnumerable(id.ClientId); IEnumerator enumerator; try { enumerator = enumerable(); } catch (Exception ex) { SendGetEnumeratorError(id, ex); return; } var enumeratorId = RegisterEnumerator(enumerator); SendGetEnumeratorResponse(id, enumeratorId); }
protected void HandleSubscribeResponse(DuplexCallbackId id, int clientSubscriptionId) { Tuple <Action <object>, Action <Exception>, Action, Action <int> > actions; if (!observableCallbacks.TryGetValue(id, out actions)) { throw new InvalidOperationException(Errors.ProtocolInvalidDuplexIdForObservable); } lock (actions) { int?inputOnly = clientSubscriptionId; if (TryGetOrAddSubscriptionOneTime(id, ref inputOnly)) { subscriptions.Remove(id); Tuple <Action <object>, Action <Exception>, Action, Action <int> > ignored; observableCallbacks.TryRemove(id, out ignored); actions.Item4(clientSubscriptionId); } } }
protected void Invoke(DuplexCallbackId id, object[] arguments) { Func <object[], object> callback; if (!invokeCallbacks.TryGetValue(id.ClientId, out callback)) { throw new InvalidOperationException(Errors.ProtocolInvalidDuplexId); } object result; try { result = callback(arguments); } catch (Exception ex) { SendError(id, ex); return; } SendResponse(id, result); }
protected override void SendEnumeratorResponse(DuplexCallbackId id, bool result, object current) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateEnumeratorResponse(id, result, current, protocol)); }
public override void SendOnError(DuplexCallbackId id, Exception error) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateOnError(id, error, protocol)); }
protected abstract void SendEnumeratorResponse(DuplexCallbackId id, bool result, object current);
public abstract void SendOnCompleted(DuplexCallbackId id);
protected abstract void SendSubscribeResponse(DuplexCallbackId id, int clientSubscriptionId);
public abstract void SendOnNext(DuplexCallbackId id, object value);
protected override void SendGetEnumeratorError(DuplexCallbackId id, Exception error) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateGetEnumeratorError(id, error, protocol)); }
protected override void SendGetEnumeratorResponse(DuplexCallbackId id, int clientEnumeratorId) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateGetEnumeratorResponse(id, clientEnumeratorId, protocol)); }
public override void SendOnCompleted(DuplexCallbackId id) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateOnCompleted(id, protocol)); }
private static IDisposable Subscribe(IClientDuplexQbservableProtocolSink sink, DuplexCallbackId id, object instance, Type dataType) { return(dataType.UpCast(instance).Subscribe( value => sink.SendOnNext(id, value), ex => sink.SendOnError(id, ex), () => sink.SendOnCompleted(id))); }
public abstract void SendOnError(DuplexCallbackId id, Exception error);
protected override void SendResponse(DuplexCallbackId id, object result) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateResponse(id, result, protocol)); }
protected abstract void SendResponse(DuplexCallbackId id, object result);
protected override void SendError(DuplexCallbackId id, Exception error) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateErrorResponse(id, error, protocol)); }
protected abstract void SendGetEnumeratorResponse(DuplexCallbackId id, int clientEnumeratorId);
protected override void SendSubscribeResponse(DuplexCallbackId id, int clientSubscriptionId) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateSubscribeResponse(id, clientSubscriptionId, protocol)); }
protected abstract void SendEnumeratorError(DuplexCallbackId id, Exception error);
public override void SendOnNext(DuplexCallbackId id, object value) { protocol.SendDuplexMessageAsync(DuplexQbservableMessage.CreateOnNext(id, value, protocol)); }