Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

BLite.Wasm: synchronous waits block or kill the browser runtime (HasIndex open, sync LINQ/BLQL ToList, Dispose, IsAvailable)

Open
#290 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 2 days

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
csharp, javascript, wasm
Domain
backend, web-dev

Research direction

Read src/BLite.Core/Collections/DocumentCollection.cs, src/BLite.Core/Query/BTreeQueryProvider.cs, src/BLite.Core/Query/Blql/BlqlQuery.cs, src/BLite.Core/Storage/StorageEngine.cs, and the IsAvailable implementations in src/BLite.Wasm/Storage. Start with the browser cases BW2, BW3, BW4, and BW7 in tests/BLite.Wasm.BrowserTests; run those cases across the affected publish variants and backends. Done means indexed context opening, supported sync operations, disposal, and availability probes behave as specified without blocking or terminating the browser runtime.

Written by the indexing model from the issue text.

Description

bug package/core severity/high

Four entry points that are safe on desktop block, throw or kill the runtime in the browser, where .NET runs on one thread and a blocking wait on a thread-pool task can never be satisfied (PlatformNotSupportedException: Cannot wait on monitors on this runtime). Each one is a Task.Run(...).GetAwaiter().GetResult() or a Task.Wait in BLite.Core, or a [JSImport] whose module is not loaded yet, in BLite.Wasm. The README's "Async-Only" line covers CRUD, which works; these are the paths around it.

Found by the browser harness (tests/BLite.Wasm.BrowserTests, https://github.com/EntglDb/BLite/pull/295), which carries the affected rows as known candidates BW2, BW3, BW4 and BW7 until this is fixed. Same result in all four publish variants, on all three backends, including the in-memory one, so it is the runtime, not the storage.

1. A typed DocumentDbContext with any declared index cannot open (BW2)

Rows Typed_context_with_async_linq InMemory/indexed=True, Typed_write_survives_reload IndexedDb/checkpoint=True/indexed=True.

Constructing a context whose OnModelCreating declares HasIndex throws on an empty, newly created database:

InvalidOperationException: Cannot build index 'idx_Value' of collection 'items' from the documents already in it: Cannot wait on monitors on this runtime. Nothing was saved, but every open fails the same way …

The same entity without the index works (Typed_context_with_async_linq */indexed=False passes on all three backends, and the write survives a reload after a checkpoint).

Mechanism: ApplyIndexBuilder fills the new index with Task.Run(() => FillUnregisteredIndexAsync(index, ct)).GetAwaiter().GetResult() (src/BLite.Core/Collections/DocumentCollection.cs:1233); the rebuild path for a changed definition does the same (:927). The comment says the Task.Run is there so a captured SynchronizationContext cannot deadlock the wait; on the browser runtime the wait itself is rejected. The message also misleads: the collection holds no documents.

Expected: a typed context with model-declared indexes opens on a browser backend; the generated context is the path WASM_SUPPORT.md's DocumentDbContext(StorageEngine) constructor exists for.

2. Synchronous LINQ and BLQL ToList throw (BW3)

Rows Typed_sync_linq_operator InMemory/* (tolist, count, first, any, foreach), Blql_sync_operator InMemory/tolist.

Every synchronous System.Linq terminal operator on collection.AsQueryable() throws PlatformNotSupportedException: Cannot wait on monitors on this runtime. BLQL ToList() throws the same; BLQL Count(), FirstOrDefault() and Any() pass.

Mechanism: BTreeQueryProvider.Execute runs the async path through Task.Run(...).GetAwaiter().GetResult() (src/BLite.Core/Query/BTreeQueryProvider.cs:95), and so does BlqlQuery.AsEnumerable (src/BLite.Core/Query/Blql/BlqlQuery.cs:146). The three BLQL operators that pass call XxxAsync().GetAwaiter().GetResult() without Task.Run; they work only because, with every page cached in memory, the async path completes without ever yielding. A backend that awaited I/O inside a read would make them fail too.

Expected: either the synchronous operators work in the browser, or they throw a clear NotSupportedException naming the async operator to use, and the docs say so.

3. Every Dispose freezes the tab for 5 seconds (BW4)

Row Dispose_returns_promptly; visible in every scenario that disposes an engine (5.0–5.2 s against 40–100 ms for the same work without Dispose).

Mechanism: StorageEngine.Dispose waits for the group-commit writer with _writerTask?.Wait(TimeSpan.FromSeconds(5)) (src/BLite.Core/Storage/StorageEngine.cs:462, also :369). The writer can only finish on the thread that is waiting for it, so the wait always runs to its timeout, with the browser's only thread blocked and the page unresponsive. The pending-free checkpoint on dispose (:498, Task.Run(() => CheckpointAsync()).GetAwaiter().GetResult()) would throw the same way; it is caught, and the pages stay leaked.

Expected: Dispose returns in milliseconds, or BLite.Wasm offers DisposeAsync and the docs say to use it.

4. IsAvailable() as the first call kills the .NET runtime (BW7)

Rows IsAvailable_before_any_other_call IndexedDb, Opfs.

Calling the public IndexedDbPageStorage.IsAvailable() or OpfsPageStorage.IsAvailable() before BLiteWasm.CreateAsync or DetectBestBackendAsync does not throw: the runtime asserts and exits, and every later .NET call in the tab fails.

MONO_WASM: Assert failed: ES6 module ./blite-indexeddb.mjs was not imported yet, please call JSHost.ImportAsync() first in order to invoke idbIsAvailable
MONO_WASM: Assert failed: ES6 module ./blite-opfs-bridge.mjs was not imported yet, please call JSHost.ImportAsync() first in order to invoke opfsWorkerIsAvailable

Mechanism: both methods call a [JSImport] function (src/BLite.Wasm/Storage/IndexedDbPageStorage.cs:60, OpfsPageStorage.cs:61) whose module only EnsureLoadedAsync imports; OpfsPageStorage's own doc comment says "Use IsAvailable to feature-detect at runtime", and WASM_SUPPORT.md lists IsAvailable as the feature probe.

Expected: a feature probe returns false or throws; it does not take the runtime down. Either make the probes IsAvailableAsync (import, then probe), or make the synchronous ones return false when the module is not loaded.

Affected versions: BLite.Wasm since #63 (830e83e, v4.4.0) through v5.2.3; the blocking waits in BLite.Core since the index fill of #151 (e187bd6, v5.2.0) and the query provider's Task.Run (v4.x).

Fix considerations

The four share one rule: on OperatingSystem.IsBrowser() nothing may block on a task. The index fill has an async caller already (FillUnregisteredIndexAsync); exposing the context's index build as part of an async open (DocumentDbContext.InitializeAsync, or building model indexes lazily on first async access) removes the wait. The LINQ provider can throw NotSupportedException with the async operator's name in the browser instead of PlatformNotSupportedException from inside the runtime. Dispose can skip the writer wait when the writer cannot run (the channel is completed, nothing is pending), or BLite.Wasm can add DisposeAsync. The probes can become async.

Related: #57 / #63 (BLite.Wasm), #151 / #156 (the index fill on open), #209 / #242 (the checkpoint on dispose). The other issues from the same harness: #289, #291, #292, #293.

Dominant language
C#
Stars
66
Forks
6
Avg merge
1d 2h
Merged PRs (30d)
58

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from EntglDb/BLite

All issues in EntglDb/BLite

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.