Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/windows/service/exe/HcsVirtualMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,9 @@ HcsVirtualMachine::HcsVirtualMachine(_In_ const WSLCSessionSettings* Settings)

HcsVirtualMachine::~HcsVirtualMachine()
{
std::lock_guard lock(m_lock);
// Do not hold m_lock: waiting on m_vmExitEvent and closing the compute system below both block
// on in-flight HCS exit/crash callbacks, which may themselves need m_lock. OnExit() is lock-free,
// and closing the compute system drains all callbacks, so the rest of teardown needs no lock.

// Wait up to 5 seconds for the VM to terminate gracefully.
bool forceTerminate = false;
Expand Down
3 changes: 1 addition & 2 deletions src/windows/service/exe/WSLCSessionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,7 @@ void WSLCSessionManagerImpl::CreateSession(
g_pluginManager, sessionId, creatorPid, std::wstring(resolvedDisplayName), wil::shared_handle(sharedToken), std::vector<BYTE>(storedSid));

// Create the VM factory in the SYSTEM service (privileged). The per-user session
// uses it to create the VM. Funneling VM creation through a factory lets the session
// own when VMs are created, rather than having one handed to it up front.
// uses it to create VMs on demand and recreate them after idle-termination.
auto vmFactory = Microsoft::WRL::Make<WSLCVirtualMachineFactory>(Settings);

// Launch per-user COM server factory and add it to a fresh per-session job object for crash cleanup.
Expand Down
8 changes: 8 additions & 0 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,14 @@ interface IWSLCSession : IUnknown
// Container management.
HRESULT CreateContainer([in] const WSLCContainerOptions* Options, [in, unique] IWarningCallback* WarningCallback, [out] IWSLCContainer** Container);
HRESULT OpenContainer([in, ref] LPCSTR Id, [out] IWSLCContainer** Container);

// Keeps the VM alive for the duration of a client-side container operation. The CLI performs
// each mutation as two round-trips (OpenContainer followed by the operation) and may stream
// output afterwards. With on-demand VM idle-termination the VM could otherwise tear down
// between those calls, disconnecting the container wrapper and failing the second call with
// RPC_E_DISCONNECTED. The client holds the returned token for the whole operation; releasing
// it (or the client exiting) lets the VM idle-terminate again.
HRESULT BeginContainerOperation([out] IUnknown** Operation);
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
HRESULT ListContainers([in, unique] const WSLCListContainersOptions* Options,[out, size_is(, *Count)] WSLCContainerEntry** Containers,[out] ULONG* Count, [out, size_is(, *PortsCount)] WSLCContainerPortMapping** Ports, [out] ULONG* PortsCount);
Comment thread
benhillis marked this conversation as resolved.
HRESULT PruneContainers([in, unique, size_is(FiltersCount)] const WSLCFilter* Filters, [in] ULONG FiltersCount, [out] WSLCPruneContainersResults* Result);

Expand Down
9 changes: 9 additions & 0 deletions src/windows/wslc/services/ContainerService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ std::wstring ContainerService::FormatRelativeTime(ULONGLONG timestamp)

int ContainerService::Attach(Session& session, const std::string& id)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));

Expand Down Expand Up @@ -452,6 +453,7 @@ CreateContainerResult ContainerService::Create(Session& session, const std::stri

int ContainerService::Start(Session& session, const std::string& id, bool attach)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
WSLCContainerStartFlags flags = attach ? WSLCContainerStartFlagsAttach : WSLCContainerStartFlagsNone;
Expand Down Expand Up @@ -482,20 +484,23 @@ int ContainerService::Start(Session& session, const std::string& id, bool attach

void ContainerService::Stop(Session& session, const std::string& id, StopContainerOptions options)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
THROW_IF_FAILED_EXCEPT(container->Stop(options.Signal, options.Timeout), WSLC_E_CONTAINER_NOT_RUNNING);
}

void ContainerService::Kill(Session& session, const std::string& id, WSLCSignal signal)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
THROW_IF_FAILED(container->Kill(signal));
}

void ContainerService::Delete(Session& session, const std::string& id, bool force)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
THROW_IF_FAILED(container->Delete(force ? WSLCDeleteFlagsForce : WSLCDeleteFlagsNone));
Expand Down Expand Up @@ -550,6 +555,7 @@ std::vector<ContainerInformation> ContainerService::List(

int ContainerService::Exec(Session& session, const std::string& id, ContainerOptions options)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));

