You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: implement Windows read limiter to prevent crashes from concurrent TensorStore reads
- Add `read_limiter.py` with a semaphore-based context manager for managing TensorStore reads on Windows.
- Update `CellMapDataset` to use the read limiter in `__getitem__` methods.
- Introduce logging warnings for potential threading issues on Windows with multiple DataLoader workers.
- Implement `close()` method in `CellMapDataset` for safe cleanup.
- Add tests in `test_windows_stress.py` to validate read limiter functionality and executor lifecycle.
- Enhance README with Windows compatibility guidelines and environment variable configurations.
Copy file name to clipboardExpand all lines: README.md
+48-3Lines changed: 48 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -399,6 +399,51 @@ input_arrays = {
399
399
}
400
400
```
401
401
402
+
## Windows Compatibility
403
+
404
+
CellMap-Data includes specific hardening for Windows to prevent native hard-crashes caused by concurrent TensorStore reads from multiple threads.
405
+
406
+
### TensorStore Read Limiter
407
+
408
+
On Windows, concurrent materializations of TensorStore-backed xarray arrays (triggered by `source[center]`, `.interp`, `.__array__`, etc.) can cause the Python process to abort. A global semaphore serializes these reads automatically:
409
+
410
+
```python
411
+
# The limiter activates automatically on Windows with the default TensorStore backend.
412
+
# No code changes required — it is transparent to all callers.
413
+
414
+
# Override the concurrency limit (default is 1 on Windows):
415
+
import os
416
+
os.environ["CELLMAP_MAX_CONCURRENT_READS"] ="2"# set BEFORE importing cellmap_data
417
+
418
+
from cellmap_data import CellMapDataset
419
+
```
420
+
421
+
### Environment Variables
422
+
423
+
| Variable | Default | Description |
424
+
|---|---|---|
425
+
|`CELLMAP_DATA_BACKEND`|`"tensorstore"`| Backend for array reads (`"tensorstore"` or `"dask"`) |
426
+
|`CELLMAP_MAX_WORKERS`|`8`| Max threads in the internal `ThreadPoolExecutor`|
- Keep the default `num_workers=0` in `CellMapDataLoader` (safest on Windows); the internal executor still parallelizes per-array I/O within each `__getitem__` call.
432
+
- If you need `num_workers > 0`, each DataLoader worker process gets its own dataset copy and its own read semaphore — this is safe.
433
+
- Do **not** share a single `CellMapDataset` instance across multiple threads that each call `__getitem__` concurrently. Use separate dataset instances instead (which is exactly what DataLoader workers do).
434
+
435
+
### Explicit Shutdown
436
+
437
+
`CellMapDataset` registers an `atexit` handler and exposes an explicit `close()` method for deterministic cleanup:
438
+
439
+
```python
440
+
dataset = CellMapDataset(...)
441
+
try:
442
+
# ... training ...
443
+
finally:
444
+
dataset.close() # shuts down the internal ThreadPoolExecutor immediately
445
+
```
446
+
402
447
## Performance Optimization
403
448
404
449
### Memory Management
@@ -407,15 +452,15 @@ input_arrays = {
407
452
- Automatic GPU memory management
408
453
- Streaming data loading for large volumes
409
454
410
-
### Parallel Processing
455
+
### Parallel Processing
411
456
412
-
- Multi-threaded data loading
457
+
- Multi-threaded data loading via persistent `ThreadPoolExecutor`
413
458
- CUDA streams for GPU optimization
414
459
- Process-safe dataset pickling
415
460
416
461
### Caching Strategy
417
462
418
-
- Persistent ThreadPoolExecutor for reduced overhead
463
+
- Persistent `ThreadPoolExecutor` per process (lazy-initialized, PID-tracked)
0 commit comments