Skip to content

Commit 3ad3097

Browse files
committed
Take up openkal 0.11's unit: a handle the caller holds, not a flag
The identity is established at the first start --- a job object created here and its handle reported --- so nothing has to be remembered and no registry appears. `kal_process_terminate' is one program again; the unit has its own operations.
1 parent aababc6 commit 3ad3097

2 files changed

Lines changed: 89 additions & 16 deletions

File tree

src/process.cpp

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ extern "C" {
8282

8383
// Starting a program. One function since openkal 0.11.
8484
//
85-
// ⚠️ TWO OF THE FIVE POSITIONS IN `kal_spawn' ARE REFUSED HERE, AND EACH REFUSAL
85+
// ⚠️ TWO POSITIONS IN `kal_spawn' ARE REFUSED HERE, AND EACH REFUSAL
8686
// IS OLDER THAN 0.11 --- the record moved, the answers did not.
8787
//
8888
// `grants': this environment has no numbering a preopen could arrive under, so
@@ -93,19 +93,32 @@ extern "C" {
9393
//
9494
// `KAL_SPAWN_BOUND_LIFETIME': no primitive arms it from inside the started image.
9595
//
96-
// ⚠️⚠️ `KAL_SPAWN_OWN_JOB' IS REFUSED, AND NOT BECAUSE THIS SYSTEM CANNOT DO IT ---
97-
// a job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE is exactly the unit the
98-
// flag describes, and this is the environment the idea comes from. What is
99-
// missing is somewhere to keep the job: `kal_process' is one machine word and it
100-
// holds the process handle, so `kal_process_terminate' would have no way to
101-
// reach the job later. The other two implementations need no such storage
102-
// because `getpgid(pid) == pid' recovers the fact from the kernel.
96+
// ⭐⭐ AND THE UNIT IS IMPLEMENTED HERE, WHICH AN EARLIER SHAPE OF IT WAS NOT.
10397
//
104-
// ⇒ Claiming it would mean a side table keyed by process handle, and a side
105-
// table that is wrong under concurrency terminates the wrong tree. Refused
106-
// until it can be done without one, which is a smaller lie than "kills
107-
// sometimes". Clause 6.2: the position is not claimed and a caller that asks
108-
// first is told.
98+
// 0.11 first spelled this as a flag: make the started program a unit, and let
99+
// `kal_process_terminate' reach the unit afterwards. That shape could not be
100+
// satisfied here. This system can FORM the unit --- a job object is exactly it ---
101+
// but it cannot RECOVER one from a process handle, and `kal_process' is one word
102+
// already holding the process. The other two implementations needed no storage
103+
// because `getpgid(pid) == pid' recovers it from the kernel; this one would have
104+
// needed a registry.
105+
//
106+
// ⚠️ Clause 7.1 states mechanically what needing a registry means: the
107+
// specification "has taken a shape borrowed from one environment, and THE SHAPE
108+
// IS AT FAULT rather than the implementation". handle.h says the same one level
109+
// down --- its array "holds generations and nothing else", and a lookup deciding
110+
// what a word referred to "would be a defect here".
111+
//
112+
// ⇒ So the shape changed rather than this file acquiring a table. The unit is now
113+
// a handle the CALLER holds, whose identity is established at the first start, and
114+
// both kinds of system perform that without remembering anything: here a job
115+
// object is created and its handle reported; where the unit is a process group
116+
// the first member's identifier is reported instead.
117+
//
118+
// ⚠️ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE IS DELIBERATELY NOT SET. It would make
119+
// `kal_process_job_close' end every member --- and closing means only releasing
120+
// where the unit is a number, so one operation would mean two things. Ending is
121+
// `kal_process_job_terminate' and nothing else is.
109122
int kal_process_spawn(const kal_spawn* how,
110123
const char* path, kal_uintptr path_len,
111124
const char** argv, const kal_uintptr* argv_lens, kal_uintptr argc,
@@ -120,6 +133,25 @@ int kal_process_spawn(const kal_spawn* how,
120133
if (how->grant_count > 0) return kal_err_not_supported;
121134
if (how->flags != 0) return kal_err_not_supported;
122135

136+
// The unit, created before its first member and reported to the caller. A
137+
// later member is assigned to the one the caller already holds.
138+
HANDLE unit = nullptr;
139+
bool unit_is_new = false;
140+
if (how->job) {
141+
if (how->job->h != 0) {
142+
unit = okw::unpack(how->job->h);
143+
if (!unit) return kal_err_invalid;
144+
} else {
145+
unit = CreateJobObjectW(nullptr, nullptr);
146+
if (!unit) return okw::translate_win32(GetLastError());
147+
unit_is_new = true;
148+
}
149+
}
150+
struct unit_guard {
151+
HANDLE h; bool own;
152+
~unit_guard() { if (own && h) CloseHandle(h); }
153+
} ug{ unit, unit_is_new };
154+
123155
// The directory's own name, and the program's beneath it.
124156
// Obtained rather than kept in static storage: static storage shared
125157
// between execution contexts would make two concurrent spawns one.
@@ -214,10 +246,40 @@ int kal_process_spawn(const kal_spawn* how,
214246
envc ? block : nullptr, cwd, &startup, &info);
215247
if (!started) return okw::translate_win32(GetLastError());
216248
CloseHandle(info.hThread);
249+
250+
// ⚠️ ASSIGNED BEFORE THE CALLER IS TOLD ANYTHING. A program that could not be
251+
// put into the unit is not the program that was asked for --- it would outlive
252+
// a termination of the unit --- so it is ended rather than handed back.
253+
if (unit && !AssignProcessToJobObject(unit, info.hProcess)) {
254+
const DWORD why = GetLastError();
255+
TerminateProcess(info.hProcess, 127);
256+
CloseHandle(info.hProcess);
257+
return okw::translate_win32(why);
258+
}
259+
260+
// The caller's word, written only now, and only for a unit this start made.
261+
if (unit_is_new) { how->job->h = okw::pack(unit); ug.own = false; }
262+
217263
*out = kal_process{ okw::pack(info.hProcess) };
218264
return kal_ok;
219265
}
220266

267+
// Every program in the unit. A job ends its members as one, which is the whole
268+
// reason this environment's job object is the right thing to build a unit from.
269+
int kal_process_job_terminate(kal_job j) {
270+
HANDLE h = okw::unpack(j.h);
271+
if (!h) return kal_err_invalid;
272+
if (!TerminateJobObject(h, 15)) return okw::translate_win32(GetLastError());
273+
return kal_ok;
274+
}
275+
276+
// ⚠️ RELEASES AND DOES NOT END. The limit that would have ended the members on
277+
// the last close is deliberately not set --- see the note above kal_process_spawn.
278+
void kal_process_job_close(kal_job j) {
279+
HANDLE h = okw::unpack(j.h);
280+
if (h) { okw::retire(j.h); CloseHandle(h); }
281+
}
282+
221283
// A channel: a pair of streams of which one end is meant to cross a spawn.
222284
//
223285
// THIS ENVIRONMENT DECIDES INHERITANCE PER HANDLE AND NOT PER EXEC, which is the
@@ -305,11 +367,10 @@ void kal_process_close(kal_process p) {
305367
//
306368
// GRANT_DIR kal_process_spawn refuses a non-empty `grants'
307369
// BOUND_LIFETIME no primitive arms it from inside the started image
308-
// OWN_JOB the job exists; somewhere to keep its handle does not ---
309-
// see the note above kal_process_spawn
310370
kal_uintptr kal_process_props(void) { return
311371
KAL_PROCESS_PROP_TERMINATE | KAL_PROCESS_PROP_STREAM_PASSING
312372
| KAL_PROCESS_PROP_EXIT_STATUS
313-
| KAL_PROCESS_PROP_CHANNEL; }
373+
| KAL_PROCESS_PROP_CHANNEL
374+
| KAL_PROCESS_PROP_JOB; }
314375

315376
}

src/win32.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,18 @@ OKW_IMPORT BOOL OKW_API CreateProcessW(LPCWSTR, LPWSTR, SECURITY_ATTRIBUTES*,
304304
LPCWSTR, STARTUPINFOW*, PROCESS_INFORMATION*);
305305
OKW_IMPORT BOOL OKW_API GetExitCodeProcess(HANDLE, DWORD*);
306306
OKW_IMPORT BOOL OKW_API TerminateProcess(HANDLE, UINT);
307+
308+
// openkal 0.11: the unit a set of started programs forms. A job object ends its
309+
// members as one, which is what `kal_process_job_terminate' is.
310+
//
311+
// ⚠️ NO `SetInformationJobObject' HERE, AND ITS ABSENCE IS THE DESIGN. The limit
312+
// that ends members when the last handle closes --- JOB_OBJECT_LIMIT_KILL_ON_JOB_
313+
// CLOSE --- is exactly what must NOT be set: `kal_process_job_close' releases and
314+
// does not end, because where a unit is a process group closing is releasing a
315+
// number. Not declaring the call is how that stays true by construction.
316+
OKW_IMPORT HANDLE OKW_API CreateJobObjectW(SECURITY_ATTRIBUTES*, LPCWSTR);
317+
OKW_IMPORT BOOL OKW_API AssignProcessToJobObject(HANDLE, HANDLE);
318+
OKW_IMPORT BOOL OKW_API TerminateJobObject(HANDLE, UINT);
307319
OKW_IMPORT HANDLE OKW_API GetCurrentProcess(void);
308320
OKW_IMPORT DWORD OKW_API WaitForSingleObject(HANDLE, DWORD);
309321

0 commit comments

Comments
 (0)