DOC add lots of notes to my future self

This commit is contained in:
Nathan Dwarshuis 2022-12-29 13:36:26 -05:00
parent 6689a53585
commit 964ec02569
1 changed files with 53 additions and 3 deletions

View File

@ -1,7 +1,57 @@
{-# LANGUAGE OverloadedStrings #-}
--------------------------------------------------------------------------------
-- | Functions for formatting and spawning shell commands
--
-- TLDR: spawning a "command" in xmonad is complicated for weird reasons, and
-- this solution is the most sane (for me) given the constraints of the xmonad
-- codebase.
--
-- A few facts about xmonad (and window managers in general):
-- 1) It is single-threaded (since X is single threaded)
-- 2) Because of (1), it ignores SIGCHLD, which means any subprocess started
-- by xmonad will instantly be reaped after spawning. This guarantees the
-- main thread running the WM will never be blocked.
--
-- In general, this means that 'System.Process.waitForProcess' (and similar)
-- will not work since these call wait() on the child process, which will fail
-- because the child has already been cleared and thus there is nothing on which
-- to wait. By extension this also means we don't have access to a child's exit
-- code.
--
-- XMonad and contrib use their own method of spawning subprocesses using the
-- extremely low-level 'System.Process.Posix' API. See the code for
-- 'XMonad.Core.spawn' or 'XMonad.Util.Run.safeSpawn'. Specifically, the
-- sequence is (in terms of the low level Linux API):
-- 1) call fork()
-- 2) uninstall signal handlers
-- 3) call setsid()
-- 4) start new thing with exec()
--
-- In practice, I'm guessing the main reason for 2 and 3 is so that child
-- processes don't inherit the weird SIGCHLD behavior of xmonad itself. The
-- setsid thing is one way to guarantee that killing the child thread will also
-- kill its children (if any). Note that this obviously will not block since
-- we are calling fork() without wait() (which would throw an error anyways).
--
-- What if I actually want the exit code?
--
-- The best solution (I can come up with), is to use bracket to uninstall
-- handlers, run process (with wait), and then reinstall handlers. I can use
-- this with a much higher-level interface which will make things easier. This
-- obviously means that if the process is running in the main thread, it needs
-- to be almost instantaneous (since it actually will be blocking). NOTE: I
-- shouldn't use this to replace the existing functions in xmonad since
-- 'spawning' a new process in a non-blocking manner with a higher-level API
-- will produce lots of Haskell objects that need to be cleaned, and it will be
-- hard (perhaps impossible) to keep track and deal with these after spawning.
--
-- This works, albeit with the cost of using almost every process API in Haskell.
--
-- Briefly:
-- 1) 'System.Process.Posix' (where xmonad lives)
-- 2) 'System.Process' (wraps 1)
-- 2) 'System.Process.Typed' (wraps 2, which I prefer for getting exit codes)
-- 3) 'RIO.Process' (wraps 3, which I prefer at the app level)
{-# LANGUAGE OverloadedStrings #-}
module XMonad.Internal.Shell
( fmtCmd