@@ -355,7 +355,7 @@ WSLCContainerImpl::WSLCContainerImpl(
355355 m_eventTracker(EventTracker),
356356 m_ioRelay(Relay),
357357 m_containerEvents(EventTracker.RegisterContainerStateUpdates(
358- m_id, std::bind(&WSLCContainerImpl::OnEvent, this , std::placeholders::_1, std::placeholders::_2))),
358+ m_id, std::bind(&WSLCContainerImpl::OnEvent, this , std::placeholders::_1, std::placeholders::_2, std::placeholders::_3 ))),
359359 m_state(InitialState),
360360 m_initProcessFlags(InitProcessFlags),
361361 m_containerFlags(ContainerFlags)
@@ -547,8 +547,6 @@ void WSLCContainerImpl::Start(WSLCContainerStartFlags Flags, LPCSTR DetachKeys)
547547 m_initProcessControl = nullptr ;
548548 });
549549
550- m_stoppedNotifiedEvent.ResetEvent ();
551-
552550 auto volumeCleanup = MountVolumes (m_mountedVolumes, m_virtualMachine);
553551
554552 auto portCleanup = wil::scope_exit_log (WI_DIAGNOSTICS_INFO, [this ]() { UnmapPorts (); });
@@ -567,34 +565,34 @@ void WSLCContainerImpl::Start(WSLCContainerStartFlags Flags, LPCSTR DetachKeys)
567565 cleanup.release ();
568566}
569567
570- void WSLCContainerImpl::OnEvent (ContainerEvent event, std::optional<int > exitCode)
568+ void WSLCContainerImpl::OnEvent (ContainerEvent event, std::optional<int > exitCode, std:: uint64_t eventTime )
571569{
572570 if (event == ContainerEvent::Stop)
573571 {
574- m_stoppedNotifiedEvent.SetEvent ();
575-
576572 THROW_HR_IF (E_UNEXPECTED, !exitCode.has_value ());
577- auto lock = m_lock.lock_exclusive ();
578- auto previousState = m_state;
579573
574+ // If a Stop() call is in progress, provide the timestamp via the promise
575+ // and let Stop() handle the state transition.
580576 {
581- std::lock_guard processesLock{m_processesLock};
582-
583- // Notify all processes that the container has exited.
584- // N.B. The exec callback isn't always sent to execed processes, so do this to avoid 'stuck' processes.
585- for (auto & process : m_processes)
577+ std::lock_guard stopLock{m_stopStateLock};
578+ if (m_stopState.has_value ())
586579 {
587- process->OnContainerReleased ();
580+ m_stopState->set_value (eventTime);
581+ m_stopState.reset ();
582+ return ;
588583 }
589-
590- m_processes.clear ();
591584 }
592585
586+ auto lock = m_lock.lock_exclusive ();
587+ auto previousState = m_state;
588+
589+ ReleaseProcesses ();
590+
593591 // Don't run the deletion logic if the container is already in a stopped / deleted state.
594592 // This can happen if Delete() is called by the user.
595593 if (previousState == WslcContainerStateRunning)
596594 {
597- Transition (WslcContainerStateExited);
595+ Transition (WslcContainerStateExited, eventTime );
598596
599597 ReleaseRuntimeResources ();
600598
@@ -639,6 +637,22 @@ void WSLCContainerImpl::Stop(WSLCSignal Signal, LONG TimeoutSeconds)
639637 m_state);
640638 }
641639
640+ // Set up a waitable stop state so OnEvent() can pass the Docker timestamp
641+ // back to Stop() without needing to take m_lock.
642+ std::future<std::uint64_t > stopFuture;
643+ {
644+ std::lock_guard stopLock{m_stopStateLock};
645+ m_stopState.emplace ();
646+ stopFuture = m_stopState->get_future ();
647+ }
648+
649+ // Ensure m_stopState is cleared on all exit paths so OnEvent() doesn't
650+ // take the promise path after a failed Stop().
651+ auto resetStopState = wil::scope_exit ([this ]() {
652+ std::lock_guard stopLock{m_stopStateLock};
653+ m_stopState.reset ();
654+ });
655+
642656 try
643657 {
644658 std::optional<WSLCSignal> SignalArg;
@@ -664,25 +678,24 @@ void WSLCContainerImpl::Stop(WSLCSignal Signal, LONG TimeoutSeconds)
664678 }
665679 }
666680
667- Transition (WslcContainerStateExited);
681+ // Wait for the stop event to get the Docker timestamp.
682+ // Safe while holding m_lock since OnEvent() uses m_stopStateLock on this path.
683+ std::optional<std::uint64_t > stopTimestamp;
684+ if (stopFuture.wait_for (60s) == std::future_status::ready)
685+ {
686+ stopTimestamp = stopFuture.get ();
687+ }
688+
689+ Transition (WslcContainerStateExited, stopTimestamp);
690+
691+ ReleaseProcesses ();
668692
669693 ReleaseRuntimeResources ();
670694
671695 if (WI_IsFlagSet (m_containerFlags, WSLCContainerFlagsRm))
672696 {
673697 DeleteExclusiveLockHeld (WSLCDeleteFlagsForce);
674698 }
675- else
676- {
677- // Wait for the stop notification to arrive before returning.
678- // This is required so that a caller that Stops() and immediately calls Start() again doesn't see the container
679- // switch back to 'stopped' state due to the delayed event notification.
680-
681- auto io = m_wslcSession.CreateIOContext ();
682- io.AddHandle (std::make_unique<EventHandle>(m_stoppedNotifiedEvent.get ()), MultiHandleWait::CancelOnCompleted);
683-
684- io.Run ({60s});
685- }
686699}
687700
688701void WSLCContainerImpl::Delete (WSLCDeleteFlags Flags)
@@ -1427,6 +1440,20 @@ void WSLCContainerImpl::UnmapPorts()
14271440 }
14281441}
14291442
1443+ __requires_exclusive_lock_held (m_lock) void WSLCContainerImpl::ReleaseProcesses()
1444+ {
1445+ std::lock_guard processesLock{m_processesLock};
1446+
1447+ // Notify all processes that the container has exited.
1448+ // The exec callback isn't always sent to execed processes, so do this to avoid 'stuck' processes.
1449+ for (auto & process : m_processes)
1450+ {
1451+ process->OnContainerReleased ();
1452+ }
1453+
1454+ m_processes.clear ();
1455+ }
1456+
14301457__requires_exclusive_lock_held (m_lock) void WSLCContainerImpl::ReleaseRuntimeResources()
14311458{
14321459 WSL_LOG (" ReleaseRuntimeResources" , TraceLoggingValue (m_id.c_str (), " ID" ));
@@ -1468,7 +1495,7 @@ __requires_exclusive_lock_held(m_lock) void WSLCContainerImpl::DisconnectComWrap
14681495 }
14691496}
14701497
1471- __requires_lock_held (m_lock) void WSLCContainerImpl::Transition(WSLCContainerState State) noexcept
1498+ __requires_lock_held (m_lock) void WSLCContainerImpl::Transition(WSLCContainerState State, std::optional<std:: uint64_t > stateChangedAt ) noexcept
14721499{
14731500 // N.B. A deleted container cannot transition back to any other state.
14741501 WI_ASSERT (m_state != WslcContainerStateDeleted);
@@ -1480,7 +1507,7 @@ __requires_lock_held(m_lock) void WSLCContainerImpl::Transition(WSLCContainerSta
14801507 TraceLoggingValue (m_id.c_str (), " ID" ));
14811508
14821509 m_state = State;
1483- m_stateChangedAt = static_cast <std::uint64_t >(std::time (nullptr ));
1510+ m_stateChangedAt = stateChangedAt. value_or ( static_cast <std::uint64_t >(std::time (nullptr ) ));
14841511}
14851512
14861513WSLCContainer::WSLCContainer (WSLCContainerImpl* impl, std::function<void (const WSLCContainerImpl*)>&& OnDeleted) :
0 commit comments