Browse Source

Add Start/FromAsync overloads enabling post-subscribe exceptions to be swallowed

Added tests verifying that the old behaviour continues to be the default, and that the new overloads provide the new behaviour when asked for it. (The tests for the latter fail right now because we've not actually changed the implementation yet. Also, the Qbservable API parity tests also fail because we've not yet updated the homoiconicity support to match the new surface area.)
Ian Griffiths 2 years ago
parent
commit
0797c31d7f

+ 5 - 0
.gitignore

@@ -189,3 +189,8 @@ nuget.exe
 
 # JetBrains Rider adds these
 .idea/
+
+# Local NCrunch settings
+*.v3.ncrunchproject
+*.v3.ncrunchsolution
+/Rx.NET/Source/.NCrunch_*/StoredText

+ 360 - 0
Rx.NET/Source/src/System.Reactive/Linq/Observable.Async.cs

@@ -1037,6 +1037,29 @@ namespace System.Reactive.Linq
         /// </list>
         /// </remarks>
         public static IObservable<TResult> StartAsync<TResult>(Func<Task<TResult>> functionAsync)
+        {
+            return StartAsync(functionAsync, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Invokes the asynchronous function, surfacing the result through an observable sequence.
+        /// </summary>
+        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
+        /// <param name="functionAsync">Asynchronous function to run.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing the function's result value, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null.</exception>
+        /// <remarks>
+        /// <list type="bullet">
+        /// <item><description>The function is started immediately, not during the subscription of the resulting sequence.</description></item>
+        /// <item><description>Multiple subscriptions to the resulting sequence can observe the function's result.</description></item>
+        /// </list>
+        /// </remarks>
+        public static IObservable<TResult> StartAsync<TResult>(Func<Task<TResult>> functionAsync, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (functionAsync == null)
             {
@@ -1061,6 +1084,30 @@ namespace System.Reactive.Linq
         /// </list>
         /// </remarks>
         public static IObservable<TResult> StartAsync<TResult>(Func<Task<TResult>> functionAsync, IScheduler scheduler)
+        {
+            return StartAsync(functionAsync, scheduler, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Invokes the asynchronous function, surfacing the result through an observable sequence.
+        /// </summary>
+        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
+        /// <param name="functionAsync">Asynchronous function to run.</param>
+        /// <param name="scheduler">Scheduler on which to notify observers.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing the function's result value, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
+        /// <remarks>
+        /// <list type="bullet">
+        /// <item><description>The function is started immediately, not during the subscription of the resulting sequence.</description></item>
+        /// <item><description>Multiple subscriptions to the resulting sequence can observe the function's result.</description></item>
+        /// </list>
+        /// </remarks>
+        public static IObservable<TResult> StartAsync<TResult>(Func<Task<TResult>> functionAsync, IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (functionAsync == null)
             {
@@ -1097,6 +1144,37 @@ namespace System.Reactive.Linq
         /// </list>
         /// </remarks>
         public static IObservable<TResult> StartAsync<TResult>(Func<CancellationToken, Task<TResult>> functionAsync)
+        {
+            return StartAsync(functionAsync, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Invokes the asynchronous function, surfacing the result through an observable sequence.
+        /// The CancellationToken is shared by all subscriptions on the resulting observable sequence. See the remarks section for more information.
+        /// </summary>
+        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
+        /// <param name="functionAsync">Asynchronous function to run.</param>
+        /// <returns>An observable sequence exposing the function's result value, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null.</exception>
+        /// <remarks>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <list type="bullet">
+        /// <item><description>The function is started immediately, not during the subscription of the resulting sequence.</description></item>
+        /// <item><description>Multiple subscriptions to the resulting sequence can observe the function's result.</description></item>
+        /// <item><description>
+        /// If any subscription to the resulting sequence is disposed, the CancellationToken is set. The observer associated to the disposed
+        /// subscription won't see the TaskCanceledException, but other observers will. You can protect against this using the Catch operator.
+        /// Be careful when handing out the resulting sequence because of this behavior. The most common use is to have a single subscription
+        /// to the resulting sequence, which controls the CancellationToken state. Alternatively, you can control subscription behavior using
+        /// multicast operators.
+        /// </description></item>
+        /// </list>
+        /// </remarks>
+        public static IObservable<TResult> StartAsync<TResult>(Func<CancellationToken, Task<TResult>> functionAsync, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (functionAsync == null)
             {
@@ -1129,6 +1207,38 @@ namespace System.Reactive.Linq
         /// </list>
         /// </remarks>
         public static IObservable<TResult> StartAsync<TResult>(Func<CancellationToken, Task<TResult>> functionAsync, IScheduler scheduler)
+        {
+            return StartAsync(functionAsync, scheduler, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Invokes the asynchronous function, surfacing the result through an observable sequence.
+        /// The CancellationToken is shared by all subscriptions on the resulting observable sequence. See the remarks section for more information.
+        /// </summary>
+        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
+        /// <param name="functionAsync">Asynchronous function to run.</param>
+        /// <param name="scheduler">Scheduler on which to notify observers.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing the function's result value, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
+        /// <remarks>
+        /// <list type="bullet">
+        /// <item><description>The function is started immediately, not during the subscription of the resulting sequence.</description></item>
+        /// <item><description>Multiple subscriptions to the resulting sequence can observe the function's result.</description></item>
+        /// <item><description>
+        /// If any subscription to the resulting sequence is disposed, the CancellationToken is set. The observer associated to the disposed
+        /// subscription won't see the TaskCanceledException, but other observers will. You can protect against this using the Catch operator.
+        /// Be careful when handing out the resulting sequence because of this behavior. The most common use is to have a single subscription
+        /// to the resulting sequence, which controls the CancellationToken state. Alternatively, you can control subscription behavior using
+        /// multicast operators.
+        /// </description></item>
+        /// </list>
+        /// </remarks>
+        public static IObservable<TResult> StartAsync<TResult>(Func<CancellationToken, Task<TResult>> functionAsync, IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (functionAsync == null)
             {
@@ -1210,6 +1320,28 @@ namespace System.Reactive.Linq
         /// </list>
         /// </remarks>
         public static IObservable<Unit> StartAsync(Func<Task> actionAsync)
+        {
+            return StartAsync(actionAsync, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Invokes the asynchronous action, surfacing the result through an observable sequence.
+        /// </summary>
+        /// <param name="actionAsync">Asynchronous action to run.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null.</exception>
+        /// <remarks>
+        /// <list type="bullet">
+        /// <item><description>The action is started immediately, not during the subscription of the resulting sequence.</description></item>
+        /// <item><description>Multiple subscriptions to the resulting sequence can observe the action's outcome.</description></item>
+        /// </list>
+        /// </remarks>
+        public static IObservable<Unit> StartAsync(Func<Task> actionAsync, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (actionAsync == null)
             {
@@ -1233,6 +1365,29 @@ namespace System.Reactive.Linq
         /// </list>
         /// </remarks>
         public static IObservable<Unit> StartAsync(Func<Task> actionAsync, IScheduler scheduler)
+        {
+            return StartAsync(actionAsync, scheduler, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Invokes the asynchronous action, surfacing the result through an observable sequence.
+        /// </summary>
+        /// <param name="actionAsync">Asynchronous action to run.</param>
+        /// <param name="scheduler">Scheduler on which to notify observers.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
+        /// <remarks>
+        /// <list type="bullet">
+        /// <item><description>The action is started immediately, not during the subscription of the resulting sequence.</description></item>
+        /// <item><description>Multiple subscriptions to the resulting sequence can observe the action's outcome.</description></item>
+        /// </list>
+        /// </remarks>
+        public static IObservable<Unit> StartAsync(Func<Task> actionAsync, IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (actionAsync == null)
             {
@@ -1268,6 +1423,36 @@ namespace System.Reactive.Linq
         /// </list>
         /// </remarks>
         public static IObservable<Unit> StartAsync(Func<CancellationToken, Task> actionAsync)
+        {
+            return StartAsync(actionAsync, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Invokes the asynchronous action, surfacing the result through an observable sequence.
+        /// The CancellationToken is shared by all subscriptions on the resulting observable sequence. See the remarks section for more information.
+        /// </summary>
+        /// <param name="actionAsync">Asynchronous action to run.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null.</exception>
+        /// <remarks>
+        /// <list type="bullet">
+        /// <item><description>The action is started immediately, not during the subscription of the resulting sequence.</description></item>
+        /// <item><description>Multiple subscriptions to the resulting sequence can observe the action's outcome.</description></item>
+        /// <item><description>
+        /// If any subscription to the resulting sequence is disposed, the CancellationToken is set. The observer associated to the disposed
+        /// subscription won't see the TaskCanceledException, but other observers will. You can protect against this using the Catch operator.
+        /// Be careful when handing out the resulting sequence because of this behavior. The most common use is to have a single subscription
+        /// to the resulting sequence, which controls the CancellationToken state. Alternatively, you can control subscription behavior using
+        /// multicast operators.
+        /// </description></item>
+        /// </list>
+        /// </remarks>
+        public static IObservable<Unit> StartAsync(Func<CancellationToken, Task> actionAsync, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (actionAsync == null)
             {
@@ -1299,6 +1484,37 @@ namespace System.Reactive.Linq
         /// </list>
         /// </remarks>
         public static IObservable<Unit> StartAsync(Func<CancellationToken, Task> actionAsync, IScheduler scheduler)
+        {
+            return StartAsync(actionAsync, scheduler, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Invokes the asynchronous action, surfacing the result through an observable sequence.
+        /// The CancellationToken is shared by all subscriptions on the resulting observable sequence. See the remarks section for more information.
+        /// </summary>
+        /// <param name="actionAsync">Asynchronous action to run.</param>
+        /// <param name="scheduler">Scheduler on which to notify observers.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
+        /// <remarks>
+        /// <list type="bullet">
+        /// <item><description>The action is started immediately, not during the subscription of the resulting sequence.</description></item>
+        /// <item><description>Multiple subscriptions to the resulting sequence can observe the action's outcome.</description></item>
+        /// <item><description>
+        /// If any subscription to the resulting sequence is disposed, the CancellationToken is set. The observer associated to the disposed
+        /// subscription won't see the TaskCanceledException, but other observers will. You can protect against this using the Catch operator.
+        /// Be careful when handing out the resulting sequence because of this behavior. The most common use is to have a single subscription
+        /// to the resulting sequence, which controls the CancellationToken state. Alternatively, you can control subscription behavior using
+        /// multicast operators.
+        /// </description></item>
+        /// </list>
+        /// </remarks>
+        public static IObservable<Unit> StartAsync(Func<CancellationToken, Task> actionAsync, IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (actionAsync == null)
             {
@@ -1330,6 +1546,23 @@ namespace System.Reactive.Linq
         /// <returns>An observable sequence exposing the result of invoking the function, or an exception.</returns>
         /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null.</exception>
         public static IObservable<TResult> FromAsync<TResult>(Func<Task<TResult>> functionAsync)
+        {
+            return FromAsync(functionAsync, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Converts an asynchronous function into an observable sequence. Each subscription to the resulting sequence causes the function to be started.
+        /// </summary>
+        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
+        /// <param name="functionAsync">Asynchronous function to convert.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing the result of invoking the function, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null.</exception>
+        public static IObservable<TResult> FromAsync<TResult>(Func<Task<TResult>> functionAsync, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (functionAsync == null)
             {
@@ -1348,6 +1581,24 @@ namespace System.Reactive.Linq
         /// <returns>An observable sequence exposing the result of invoking the function, or an exception.</returns>
         /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
         public static IObservable<TResult> FromAsync<TResult>(Func<Task<TResult>> functionAsync, IScheduler scheduler)
+        {
+            return FromAsync(functionAsync, scheduler, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Converts an asynchronous function into an observable sequence. Each subscription to the resulting sequence causes the function to be started.
+        /// </summary>
+        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
+        /// <param name="functionAsync">Asynchronous function to convert.</param>
+        /// <param name="scheduler">Scheduler on which to notify observers.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing the result of invoking the function, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
+        public static IObservable<TResult> FromAsync<TResult>(Func<Task<TResult>> functionAsync, IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (functionAsync == null)
             {
@@ -1372,6 +1623,25 @@ namespace System.Reactive.Linq
         /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null.</exception>
         /// <remarks>When a subscription to the resulting sequence is disposed, the CancellationToken that was fed to the asynchronous function will be signaled.</remarks>
         public static IObservable<TResult> FromAsync<TResult>(Func<CancellationToken, Task<TResult>> functionAsync)
+        {
+            return FromAsync(functionAsync, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Converts an asynchronous function into an observable sequence. Each subscription to the resulting sequence causes the function to be started.
+        /// The CancellationToken passed to the asynchronous function is tied to the observable sequence's subscription that triggered the function's invocation and can be used for best-effort cancellation.
+        /// </summary>
+        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
+        /// <param name="functionAsync">Asynchronous function to convert.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing the result of invoking the function, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null.</exception>
+        /// <remarks>When a subscription to the resulting sequence is disposed, the CancellationToken that was fed to the asynchronous function will be signaled.</remarks>
+        public static IObservable<TResult> FromAsync<TResult>(Func<CancellationToken, Task<TResult>> functionAsync, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (functionAsync == null)
             {
@@ -1392,6 +1662,26 @@ namespace System.Reactive.Linq
         /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
         /// <remarks>When a subscription to the resulting sequence is disposed, the CancellationToken that was fed to the asynchronous function will be signaled.</remarks>
         public static IObservable<TResult> FromAsync<TResult>(Func<CancellationToken, Task<TResult>> functionAsync, IScheduler scheduler)
+        {
+            return FromAsync(functionAsync, scheduler, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Converts an asynchronous function into an observable sequence. Each subscription to the resulting sequence causes the function to be started.
+        /// The CancellationToken passed to the asynchronous function is tied to the observable sequence's subscription that triggered the function's invocation and can be used for best-effort cancellation.
+        /// </summary>
+        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
+        /// <param name="functionAsync">Asynchronous function to convert.</param>
+        /// <param name="scheduler">Scheduler on which to notify observers.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing the result of invoking the function, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
+        /// <remarks>When a subscription to the resulting sequence is disposed, the CancellationToken that was fed to the asynchronous function will be signaled.</remarks>
+        public static IObservable<TResult> FromAsync<TResult>(Func<CancellationToken, Task<TResult>> functionAsync, IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (functionAsync == null)
             {
@@ -1417,6 +1707,22 @@ namespace System.Reactive.Linq
         /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
         /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null.</exception>
         public static IObservable<Unit> FromAsync(Func<Task> actionAsync)
+        {
+            return FromAsync(actionAsync, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Converts an asynchronous action into an observable sequence. Each subscription to the resulting sequence causes the action to be started.
+        /// </summary>
+        /// <param name="actionAsync">Asynchronous action to convert.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null.</exception>
+        public static IObservable<Unit> FromAsync(Func<Task> actionAsync, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (actionAsync == null)
             {
@@ -1434,6 +1740,23 @@ namespace System.Reactive.Linq
         /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
         /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
         public static IObservable<Unit> FromAsync(Func<Task> actionAsync, IScheduler scheduler)
+        {
+            return FromAsync(actionAsync, scheduler, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Converts an asynchronous action into an observable sequence. Each subscription to the resulting sequence causes the action to be started.
+        /// </summary>
+        /// <param name="actionAsync">Asynchronous action to convert.</param>
+        /// <param name="scheduler">Scheduler on which to notify observers.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
+        public static IObservable<Unit> FromAsync(Func<Task> actionAsync, IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (actionAsync == null)
             {
@@ -1457,6 +1780,24 @@ namespace System.Reactive.Linq
         /// <remarks>When a subscription to the resulting sequence is disposed, the CancellationToken that was fed to the asynchronous function will be signaled.</remarks>
         /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null.</exception>
         public static IObservable<Unit> FromAsync(Func<CancellationToken, Task> actionAsync)
+        {
+            return FromAsync(actionAsync, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Converts an asynchronous action into an observable sequence. Each subscription to the resulting sequence causes the action to be started.
+        /// The CancellationToken passed to the asynchronous action is tied to the observable sequence's subscription that triggered the action's invocation and can be used for best-effort cancellation.
+        /// </summary>
+        /// <param name="actionAsync">Asynchronous action to convert.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
+        /// <remarks>When a subscription to the resulting sequence is disposed, the CancellationToken that was fed to the asynchronous function will be signaled.</remarks>
+        /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null.</exception>
+        public static IObservable<Unit> FromAsync(Func<CancellationToken, Task> actionAsync, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (actionAsync == null)
             {
@@ -1476,6 +1817,25 @@ namespace System.Reactive.Linq
         /// <remarks>When a subscription to the resulting sequence is disposed, the CancellationToken that was fed to the asynchronous function will be signaled.</remarks>
         /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
         public static IObservable<Unit> FromAsync(Func<CancellationToken, Task> actionAsync, IScheduler scheduler)
+        {
+            return FromAsync(actionAsync, scheduler, ignoreExceptionsAfterUnsubscribe: false);
+        }
+
+        /// <summary>
+        /// Converts an asynchronous action into an observable sequence. Each subscription to the resulting sequence causes the action to be started.
+        /// The CancellationToken passed to the asynchronous action is tied to the observable sequence's subscription that triggered the action's invocation and can be used for best-effort cancellation.
+        /// </summary>
+        /// <param name="actionAsync">Asynchronous action to convert.</param>
+        /// <param name="scheduler">Scheduler on which to notify observers.</param>
+        /// <param name="ignoreExceptionsAfterUnsubscribe">
+        /// If true, exceptions that occur after cancellation has been initiated by unsubscribing from the observable
+        /// this method returns will be handled and silently ignored. If false, they will go unobserved, meaning they
+        /// will eventually emerge through <see cref="TaskScheduler.UnobservedTaskException"/>.
+        /// </param>
+        /// <returns>An observable sequence exposing a Unit value upon completion of the action, or an exception.</returns>
+        /// <remarks>When a subscription to the resulting sequence is disposed, the CancellationToken that was fed to the asynchronous function will be signaled.</remarks>
+        /// <exception cref="ArgumentNullException"><paramref name="actionAsync"/> is null or <paramref name="scheduler"/> is null.</exception>
+        public static IObservable<Unit> FromAsync(Func<CancellationToken, Task> actionAsync, IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe)
         {
             if (actionAsync == null)
             {

+ 8 - 0
Rx.NET/Source/tests/Tests.System.Reactive.ApiApprovals/Api/ApiApprovalTests.Core.verified.cs

@@ -1049,11 +1049,15 @@ namespace System.Reactive.Linq
         public static System.IObservable<System.Reactive.Unit> FromAsync(System.Func<System.Threading.Tasks.Task> actionAsync) { }
         public static System.IObservable<System.Reactive.Unit> FromAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> actionAsync) { }
         public static System.IObservable<System.Reactive.Unit> FromAsync(System.Func<System.Threading.Tasks.Task> actionAsync, System.Reactive.Concurrency.IScheduler scheduler) { }
+        public static System.IObservable<System.Reactive.Unit> FromAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> actionAsync, bool ignoreExceptionsAfterUnsubscribe) { }
         public static System.IObservable<System.Reactive.Unit> FromAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> actionAsync, System.Reactive.Concurrency.IScheduler scheduler) { }
+        public static System.IObservable<System.Reactive.Unit> FromAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> actionAsync, System.Reactive.Concurrency.IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe) { }
         public static System.IObservable<TResult> FromAsync<TResult>(System.Func<System.Threading.Tasks.Task<TResult>> functionAsync) { }
         public static System.IObservable<TResult> FromAsync<TResult>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<TResult>> functionAsync) { }
         public static System.IObservable<TResult> FromAsync<TResult>(System.Func<System.Threading.Tasks.Task<TResult>> functionAsync, System.Reactive.Concurrency.IScheduler scheduler) { }
+        public static System.IObservable<TResult> FromAsync<TResult>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<TResult>> functionAsync, bool ignoreExceptionsAfterUnsubscribe) { }
         public static System.IObservable<TResult> FromAsync<TResult>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<TResult>> functionAsync, System.Reactive.Concurrency.IScheduler scheduler) { }
+        public static System.IObservable<TResult> FromAsync<TResult>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<TResult>> functionAsync, System.Reactive.Concurrency.IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe) { }
         [System.Obsolete(@"This conversion is no longer supported. Replace use of the Begin/End asynchronous method pair with a new Task-based async method, and convert the result using ToObservable. If no Task-based async method is available, use Task.Factory.FromAsync to obtain a Task object.")]
         public static System.Func<System.IObservable<System.Reactive.Unit>> FromAsyncPattern(System.Func<System.AsyncCallback, object?, System.IAsyncResult> begin, System.Action<System.IAsyncResult> end) { }
         [System.Obsolete(@"This conversion is no longer supported. Replace use of the Begin/End asynchronous method pair with a new Task-based async method, and convert the result using ToObservable. If no Task-based async method is available, use Task.Factory.FromAsync to obtain a Task object.")]
@@ -1379,11 +1383,15 @@ namespace System.Reactive.Linq
         public static System.IObservable<System.Reactive.Unit> StartAsync(System.Func<System.Threading.Tasks.Task> actionAsync) { }
         public static System.IObservable<System.Reactive.Unit> StartAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> actionAsync) { }
         public static System.IObservable<System.Reactive.Unit> StartAsync(System.Func<System.Threading.Tasks.Task> actionAsync, System.Reactive.Concurrency.IScheduler scheduler) { }
+        public static System.IObservable<System.Reactive.Unit> StartAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> actionAsync, bool ignoreExceptionsAfterUnsubscribe) { }
         public static System.IObservable<System.Reactive.Unit> StartAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> actionAsync, System.Reactive.Concurrency.IScheduler scheduler) { }
+        public static System.IObservable<System.Reactive.Unit> StartAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> actionAsync, System.Reactive.Concurrency.IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe) { }
         public static System.IObservable<TResult> StartAsync<TResult>(System.Func<System.Threading.Tasks.Task<TResult>> functionAsync) { }
         public static System.IObservable<TResult> StartAsync<TResult>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<TResult>> functionAsync) { }
         public static System.IObservable<TResult> StartAsync<TResult>(System.Func<System.Threading.Tasks.Task<TResult>> functionAsync, System.Reactive.Concurrency.IScheduler scheduler) { }
+        public static System.IObservable<TResult> StartAsync<TResult>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<TResult>> functionAsync, bool ignoreExceptionsAfterUnsubscribe) { }
         public static System.IObservable<TResult> StartAsync<TResult>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<TResult>> functionAsync, System.Reactive.Concurrency.IScheduler scheduler) { }
+        public static System.IObservable<TResult> StartAsync<TResult>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<TResult>> functionAsync, System.Reactive.Concurrency.IScheduler scheduler, bool ignoreExceptionsAfterUnsubscribe) { }
         public static System.IObservable<TSource> StartWith<TSource>(this System.IObservable<TSource> source, System.Collections.Generic.IEnumerable<TSource> values) { }
         public static System.IObservable<TSource> StartWith<TSource>(this System.IObservable<TSource> source, params TSource[] values) { }
         public static System.IObservable<TSource> StartWith<TSource>(this System.IObservable<TSource> source, System.Reactive.Concurrency.IScheduler scheduler, System.Collections.Generic.IEnumerable<TSource> values) { }

+ 162 - 0
Rx.NET/Source/tests/Tests.System.Reactive/TaskErrorObservation.cs

@@ -0,0 +1,162 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT License.
+// See the LICENSE file in the project root for more information. 
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Xunit;
+
+namespace Tests.System.Reactive
+{
+    /// <summary>
+    /// Verifies behavior around unobserved exceptions from tasks.
+    /// </summary>
+    /// <remarks>
+    /// Testing whether unhandled exceptions emerge from <see cref="TaskScheduler.UnobservedTaskException"/> is not
+    /// entirely straightforward. A few tests need to do this because we have some historical behaviour described in
+    /// https://github.com/dotnet/reactive/issues/1256 that needs to be preserved for backwards compatibility, along
+    /// with some new functionality enabling optional different behavior regarding unobserved exceptions. This provides
+    /// common mechanism to enable such testing.
+    /// </remarks>
+    internal class TaskErrorObservation : IDisposable
+    {
+        private ManualResetEventSlim _exceptionReportedAsUnobserved;
+        private WeakReference<Task> _taskWeakReference;
+
+        public TaskErrorObservation()
+        {
+            _exceptionReportedAsUnobserved = new(false);
+            TaskScheduler.UnobservedTaskException += HandleUnobservedException;
+        }
+
+        public Exception Exception { get; } = new();
+
+        public void Dispose()
+        {
+            if (_exceptionReportedAsUnobserved is not null)
+            {
+                _exceptionReportedAsUnobserved.Dispose();
+                _exceptionReportedAsUnobserved = null;
+                TaskScheduler.UnobservedTaskException -= HandleUnobservedException;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public IDisposable SuscribeWithoutKeepingSourceReachable(
+            Func<Func<Task, Task>, Exception, IDisposable> subscribe)
+        {
+            // We provide nested function because the temporary storage location that
+            // holds the value returned by a call to, say, Observable.StartAsync can end up keeping it reachable
+            // for GC purposes, which in turn keeps the task reachable. That stops the
+            // finalization-driven unobserved exception detection from working.
+            // By calling Subscribe in a method whose stack frame is then immediately torn
+            // down, we ensure that we don't hang onto anything other than the IDisposable
+            // it returns.
+
+            return subscribe(
+                t =>
+                {
+                    _taskWeakReference = new(t);
+                    return t;
+                },
+                this.Exception);
+        }
+
+        public IDisposable SuscribeWithoutKeepingSourceReachable<T>(
+            Func<Func<Task<T>, Task<T>>, Exception, IDisposable> subscribe)
+        {
+            return SuscribeWithoutKeepingSourceReachable(
+                (Func<Task, Task> setTask, Exception ex) => subscribe(
+                    t =>
+                    {
+                        setTask(t);
+                        return t;
+                    }, ex));
+        }
+
+
+        public void AssertExceptionReportedAsUnobserved()
+        {
+            int start = Environment.TickCount;
+            bool firstIteration = true;
+            while (!_exceptionReportedAsUnobserved.Wait(TimeSpan.FromSeconds(firstIteration ? 0 : 0.001)) &&
+                ((Environment.TickCount - start) < 5000))
+            {
+                firstIteration = false;
+                GC.Collect();
+                GC.WaitForPendingFinalizers();
+            }
+
+            Assert.True(_exceptionReportedAsUnobserved.Wait(TimeSpan.FromSeconds(0.01)));
+        }
+
+        /// <summary>
+        /// Waits for the task to become unreachable, and then verifies that this did not result in
+        /// <see cref="TaskScheduler.UnobservedTaskException"/> reporting the failure.
+        /// </summary>
+        /// <exception cref="InvalidOperationException"></exception>
+        public void AssertExceptionNotReportedAsUnobserved()
+        {
+            if (_taskWeakReference is null)
+            {
+                throw new InvalidOperationException("Test did not supply task to " + nameof(TaskErrorObservation));
+            }
+
+            int start = Environment.TickCount;
+            bool firstIteration = true;
+            do
+            {
+                // We try to get away without sleeping, to enable tests to run as quickly as
+                // possible, but if the object remains reachable after the initial attempt to
+                // force a GC and then immediately run finalizers, there's probably some deferred
+                // work waiting to happen somewhere, so we are better off backing off and giving
+                // that a chance to run.
+                if (firstIteration)
+                {
+                    firstIteration = false;
+                }
+                else
+                {
+                    Thread.Sleep(1);
+                }
+                GC.Collect();
+                GC.WaitForPendingFinalizers();
+            } while (IsTaskStillReachable() &&
+                     ((Environment.TickCount - start) < 5000));
+
+
+            // The task is now unreachable, but it's possible that this happened in between our
+            // last call to GC.WaitForPendingFinalizers and our test for reachability, in which
+            // case it might still be awaiting finalization, so we need one more of these to ensure
+            // it gets flushed through:
+            GC.WaitForPendingFinalizers();
+
+            Assert.False(_exceptionReportedAsUnobserved.IsSet);
+        }
+
+        // This needs to be done in a separate method to ensure that when the weak reference returns a task, we
+        // immediately destroy the stack frame containing the temporary variable into which it was returned,
+        // to avoid keeping the task reachable by accident.
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private bool IsTaskStillReachable()
+        {
+            return _taskWeakReference.TryGetTarget(out _);
+        }
+
+
+        private void HandleUnobservedException(object sender, UnobservedTaskExceptionEventArgs e)
+        {
+            if (e.Exception.InnerException == this.Exception)
+            {
+                e.SetObserved();
+                _exceptionReportedAsUnobserved.Set();
+            }
+        }
+    }
+}

+ 1 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/ArgumentValidationTest.cs

@@ -67,6 +67,7 @@ namespace ReactiveTests.Tests
                 { "Object", new object() },
                 { "Exception", new Exception() },
                 { "String", "String" },
+                { "Boolean", false },
 
                 { "IDictionary`2[Int32, IObservable`1[Int32]]", new Dictionary<int, IObservable<int>>() },
 

+ 235 - 4
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/FromAsyncTest.cs

@@ -12,6 +12,8 @@ using System.Threading.Tasks;
 using Microsoft.Reactive.Testing;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
+using Tests.System.Reactive;
+
 using Assert = Xunit.Assert;
 
 namespace ReactiveTests.Tests
@@ -37,11 +39,16 @@ namespace ReactiveTests.Tests
 
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<Task<int>>)));
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task<int>>), true));
 
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<Task<int>>), s));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(() => _doneTask, default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<Task<int>>), s, true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(() => _doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(() => _doneTask, default(IScheduler), true));
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task<int>>), s));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(ct => _doneTask, default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task<int>>), s, true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(ct => _doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(ct => _doneTask, default(IScheduler), true));
         }
 
         [TestMethod]
@@ -168,6 +175,94 @@ namespace ReactiveTests.Tests
             }
         }
 
+        [TestMethod]
+        public void FromAsync_Func_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(createTask),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_FuncWithCancel_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(_ => createTask()),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_Func_WithScheduler_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(createTask, TaskPoolScheduler.Default),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_FuncWithCancel_WithScheduler_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(_ => createTask(), TaskPoolScheduler.Default),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_Func_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(createTask, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_FuncWithCancel_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(_ => createTask(), ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_Func_WithScheduler_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(createTask, TaskPoolScheduler.Default, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_FuncWithCancel_WithScheduler_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(_ => createTask(), TaskPoolScheduler.Default, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
 #if DESKTOPCLR
         [TestMethod]
         public void FromAsync_Func_Scheduler1()
@@ -231,11 +326,15 @@ namespace ReactiveTests.Tests
 
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<Task>)));
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task>), true));
 
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<Task>), s));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(() => (Task)_doneTask, default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<Task>), s, true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(() => (Task)_doneTask, default(IScheduler), true));
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task>), s));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(ct => (Task)_doneTask, default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(default(Func<CancellationToken, Task>), s, true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(ct => (Task)_doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.FromAsync(ct => (Task)_doneTask, default(IScheduler), true));
         }
 
         [TestMethod]
@@ -358,6 +457,94 @@ namespace ReactiveTests.Tests
             }
         }
 
+
+        [TestMethod]
+        public void FromAsync_Action_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(createTask),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_ActionWithCancel_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(_ => createTask()),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_Action_WithScheduler_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(createTask, TaskPoolScheduler.Default),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_ActionWithCancel_WithScheduler_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(_ => createTask(), TaskPoolScheduler.Default),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_Action_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(createTask, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_ActionWithCancel_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(_ => createTask(), ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_Action_WithScheduler_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(createTask, TaskPoolScheduler.Default, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void FromAsync_ActionWithCancel_WithScheduler_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.FromAsync(_ => createTask(), TaskPoolScheduler.Default, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
 #if DESKTOPCLR
         [TestMethod]
         public void FromAsync_Action_Scheduler1()
@@ -406,5 +593,49 @@ namespace ReactiveTests.Tests
 
         #endregion
 
+        private void FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+            Func<Func<Task<int>>, IObservable<int>> createObservable,
+            Action<TaskErrorObservation> testResults)
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core<int>(createObservable, testResults);
+        }
+
+        private void FromAsync_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+            Func<Func<Task>, IObservable<Unit>> createObservable,
+            Action<TaskErrorObservation> testResults)
+        {
+            FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core<Unit>(createObservable, testResults);
+        }
+
+        private void FromAsync_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core<T>(
+            Func<Func<Task<T>>, IObservable<T>> createObservable,
+            Action<TaskErrorObservation> testResults)
+        {
+            using Barrier gate = new(2);
+            using TaskErrorObservation errorObservation = new();
+
+            var sub = errorObservation.SuscribeWithoutKeepingSourceReachable<T>(
+                (setTask, exception) => createObservable(
+                    () => setTask(Task.Factory.StartNew<T>(
+                        () =>
+                        {
+                            // 1: Notify that task execution has begun
+                            gate.SignalAndWait();
+                            // 2: Wait until unsubscribe Dispose has returned
+                            gate.SignalAndWait();
+                            throw exception;
+                        })))
+                    .Subscribe());
+
+            // 1: wait until task execution has begun
+            gate.SignalAndWait();
+
+            sub.Dispose();
+
+            // 2: Notify that unsubscribe Dispose has returned
+            gate.SignalAndWait();
+
+            testResults(errorObservation);
+        }
     }
 }

+ 240 - 4
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/StartAsyncTest.cs

@@ -7,11 +7,15 @@ using System.Linq;
 using System.Reactive;
 using System.Reactive.Concurrency;
 using System.Reactive.Linq;
+using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
 using System.Threading;
 using System.Threading.Tasks;
 using Microsoft.Reactive.Testing;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
+using Tests.System.Reactive;
+
 using Assert = Xunit.Assert;
 
 namespace ReactiveTests.Tests
@@ -37,13 +41,19 @@ namespace ReactiveTests.Tests
             var s = Scheduler.Immediate;
 
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<Task<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<Task<int>>), true));
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<CancellationToken, Task<int>>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<CancellationToken, Task<int>>), true));
 
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<Task<int>>), s));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<Task<int>>), s, true));
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<CancellationToken, Task<int>>), s));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<CancellationToken, Task<int>>), s, true));
 
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(() => _doneTask, default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(ct => _doneTask, default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(() => _doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(() => _doneTask, default(IScheduler), true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(ct => _doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(ct => _doneTask, default(IScheduler), true));
         }
 
         [TestMethod]
@@ -182,6 +192,94 @@ namespace ReactiveTests.Tests
             }
         }
 
+        [TestMethod]
+        public void Start_Func_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(createTask),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_FuncWithCancel_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(_ => createTask()),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_Func_WithScheduler_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(createTask, TaskPoolScheduler.Default),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_FuncWithCancel_WithScheduler_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(_ => createTask(), TaskPoolScheduler.Default),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_Func_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(createTask, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_FuncWithCancel_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(_ => createTask(), ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_Func_WithScheduler_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(createTask, TaskPoolScheduler.Default, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_FuncWithCancel_WithScheduler_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(_ => createTask(), TaskPoolScheduler.Default, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
 #if DESKTOPCLR
         [TestMethod]
         public void StartAsync_Func_Scheduler1()
@@ -244,12 +342,17 @@ namespace ReactiveTests.Tests
             var s = Scheduler.Immediate;
 
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<Task>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<Task>), true));
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<CancellationToken, Task>)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<CancellationToken, Task>), true));
 
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<Task>), s));
             ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<CancellationToken, Task>), s));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(() => (Task)_doneTask, default));
