ReactiveUI icon indicating copy to clipboard operation
ReactiveUI copied to clipboard

[Bug] Alternative STA threads do not play nice with WhenAnyValue and dependency properties.

Open bradphelan opened this issue 7 years ago • 9 comments

What is the current behavior?

<TextBox x:Name="FilterText"/>

and

this.WhenAnyValue(p=>p.FilterText.Text).Subscribe(Console.WriteLine)

will crash with a cross thread access exception. This happens if the window the control in hosted in is running in a different thread than the RxApp.MainSchedular

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem

Full description is here

https://stackoverflow.com/questions/44236317/whenanyvalue-throws-a-threading-exception-when-used-against-a-dependency-propert/44237800#44237800

What is the expected behavior?

When WhenAnyValue is executed in a specific synchronization context then it should not blindly dispatch to RxApp.MainThreadScheduler.

What is the motivation / use case for changing the behavior?

My logging window does't work :(

Which versions of ReactiveUI, and which platform / OS are affected by this issue? Did this work in previous versions of ReativeUI? Please also test with the latest stable and snapshot (http://docs.reactiveui.net/en/contributing/snapshot/index.html) versions.

Currently running 7.2

Other information (e.g. stacktraces, related issues, suggestions how to fix)

https://stackoverflow.com/questions/44236317/whenanyvalue-throws-a-threading-exception-when-used-against-a-dependency-propert/44237800#44237800

bradphelan avatar May 29 '17 08:05 bradphelan

I wasn't able to reproduce this in a brand new WPF project. Is there any additional complexity in the way you call CreateAndShowStaWindow or in your factory?

I used:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            await CreateAndShowStaWindow(() => new Window1());
        }

        public static Task<T> CreateAndShowStaWindow<T>(Func<T> factory) where T : Window
        {
            var windowResult = new TaskCompletionSource<T>();

            var newWindowThread = new Thread(() =>
            {
                var window = factory();
                window.Show();
                windowResult.SetResult(window);            
                window.Events().Closed.Subscribe( _ => window.Dispatcher.InvokeShutdown() );
                System.Windows.Threading.Dispatcher.Run();

            });
            newWindowThread.SetApartmentState(ApartmentState.STA);
            newWindowThread.IsBackground = true;
            newWindowThread.Start();
            newWindowThread.Name = "STA WPF";

            return windowResult.Task;
        }
    }
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            
            this.WhenAnyValue(x => x.FilterText.Text).Subscribe(x => Console.WriteLine(x));
        }
    }

rdavisau avatar May 29 '17 09:05 rdavisau

Yeah. I just did the same and the error does not occur. Something evil going on :(

bradphelan avatar May 29 '17 09:05 bradphelan

I'll try and isolate some more features of my large project in the test project and get back to you soon.

bradphelan avatar May 29 '17 09:05 bradphelan

I can't figure it out. Is there anything in the code that might try to dispatch to another context under certain conditions?

bradphelan avatar May 29 '17 09:05 bradphelan

Got it.

Add in the following calls

       Locator.CurrentMutable.InitializeReactiveUI();

I have a reproducing repository.

https://github.com/bradphelan/RxUIBug1375

There is probably a bit more guff in there than necessary to reproduce as I was adding in more and more stuff till I found the trigger. I'll try to pare it back a bit.

bradphelan avatar May 29 '17 10:05 bradphelan

The reason I was calling InitializeReactiveUI is made clearer by the following snippet from my main application. I had to set up the RxUI / Ninject binding as well as some other stuff. The code has been there for a long time and works apart from this discovery I have made now.

    public static void Init(IKernel kernel)
    {
        var customResolver = new FuncDependencyResolver
            (
             (service, contract) =>
             {
                 if (contract != null)
                     return kernel.GetAll( service, contract );
                 var items = kernel.GetAll( service );
                 var list = items.ToList();
                 return list;
             }
             , (factory, service, contract) =>
             {
                 var binding = kernel.Bind( service ).ToMethod( _ => factory() );
                 if (contract != null)
                     binding.Named( contract );
             } );


        Locator.CurrentMutable.InitializeSplat();
        Locator.CurrentMutable.InitializeReactiveUI();
        Locator.CurrentMutable = customResolver;
        Locator.CurrentMutable.RegisterLazySingleton(() => new WeinCadViewLocator(kernel), typeof(IViewLocator));

        var log = SerilogExtensions.DefaultLogger;
        Log.Logger = log;
        SerilogSplatLogger.Register(log);
    }

bradphelan avatar May 29 '17 10:05 bradphelan

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. We do this because it helps reduce the workload of our extremely busy maintainers. If you want this issue progressed faster please start talks with a maintainer with how you can help out. There are so many ways to contribute to open-source and the most valued are often not code related. We are always looking for more help and look after those who stick around. Say howdy to one of the maintainers or browse https://reactiveui.net/contribute/ for ideas on where to start your journey.

stale[bot] avatar Aug 25 '17 09:08 stale[bot]

This should not be marked as stale as there is a reproducing example provided

JKronberger avatar Sep 06 '17 04:09 JKronberger

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. We do this because it helps reduce the workload of our extremely busy maintainers. If you want this issue progressed faster please start talks with a maintainer with how you can help out. There are so many ways to contribute to open-source and the most valued are often not code related. We are always looking for more help and look after those who stick around. Say howdy to one of the maintainers or browse https://reactiveui.net/contribute/ for ideas on where to start your journey.

stale[bot] avatar Nov 05 '17 04:11 stale[bot]