Merge nucleic/olive-jade-civet-rznt into dev
This commit is contained in:
+10
-6
@@ -87,12 +87,16 @@ name**. That second construction is the whole point, and it is the last open que
|
|||||||
on: the compat SDK exposes a `Session` *constructor* and no attach, so does constructing over an
|
on: the compat SDK exposes a `Session` *constructor* and no attach, so does constructing over an
|
||||||
existing name re-adopt it, or refuse?
|
existing name re-adopt it, or refuse?
|
||||||
|
|
||||||
- **Refused** (`WSLC_E_SESSION_RESERVED`, `0x80040607`) — D13's premise is confirmed, and session
|
**Answered on 2.9.4: it cannot.** The constructor is lazy and always succeeds — judging by it is
|
||||||
reattach genuinely requires `IWSLCSessionManager::OpenSessionByName` on the internal interface.
|
what made the first reading of this probe wrong. `Start()` is where the service is consulted, and
|
||||||
- **Constructed** — which is what 2.9.4 actually does. That alone proves nothing (construction may
|
a second `Start()` on a running name fails with `ERROR_ALREADY_EXISTS` (0x800700B7). Note it is
|
||||||
be lazy), so the probe then runs an **identity test**: terminate the FIRST session and read from
|
*not* `WSLC_E_SESSION_RESERVED`, which exists in `wslc.idl` but evidently means something
|
||||||
the SECOND. A read that worked before and fails after is one underlying session answering both
|
narrower — don't key on it.
|
||||||
handles; a read that keeps working means two independent VMs were running.
|
|
||||||
|
So the probe judges on `Start()`. If a future version lets the second `Start()` through, it then
|
||||||
|
runs an **identity test**: terminate the FIRST session and read from the SECOND. A read that
|
||||||
|
worked before and fails after is one underlying session answering both handles; a read that keeps
|
||||||
|
working means two independent VMs.
|
||||||
|
|
||||||
The identity test deliberately uses only the compat SDK. The obvious check would be
|
The identity test deliberately uses only the compat SDK. The obvious check would be
|
||||||
`wslc session ls` — but **`wslc.exe` is not on PATH by default**, so a spike that depends on it
|
`wslc session ls` — but **`wslc.exe` is not on PATH by default**, so a spike that depends on it
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ namespace WslcApiDump;
|
|||||||
///
|
///
|
||||||
/// It never mutates anything unless asked: the default run only reads type metadata. `--probe`
|
/// It never mutates anything unless asked: the default run only reads type metadata. `--probe`
|
||||||
/// additionally calls the two safe statics (`GetVersion`, `GetMissingComponents`). `--session`
|
/// additionally calls the two safe statics (`GetVersion`, `GetMissingComponents`). `--session`
|
||||||
/// goes further and creates a real session, then creates a SECOND one with the same name — which
|
/// goes further and creates a real session, then a SECOND one with the same name, to settle
|
||||||
/// is the open question D13 turns on: whether the compat SDK's constructor can re-adopt a session
|
/// whether the compat SDK can re-adopt a running session. Answered on 2.9.4: it cannot. The
|
||||||
/// by name, or refuses with `WSLC_E_SESSION_RESERVED` and leaves reattach to the internal COM
|
/// constructor is lazy and always succeeds; `Start()` is where the service is consulted, and it
|
||||||
/// interface (docs/WINDOWS_PORT.md §13.1).
|
/// refuses with `ERROR_ALREADY_EXISTS`. So reattach needs the internal COM interface, which is
|
||||||
|
/// what D13 assumes (docs/WINDOWS_PORT.md §13.1).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
@@ -402,19 +403,39 @@ internal static class Program
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Starting it is one more signal: re-adopting a RUNNING session should be a no-op,
|
// The constructor is LAZY — it only captures settings, so its success says nothing.
|
||||||
// whereas a genuinely separate session would be booting a second VM here.
|
// `Start()` is where the service is actually asked for the session, and therefore
|
||||||
if (start is not null)
|
// where exclusivity is enforced. Judge on that.
|
||||||
|
var secondStarted = false;
|
||||||
|
string? startFailure = null;
|
||||||
|
if (start is null)
|
||||||
{
|
{
|
||||||
try { start.Invoke(second, null); Console.WriteLine(" second Start() ok"); }
|
Console.WriteLine(" (no Session.Start() — cannot judge)");
|
||||||
catch (TargetInvocationException e)
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($" second Start() threw {Describe(e.InnerException)}");
|
try { start.Invoke(second, null); secondStarted = true; }
|
||||||
|
catch (TargetInvocationException e) { startFailure = Describe(e.InnerException); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Console.WriteLine(" → CONSTRUCTED — the constructor did NOT refuse.");
|
if (secondStarted)
|
||||||
|
{
|
||||||
|
Console.WriteLine(" second Start() ok — it did NOT refuse, so it may have re-adopted");
|
||||||
IdentityTest(sessionType, first, second);
|
IdentityTest(sessionType, first, second);
|
||||||
}
|
}
|
||||||
|
else if (startFailure is not null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" second Start() threw {startFailure}");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine(" → REFUSED at Start(). The constructor succeeding meant nothing:");
|
||||||
|
Console.WriteLine(" it is lazy, and the service is only consulted by Start().");
|
||||||
|
Console.WriteLine(" So the compat SDK CANNOT re-adopt a running session, and D13");
|
||||||
|
Console.WriteLine(" holds — §2.3 reattach needs IWSLCSessionManager::OpenSessionByName");
|
||||||
|
Console.WriteLine(" (or EnterSession) on the internal COM interface.");
|
||||||
|
Console.WriteLine(" Note the code is ERROR_ALREADY_EXISTS, not WSLC_E_SESSION_RESERVED;");
|
||||||
|
Console.WriteLine(" the reserved code evidently means something narrower.");
|
||||||
|
}
|
||||||
|
}
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
if (keep)
|
if (keep)
|
||||||
{
|
{
|
||||||
@@ -447,7 +468,8 @@ internal static class Program
|
|||||||
Console.WriteLine(" identity test: terminate the FIRST, then read from the SECOND");
|
Console.WriteLine(" identity test: terminate the FIRST, then read from the SECOND");
|
||||||
if (!Reads(getImages, second, out var before))
|
if (!Reads(getImages, second, out var before))
|
||||||
{
|
{
|
||||||
Console.WriteLine($" inconclusive — the second session already fails to read: {before}");
|
// Only reachable if Start() reported success but the session is unusable anyway.
|
||||||
|
Console.WriteLine($" inconclusive — the second session reads fail already: {before}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Console.WriteLine(" second reads ok before termination");
|
Console.WriteLine(" second reads ok before termination");
|
||||||
@@ -564,6 +586,9 @@ internal static class Program
|
|||||||
0x80070032 => "ERROR_NOT_SUPPORTED — the installed WSL does not implement this call "
|
0x80070032 => "ERROR_NOT_SUPPORTED — the installed WSL does not implement this call "
|
||||||
+ "(almost always: WSL is older than the SDK)",
|
+ "(almost always: WSL is older than the SDK)",
|
||||||
0x80070005 => "E_ACCESSDENIED",
|
0x80070005 => "E_ACCESSDENIED",
|
||||||
|
0x800700B7 => "ERROR_ALREADY_EXISTS — a session of that name is already running",
|
||||||
|
0x8000000E => "E_ILLEGAL_METHOD_CALL — the object is not in a state to serve this call "
|
||||||
|
+ "(e.g. Session.Start() was never called, or failed)",
|
||||||
0x80040601 => "WSLC_E_IMAGE_NOT_FOUND",
|
0x80040601 => "WSLC_E_IMAGE_NOT_FOUND",
|
||||||
0x80040603 => "WSLC_E_CONTAINER_NOT_FOUND",
|
0x80040603 => "WSLC_E_CONTAINER_NOT_FOUND",
|
||||||
0x80040605 => "WSLC_E_CONTAINER_NOT_RUNNING",
|
0x80040605 => "WSLC_E_CONTAINER_NOT_RUNNING",
|
||||||
|
|||||||
Reference in New Issue
Block a user