extending uiop/run-program

Elias Pipping pipping.elias at icloud.com
Mon Aug 15 13:25:49 UTC 2016

> On 15 Aug 2016, at 15:07, Faré <fahree at gmail.com> wrote:
>>  https://gitlab.common-lisp.net/epipping/asdf/commit/ffd8d8564bdc6551f2b682e7683bf16d6d459161
>> (to be squashed into an earlier commit) that I’m rather happy with.
> Uh, your comment suggests that on lispworks6, your destructuring-bind is wrong.

Oh, the comment may be doing more harm than good then. The code is actually correct. It’s just that
in the if-branch, lispworks 6 will return (io,err,pid), and in the else-branch (pid), whereas lispworks7 will
return (io,err,pid) in both branches.

> Note that I'm OK with declaring we don't support lispworks6 anymore,
> but that must be made explicit. Also, ask Robert what he thinks about it.

I don’t think this will be necessary. I haven’t been able to test with lispworks 5 but lispworks 6 works
pretty well.

>>> * Also, is #+lispworks7 future-proof? Is there a feature for 7-or-later?
>> I don’t think so, no. It seems that each version has a feature corresponding to its own version but not others. We could maybe provide our own, though. We cannot know what versioning scheme they’ll use in the future but it shouldn’t be too difficult to find out what features lispworks has defined in the past. So a
>>  (lispworks7-or-greater-p)
>> could probably be implemented as
>>  (not (or #+(or lispworks4 lispworks5 lispworks6) t))
>> or something along those lines.
> Yuck. I'd rather query SYSTEM::*MAJOR-VERSION-NUMBER* in a #..

I didn’t know about that variable. The resulting code would be nicer but it’s still a private symbol which might disappear in the future. Personally, I’d prefer to go with the uglier but more official version.

>> (A) The first is with the :overwrite default for :if-output-exists. [...]
>> So since all [implementations] appear to support the behaviour of :supersede (even though not necessarily by that name) and not all of them support :overwrite, it might make sense to change the default from :overwrite to :supersede and then (like other normaliser functions) have a translator that turns :supersede into :overwrite for CLISP. This would unfortunately also affect the (exported) run-program function. On the plus side, it would only change behaviour that previously could not be relied upon anyway.
> Yes, that would be the Right Thing(tm).
> The entire purpose of UIOP is to provide some portable abstractions
> with stable semantics abstracting over implementation specifics,
> rather than functions the meaning of which is actually not portable.
> See my ASDF3 essay on this topic.
>> (B) Process termination
>> Even though it’s possible to kill a process on nearly ever platform somehow (not on CLISP, except for processes spawned through ext::launch) there are multiple tiers of support for that: From native to external: If you have a process in MKCL, you can terminate it through its process object, on unix or windows. That’s the ideal situation.
> If it helps with CLISP, maybe ext::launch should be the default on CLISP.
> I admit I don't care too much about CLISP, that hasn't been actively
> maintained for years.
> (It still ships with ASDF 2.33, for John McCarthy's sake.)

It appears there are some attempts to breathe new life into CLISP, though. See e.g.


>> The worst-possible situation that can still be handled if needed is when all you have is the PID: you end up calling `taskkill` on windows, through cmd.exe, or `kill` on unix, through the shell. I only recently started thinking about potential issues that this might involve other than performance, not only in this extreme case but also in cases that lie half-way between the MKCL situation and the worst-possible scenario, e.g. SBCL’s.
>> If you obtain the PID of an arbitrary external process and then try to kill it through that PID at a later point in time, the process may no longer be running. It may not even exist anymore. Worse yet, its PID may have been recycled by the system and assigned to a newly spawned process that you end up killing by accident!
> Note that on Unix, the race condition between killing a process
> and the process dying then its PID being recycled is inherent,
> whatever abstraction MKCL may try to provide that SBCL doesn’t.

Yes, but we’re not dealing with this general problem as I tried to outline in the paragraph after that (quoting myself):

  Fortunately, I am not trying to solve this general problem but the simpler problem of killing a process spawned through run-program, which will necessarily be a child process. Such a process will signal its parent with SIGCHLD when it terminates and stick around as a zombie until it is waited for.

So the issue is solvable here, no?

> The only mitigating behavior would be for the kernel to insert a pause
> before a PID could be used, and hope that programs never try kill a PID
> more than that pause duration after having checked the process was alive
> (and even then, if the process is kill -STOP'ped between the check and the kill,
> there's nothing that can prevent the race condition).
> Of course, 16-bit PIDs make the situation particularly tight on Unix
>> ((B') I’m not sure what the best way to handle this requirement of reaping is. %wait-process-result currently calls both functions, so it would be possible to require that asynchronously spawned processes are waited on at some point, mirroring to some extent the requirement that every C program that calls fork() should call wait() at some point. So the name would be rather appropriate even.)
> Yes, it is fair to require that asynchronous UIOP:RUN-PROGRAM users
> should always call wait-process (or whatever you call the API
> function) and not rely on the implementation doing it for them.

Okay, great.


More information about the asdf-devel mailing list