Expand Down Expand Up @@ -581,6 +587,7 @@ int ContainerService::Exec(Session& session, const std::string& id, ContainerOpt

InspectContainer ContainerService::Inspect(Session& session, const std::string& id)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
wil::unique_cotaskmem_ansistring output;
Expand Down Expand Up @@ -610,6 +617,7 @@ void ContainerService::Export(Session& session, const std::string& id, HANDLE ou

void ContainerService::Logs(Session& session, const std::string& id, bool follow, bool timestamps, ULONGLONG since, ULONGLONG until, ULONGLONG tail)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));

Expand Down Expand Up @@ -637,6 +645,7 @@ void ContainerService::Logs(Session& session, const std::string& id, bool follow

wsl::windows::common::docker_schema::ContainerStats ContainerService::Stats(Session& session, const std::string& id)
{
auto operation = session.BeginContainerOperation();
Comment thread
benhillis marked this conversation as resolved.
Comment thread
benhillis marked this conversation as resolved.
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
wil::unique_cotaskmem_ansistring output;
Expand Down
18 changes: 17 additions & 1 deletion src/windows/wslc/services/SessionModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ struct Session
NON_COPYABLE(Session);
DEFAULT_MOVABLE(Session);

explicit Session(wil::com_ptr<IWSLCSession> session) : m_session(std::move(session))
explicit Session(wil::com_ptr<IWSLCSession> session, wil::com_ptr<IWarningCallback> warningCallback = {}) :
m_session(std::move(session)), m_warningCallback(std::move(warningCallback))
{
}

Expand All @@ -31,8 +32,23 @@ struct Session
return m_session.get();
}

// Acquires an activity token that keeps the VM alive for the duration of a client-side
// container operation (resolve + operate, plus any streamed output). Hold the returned
// pointer for the whole operation; releasing it lets the VM idle-terminate again.
[[nodiscard]] wil::com_ptr<IUnknown> BeginContainerOperation() const
{
wil::com_ptr<IUnknown> operation;
THROW_IF_FAILED(m_session->BeginContainerOperation(&operation));
return operation;
}

private:
wil::com_ptr<IWSLCSession> m_session;

// Kept alive for the lifetime of the session model (i.e. the whole CLI command) so the service
// can deliver warnings emitted by lazy/background work — such as resource recovery on the first
// VM start — back to this CLI invocation, even though no single COM call carries the callback.
wil::com_ptr<IWarningCallback> m_warningCallback;
};

} // namespace wsl::windows::wslc::models
5 changes: 4 additions & 1 deletion src/windows/wslc/services/SessionService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ Session SessionService::OpenOrCreateDefaultSession()
auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
THROW_IF_FAILED(manager->CreateSession(nullptr, WSLCSessionFlagsNone, warningCallback.Get(), &session));
wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
return Session(std::move(session));

// Hold the warning callback for the session's lifetime so warnings emitted by a lazy VM start
// (e.g. resource recovery) are still delivered to this CLI invocation.
return Session(std::move(session), wil::com_ptr<IWarningCallback>(warningCallback.Get()));
}

int SessionService::Attach(const Session& session)
Expand Down
9 changes: 9 additions & 0 deletions src/windows/wslcsession/IORelay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,20 @@ void IORelay::Stop()
}
}

bool IORelay::IsRelayThread() const noexcept
{
return m_thread.get_id() == std::this_thread::get_id();
}

void IORelay::Run()
try
{
common::wslutil::SetThreadDescription(L"IORelay");

// Handle callbacks dispatched from this thread (e.g. unexpected VM exit) can tear the VM down,
// releasing cross-process COM proxies, so join the process MTA to avoid RPC_E_WRONG_THREAD.
const auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED);

windows::common::io::MultiHandleWait io;

// N.B. All the IO must happen on the thread.
Expand Down
6 changes: 6 additions & 0 deletions src/windows/wslcsession/IORelay.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ class IORelay

void Stop();

// Returns true if the calling thread is the IORelay's own worker thread (i.e. the call
// is being made from a handle callback). Destroying the IORelay from this thread would
// join the thread with itself and call std::terminate(), so callers that may run on the
// relay thread must check this before destroying the object.
bool IsRelayThread() const noexcept;

private:
void Start();
void Run();
Expand Down
Loading