-            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(ct => (Task)_doneTask, default));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(default(Func<CancellationToken, Task>), s, true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(() => (Task)_doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(() => (Task)_doneTask, default(IScheduler), true));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(ct => (Task)_doneTask, default(IScheduler)));
+            ReactiveAssert.Throws<ArgumentNullException>(() => Observable.StartAsync(ct => (Task)_doneTask, default(IScheduler), true));
         }
 
         [TestMethod]
@@ -380,6 +483,94 @@ namespace ReactiveTests.Tests
             }
         }
 
+        [TestMethod]
+        public void Start_Action_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(createTask),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_ActionWithCancel_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(_ => createTask()),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_Action_WithScheduler_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(createTask, TaskPoolScheduler.Default),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_ActionWithCancel_WithScheduler_UnsubscribeThenError_ErrorReportedAsUnobserved()
+        {
+            Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(_ => createTask(), TaskPoolScheduler.Default),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_Action_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(createTask, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_ActionWithCancel_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(_ => createTask(), ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_Action_WithScheduler_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(createTask, TaskPoolScheduler.Default, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
+        [TestMethod]
+        public void Start_ActionWithCancel_WithScheduler_IgnorePostUnsubscribeErrors_UnsubscribeThenError_ErrorNotReportedAsUnobserved()
+        {
+            Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+                createTask => Observable.StartAsync(_ => createTask(), TaskPoolScheduler.Default, ignoreExceptionsAfterUnsubscribe: true),
+                errorObservation =>
+                {
+                    errorObservation.AssertExceptionNotReportedAsUnobserved();
+                });
+        }
+
 #if DESKTOPCLR
         [TestMethod]
         public void StartAsync_Action_Scheduler1()
@@ -428,5 +619,50 @@ namespace ReactiveTests.Tests
 
         #endregion
 
+        private void Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+            Func<Func<Task<int>>, IObservable<int>> createObservable,
+            Action<TaskErrorObservation> testResults)
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core<int>(createObservable, testResults);
+        }
+
+        private void Start_Action_ErrorAfterUnsubscribeReportedAsUnobserved_Core(
+            Func<Func<Task>, IObservable<Unit>> createObservable,
+            Action<TaskErrorObservation> testResults)
+        {
+            Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core<Unit>(createObservable, testResults);
+        }
+
+        private void Start_Func_ErrorAfterUnsubscribeReportedAsUnobserved_Core<T>(
+            Func<Func<Task<T>>, IObservable<T>> createObservable,
+            Action<TaskErrorObservation> testResults)
+        {
+            using Barrier gate = new(2);
+            using TaskErrorObservation errorObservation = new();
+
+            var sub = errorObservation.SuscribeWithoutKeepingSourceReachable<T>(
+                (setTask, exception) => createObservable(
+                    () => setTask(Task.Factory.StartNew<T>(
+                        () =>
+                        {
+                            // 1: Notify that task execution has begun
+                            gate.SignalAndWait();
+                            // 2: Wait until unsubscribe Dispose has returned
+                            gate.SignalAndWait();
+                            throw exception;
+                        })))
+                    .Subscribe());
+
+            // 1: wait until task execution has begun
+            gate.SignalAndWait();
+
+            sub.Dispose();
+            //sub = null;
+
+            // 2: Notify that unsubscribe Dispose has returned
+            gate.SignalAndWait();
+
+            testResults(errorObservation);
+        }
     }
 }

