Fix server initialization race condition with proper event signaling
This commit is contained in:
@@ -61,25 +61,34 @@ def start_embedded_server() -> tuple[SimulationServer, threading.Thread]:
|
||||
)
|
||||
)
|
||||
|
||||
server_ready = threading.Event()
|
||||
|
||||
def run_server() -> None:
|
||||
"""Run the async server in a new event loop."""
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
try:
|
||||
loop.run_until_complete(server.start())
|
||||
# Signal that server is ready
|
||||
server_ready.set()
|
||||
# Keep the event loop running
|
||||
loop.run_forever()
|
||||
except Exception as e:
|
||||
st.error(f"Server error: {e}")
|
||||
server_ready.set() # Signal even on error
|
||||
finally:
|
||||
try:
|
||||
loop.run_until_complete(server.stop())
|
||||
except Exception:
|
||||
pass
|
||||
loop.close()
|
||||
|
||||
thread = threading.Thread(target=run_server, daemon=True)
|
||||
thread.start()
|
||||
|
||||
# Wait a moment for server to start
|
||||
time.sleep(0.5)
|
||||
# Wait for server to be fully started (up to 5 seconds)
|
||||
if not server_ready.wait(timeout=5.0):
|
||||
st.error("Server failed to start within timeout")
|
||||
|
||||
return server, thread
|
||||
|
||||
@@ -87,13 +96,22 @@ def start_embedded_server() -> tuple[SimulationServer, threading.Thread]:
|
||||
def init_session_state() -> None:
|
||||
"""Initialise Streamlit session state."""
|
||||
if "server" not in st.session_state:
|
||||
with st.spinner("Starting simulation server..."):
|
||||
st.session_state.server, st.session_state.server_thread = start_embedded_server()
|
||||
|
||||
# Verify server started correctly
|
||||
if st.session_state.server.physics_engine is None:
|
||||
st.error("Failed to start simulation server. Please refresh the page.")
|
||||
st.stop()
|
||||
|
||||
# Register cleanup
|
||||
def cleanup() -> None:
|
||||
if hasattr(st.session_state, "server") and st.session_state.server is not None:
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
loop.run_until_complete(st.session_state.server.stop())
|
||||
except Exception:
|
||||
pass
|
||||
loop.close()
|
||||
atexit.register(cleanup)
|
||||
|
||||
@@ -308,7 +326,24 @@ def display_controls() -> None:
|
||||
@st.fragment(run_every=0.1)
|
||||
def simulation_display() -> None:
|
||||
"""Fragment that displays and updates simulation state."""
|
||||
# Check if server is initialized
|
||||
if "server" not in st.session_state:
|
||||
st.warning("⏳ Initializing simulation server...")
|
||||
return
|
||||
|
||||
server: SimulationServer = st.session_state.server
|
||||
|
||||
# Get current state from physics engine (for visualization)
|
||||
engine: PhysicsEngine | None = server.physics_engine
|
||||
if engine is None:
|
||||
st.error("❌ Physics engine not available. The server may not have started correctly. Try refreshing the page.")
|
||||
return
|
||||
|
||||
# Check if server is running
|
||||
if not server.is_running:
|
||||
st.warning("⚠️ Server is not running. Try refreshing the page.")
|
||||
return
|
||||
|
||||
instruments: InstrumentSet = st.session_state.instruments
|
||||
history: SimulationHistory = st.session_state.history
|
||||
|
||||
@@ -319,12 +354,6 @@ def simulation_display() -> None:
|
||||
if st.session_state.running:
|
||||
step_simulation()
|
||||
|
||||
# Get current state from physics engine (for visualization)
|
||||
engine: PhysicsEngine | None = server.physics_engine
|
||||
if engine is None:
|
||||
st.error("Physics engine not available")
|
||||
return
|
||||
|
||||
thermal = engine.get_thermal_state()
|
||||
electrical = engine.get_electrical_state()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user