Open jeremy-morren opened 12 months ago
The issue is slightly deeper than this. Observable.Delay
itself isn't the problem—it can be made to work. Here's a modification that works around the issue you're seeing:
IScheduler sch = DefaultScheduler.Instance.DisableOptimizations();
await Observable.Return(1)
.Delay(TimeSpan.FromSeconds(1), sch)
.ToTask();
You should then see Delay
work correctly. This demonstrates that Delay
itself is perfectly capable of working on Blazor WASM.
So the question is why does it fail when used in the default way?
The basic problem here is this: by default all timed operators in Rx default to using SchedulerDefaults.TimeBasedOperations
to run work at the necessary time. That property returns an instance of DefaultScheduler
, and DefaultScheduler
supports an optional scheduler feature: ISchedulerLongRunning
which it implements by spinning up a new thread. Delay
will use this scheduler feature if available, because the nature of Delay
(an inherently very expensive operator, and not one you would use if your only goal was to generate a single notification 1 second from now) is that it can work a lot better if it has a thread to itself.
The problem of course is that you're not allowed to create new threads on WASM. The workaround above basically downgrades the scheduler to one where no optional features (such as long running operation support) are available. This is probably overkill. This may be disabling other optimizations that would actually work.
It might be that the most straightforward way for us to fix this is to have the ConcurrencyAbstractionLayerImpl
detect when threads can't be created. It currently has this:
public bool SupportsLongRunning => true;
but if this were to return false
on Blazor WASM, you would no longer need the workaround, because the DefaultScheduler
checks that property and only tries to create new threads if it returns true.
There was a time where different builds of Rx for different environments would hard-code SupportsLongRunning
either to true
or false
because some of our build targets just didn't support creation of new threads at all.
The problem for WASM is that it's not a distinct build target. Because of the "One .NET" initiative, it just ends up being net6.0
.
Presumably there's some way to discover at runtime that thread creation is unavailable—other libraries must have encountered this problem on WASM before. So it might just be a case of finding out what the preferred way is to ask "Are we allowed to create new threads?" and making SupportsLongRunning
report that.
However, there might well be other WASM-specific problems. The root cause of why it was possible for this to blow up like this is that we've never executed our test suites on WASM.
So rather than merely fixing up this one symptom, I think the best thing to do would be to work out how to run the full Rx test suite on Blazor WASM. That would reveal any other issues.
Note to self: maybe https://devblogs.microsoft.com/dotnet/introducing-ms-test-runner/ could help with this?
The other suggestion is to use https://learn.microsoft.com/en-us/aspnet/core/client-side/dotnet-interop?view=aspnetcore-8.0 as a testing approach.
Bug
On Blazor WASM,
Observable.Delay
is not implemented.Index.razor
Exception: