/// <summary> /// Post the given action in an async manner to the given SynchronizationContext. /// </summary> /// <param name="syncContext">The target SynchronizationContext.</param> /// <param name="postAction">The action to be posted.</param> /// <param name="actionIfNull">What should we do if we get no SyncContext?</param> public static Task PostAlsoIfNullAsync(this SynchronizationContext syncContext, Action postAction, ActionIfSyncContextIsNull actionIfNull) { TaskCompletionSource <object> completionSource = new TaskCompletionSource <object>(); syncContext.PostAlsoIfNull(() => { try { postAction(); completionSource.SetResult(null); } catch (Exception ex) { completionSource.SetException(ex); } }, actionIfNull); return(completionSource.Task); }
/// <summary> /// Sends the given message to all subscribers (synchonous processing). /// </summary> /// <typeparam name="MessageType">Type of the message.</typeparam> /// <param name="message">The message to send.</param> /// <param name="isInitialCall">Is this one the initial call to publish? (false if we are coming from async routing)</param> private void PublishInternal <MessageType>( MessageType message, bool isInitialCall) where MessageType : SeeingSharpMessage { message.EnsureNotNull(nameof(message)); try { // Check whether publich is possible if (m_checkBehavior == SeeingSharpMessageThreadingBehavior.EnsureMainSyncContextOnSyncCalls) { if (!CompareSynchronizationContexts()) { throw new SeeingSharpException( "Unable to perform a synchronous publish call because current " + "SynchronizationContext is set wrong. This indicates that the call " + "comes from a wrong thread!"); } } // Notify all subscribed targets Type currentType = typeof(MessageType); // Check for correct message sources if (isInitialCall) { string[] possibleSources = s_messageSources.GetOrAdd(currentType, (inputType) => message.GetPossibleSourceThreads()); if (possibleSources.Length > 0) { string mainThreadName = m_messengerName; if (string.IsNullOrEmpty(mainThreadName) || (Array.IndexOf(possibleSources, mainThreadName) < 0)) { throw new InvalidOperationException(string.Format( "The message of type {0} can only be sent by the threads {1}. This Messenger belongs to the thread {2}, so no publish possible!", currentType.FullName, possibleSources.ToCommaSeparatedString(), !string.IsNullOrEmpty(mainThreadName) ? mainThreadName as string : "(empty)")); } } } // Perform synchronous message handling List <MessageSubscription> subscriptionsToTrigger = new List <MessageSubscription>(20); lock (m_messageSubscriptionsLock) { if (m_messageSubscriptions.ContainsKey(currentType)) { // Need to copy the list to avoid problems, when the list is changed during the loop and cross thread accesses subscriptionsToTrigger = new List <MessageSubscription>(m_messageSubscriptions[currentType]); } } // Trigger all found subscriptions List <Exception> occurredExceptions = null; for (int loop = 0; loop < subscriptionsToTrigger.Count; loop++) { try { subscriptionsToTrigger[loop].Publish(message); } catch (Exception ex) { if (occurredExceptions == null) { occurredExceptions = new List <Exception>(); } occurredExceptions.Add(ex); } } // Perform further message routing if enabled if (isInitialCall) { // Get information about message routing string[] asyncTargets = s_messagesAsyncTargets.GetOrAdd(currentType, (inputType) => message.GetAsyncRoutingTargetThreads()); string mainThreadName = m_messengerName; for (int loop = 0; loop < asyncTargets.Length; loop++) { string actAsyncTargetName = asyncTargets[loop]; if (mainThreadName == actAsyncTargetName) { continue; } SeeingSharpMessenger actAsyncTargetHandler = null; if (s_messengersByName.TryGetValue(actAsyncTargetName, out actAsyncTargetHandler)) { SynchronizationContext actSyncContext = actAsyncTargetHandler.m_syncContext; if (actSyncContext == null) { continue; } SeeingSharpMessenger innerHandlerForAsyncCall = actAsyncTargetHandler; actSyncContext.PostAlsoIfNull(() => { innerHandlerForAsyncCall.PublishInternal(message, false); }); } } } // Notify all exceptions occurred during publish progress if (isInitialCall) { if ((occurredExceptions != null) && (occurredExceptions.Count > 0)) { throw new MessagePublishException(typeof(MessageType), occurredExceptions); } } } catch (Exception ex) { // Check whether we have to throw the exception globally var globalExceptionHandler = SeeingSharpMessenger.CustomPublishExceptionHandler; bool doRaise = true; if (globalExceptionHandler != null) { try { doRaise = !globalExceptionHandler(this, ex); } catch { doRaise = true; } } // Raise the exception to inform caller about it if (doRaise) { throw; } } }
/// <summary> /// Sends the given message to all subscribers (asynchonous processing). /// There is no possibility here to wait for the answer. /// </summary> /// <typeparam name="MessageType">The type of the essage type.</typeparam> /// <param name="message">The message.</param> public void BeginPublish <MessageType>( MessageType message) where MessageType : SeeingSharpMessage { m_syncContext.PostAlsoIfNull(() => Publish(message)); }