+ 28 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/StartTest.cs

@@ -6,6 +6,8 @@ using System;
 using System.Linq;
 using System.Reactive;
 using System.Reactive.Linq;
+using System.Threading;
+
 using Microsoft.Reactive.Testing;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
@@ -70,6 +72,32 @@ namespace ReactiveTests.Tests
             }));
         }
 
+        [TestMethod]
+        public void Start_ActionErrorAfterUnsubscribeDoesNotThrow()
+        {
+            var ex = new Exception();
+            using Barrier gate = new(2);
+
+            var sub = Observable.Start(
+                () =>
+                {
+                    // 1: action running
+                    gate.SignalAndWait();
+                    // 2: unsubscribe Dispose returned
+                    gate.SignalAndWait();
+                    throw ex;
+                })
+                .Subscribe();
+
+            // 1: action running
+            gate.SignalAndWait();
+
+            sub.Dispose();
+
+            // 2: unsubscribe Dispose returned
+            gate.SignalAndWait();
+        }
+
         [TestMethod]
         public void Start_Func()
         {

+ 13 - 15
Rx.NET/tools/HomoIcon/Program.cs

@@ -153,26 +153,24 @@ namespace HomoIconize
  */
 ");
             WriteLine(
-@"#pragma warning disable 1591
+@"#nullable enable
+#pragma warning disable 1591
 ");
 
             WriteLine(
-@"using System;
-using System.Reactive.Concurrency;
-using System.Collections.Generic;");
+@"using System.Collections.Generic;");
 
             if (exludeFromCodeCoverage)
                 WriteLine("using System.Diagnostics.CodeAnalysis;");
 
             WriteLine(
-@"using System.Reactive.Joins;
-using System.Linq;
+@"using System.Linq;
 using System.Linq.Expressions;
+using System.Reactive.Concurrency;
+using System.Reactive.Subjects;
 using System.Reflection;
 using System.Threading;
 using System.Threading.Tasks;
-using System.Reactive;
-using System.Reactive.Subjects;
 ");
             WriteLine(
 @"namespace System.Reactive.Linq
@@ -425,9 +423,9 @@ using System.Reactive.Subjects;
                     WriteLine("InfoOf(() => " + typeName + "." + name + g + "(" + string.Join(", ", ptps.Select(pt => "default(" + pt + ")")) + "))" + cma);
                     WriteLine("#else", true);
                     if (!m.IsGenericMethod)
-                        WriteLine("(MethodInfo)MethodInfo.GetCurrentMethod()" + cma);
+                        WriteLine("(MethodInfo)MethodInfo.GetCurrentMethod()!" + cma);
                     else
-                        WriteLine("((MethodInfo)MethodInfo.GetCurrentMethod()).MakeGenericMethod(" + string.Join(", ", m.GetGenericArguments().Select(ga => "typeof(" + ga.Name + ")").ToArray()) + ")" + cma);
+                        WriteLine("((MethodInfo)MethodInfo.GetCurrentMethod()!).MakeGenericMethod(" + string.Join(", ", m.GetGenericArguments().Select(ga => "typeof(" + ga.Name + ")").ToArray()) + ")" + cma);
                     WriteLine("#endif", true);
                     for (int j = 0; j < args.Count; j++)
                         WriteLine(args[j] + (j < args.Count - 1 ? "," : ""));
@@ -614,9 +612,9 @@ using System.Reactive.Subjects;
                         WriteLine("var m = InfoOf(() => " + typeName + ".ToAsync" + genArgss + "(" + string.Join(", ", aprs.Select(pt => "default(" + pt + ")")) + "));");
                         WriteLine("#else", true);
                         if (genArgs.Length == 0)
-                            WriteLine("var m = (MethodInfo)MethodInfo.GetCurrentMethod();");
+                            WriteLine("var m = (MethodInfo)MethodInfo.GetCurrentMethod()!;");
                         else
-                            WriteLine("var m = ((MethodInfo)MethodInfo.GetCurrentMethod()).MakeGenericMethod(" + string.Join(", ", genArgs.Select(a => "typeof(" + a + ")").ToArray()) + ");");
+                            WriteLine("var m = ((MethodInfo)MethodInfo.GetCurrentMethod()!).MakeGenericMethod(" + string.Join(", ", genArgs.Select(a => "typeof(" + a + ")").ToArray()) + ");");
                         WriteLine("#endif", true);
 
                         WriteLine("return (" + string.Join(", ", lamPars) + ") => provider.CreateQuery<" + ret + ">(");
@@ -724,7 +722,7 @@ using System.Reactive.Subjects;
                     WriteLine("[Obsolete(Constants_Linq.USE_TASK_FROMASYNCPATTERN)]");
                     WriteLine("#endif", true);
 
-                    WriteLine("public static " + retType + " FromAsyncPattern" + genArgss + "(this IQbservableProvider provider, " + begType + " begin, " + endType + "end)");
+                    WriteLine("public static " + retType + " FromAsyncPattern" + genArgss + "(this IQbservableProvider provider, " + begType + " begin, " + endType + " end)");
                     WriteLine("{");
 
                     Indent();
@@ -751,9 +749,9 @@ using System.Reactive.Subjects;
                     WriteLine("var m = InfoOf(() => " + typeName + ".FromAsyncPattern" + genArgss + "(" + string.Join(", ", aprs.Select(pt => "default(" + pt + ")")) + "));");
                     WriteLine("#else", true);
                     if (genArgs.Length == 0)
-                        WriteLine("var m = (MethodInfo)MethodInfo.GetCurrentMethod();");
+                        WriteLine("var m = (MethodInfo)MethodInfo.GetCurrentMethod()!;");
                     else
-                        WriteLine("var m = ((MethodInfo)MethodInfo.GetCurrentMethod()).MakeGenericMethod(" + string.Join(", ", genArgs.Select(a => "typeof(" + a + ")").ToArray()) + ");");
+                        WriteLine("var m = ((MethodInfo)MethodInfo.GetCurrentMethod()!).MakeGenericMethod(" + string.Join(", ", genArgs.Select(a => "typeof(" + a + ")").ToArray()) + ");");
                     WriteLine("#endif", true);
 
                     WriteLine("return (" + string.Join(", ", lamPars) + ") => provider.CreateQuery<" + ret + ">(");