Follow-up to #249. defaultEngine is now @Volatile, so the original visibility race is fixed, but a few concerns remain in multik-core/.../api/Engine.kt:
Problems
- No fail-fast. If no engine is registered, the user only finds out when they first call
mk.math / mk.linalg / mk.stat — not at Multik initialization. EngineMultikException("The map of engines is empty...") is thrown lazily from each getter.
- Duplicated lazy-init triad. The same
engine == null → loadEngine() → engine = defaultEngine block is repeated in getMath(), getLinAlg(), getStatistics(), and getDefaultEngine().
- Benign but real race in
loadEngine(). The check-then-assign on defaultEngine is not atomic; two threads can both execute the resolve. Idempotent today, but fragile.
Suggested direction
- Validate
enginesProvider once at companion init and fail fast if empty (or document the lazy contract explicitly).
- Extract the engine-resolution logic into a single helper used by all getters.
- Consider
AtomicReference<EngineType?> with compareAndSet in loadEngine() to make the initialization properly atomic.
Follow-up to #249.
defaultEngineis now@Volatile, so the original visibility race is fixed, but a few concerns remain inmultik-core/.../api/Engine.kt:Problems
mk.math/mk.linalg/mk.stat— not atMultikinitialization.EngineMultikException("The map of engines is empty...")is thrown lazily from each getter.engine == null → loadEngine() → engine = defaultEngineblock is repeated ingetMath(),getLinAlg(),getStatistics(), andgetDefaultEngine().loadEngine(). The check-then-assign ondefaultEngineis not atomic; two threads can both execute the resolve. Idempotent today, but fragile.Suggested direction
enginesProvideronce at companion init and fail fast if empty (or document the lazy contract explicitly).AtomicReference<EngineType?>withcompareAndSetinloadEngine()to make the initialization properly atomic.