Skip to content
Draft
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
25 changes: 25 additions & 0 deletions devenv-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,31 @@ that is defined in flake-module.nix
touch $out
'';
};

run-devserver-sigterm = pkgs.stdenv.mkDerivation {
name = "run-devserver-sigterm";
src = self;
sourceRoot = "source";
nativeBuildInputs = [
(pkgs.ghc.ghc.withPackages (p: with p; [
ihp ihp-ide ihp-schema-compiler
]))
pkgs.gnumake
pkgs.postgresql
pkgs.procps
];
buildPhase = ''
export IHP_LIB=${self.packages.${system}.ihp-env-var-backwards-compat}
export IHP_STATIC=${self.packages.${system}.ihp-static}
export PS_BIN=${pkgs.procps}/bin/ps
export RUN_DEVSERVER=${pkgs.ghc.ihp-ide}/bin/RunDevServer

bash integration-test/run-devserver-sigterm-check.sh
'';
installPhase = ''
touch $out
'';
};
}

# GHC 9.12 compatibility checks (build and test all IHP packages)
Expand Down
4 changes: 4 additions & 0 deletions integration-test/.ghci
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:set -XNoImplicitPrelude
:def loadFromIHP \file -> (System.Environment.getEnv "IHP_LIB") >>= (\ihpLib -> readFile (ihpLib <> "/" <> file))
:loadFromIHP applicationGhciConfig
import IHP.Prelude
16 changes: 16 additions & 0 deletions integration-test/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Main where

import IHP.Prelude
import IHP.FrameworkConfig
import qualified IHP.Server
import IHP.Job.Types

import Config
import Web.FrontController ()
import Web.SlowLoad ()

instance Worker RootApplication where
workers _ = []

main :: IO ()
main = IHP.Server.run config
12 changes: 12 additions & 0 deletions integration-test/Web/SlowLoad.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{-# LANGUAGE TemplateHaskell #-}
module Web.SlowLoad where

import Control.Concurrent (threadDelay)
import Language.Haskell.TH.Syntax (Dec, runIO)
import Prelude (Int, pure, (*))

$(do
-- Keep GHCi in the initial :l Main.hs load long enough for the SIGTERM
-- regression check to hit the orphaning window deterministically.
runIO (threadDelay (10 * (1000000 :: Int)))
pure ([] :: [Dec]))
99 changes: 99 additions & 0 deletions integration-test/run-devserver-sigterm-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
set -euo pipefail

app_dir="$(pwd)/integration-test"
log_file="${TMPDIR:-/tmp}/run-devserver.log"
devserver_pid=""
ghci_pid=""
ps_bin="${PS_BIN:-ps}"

cleanup() {
if [ -n "${ghci_pid:-}" ] && kill -0 "$ghci_pid" 2>/dev/null; then
kill -KILL "$ghci_pid" 2>/dev/null || true
fi

if [ -n "${devserver_pid:-}" ] && kill -0 "$devserver_pid" 2>/dev/null; then
kill -KILL "$devserver_pid" 2>/dev/null || true
fi

if [ -n "${PGDATA:-}" ]; then
pg_ctl -D "$PGDATA" stop >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT

find_ghci_child() {
"$ps_bin" -axo pid=,ppid=,command= | awk -v ppid="$devserver_pid" '
$2 == ppid && $0 ~ /--interactive/ { print $1; exit }
'
}

export HOME="${TMPDIR:-/tmp}/home"
mkdir -p "$HOME"

export PGDATA="${TMPDIR:-/tmp}/pgdata"
export PGHOST="${TMPDIR:-/tmp}/pghost"
mkdir -p "$PGHOST"
initdb -D "$PGDATA" --no-locale --encoding=UTF8
echo "unix_socket_directories = '$PGHOST'" >> "$PGDATA/postgresql.conf"
echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf"
pg_ctl -D "$PGDATA" -l "${TMPDIR:-/tmp}/pg.log" start
createdb -h "$PGHOST" app

export DATABASE_URL="postgresql:///app?host=$PGHOST"
export IHP_BROWSER=true

cd "$app_dir"
"$RUN_DEVSERVER" >"$log_file" 2>&1 &
devserver_pid="$!"

for _ in $(seq 1 200); do
ghci_pid="$(find_ghci_child || true)"
if [ -n "$ghci_pid" ]; then
break
fi
sleep 0.1
done

if [ -z "$ghci_pid" ]; then
echo "RunDevServer never spawned the GHCi child" >&2
cat "$log_file" >&2
exit 1
fi

sleep 1

if grep -Eq 'modules (loaded|reloaded)\.|Server started' "$log_file"; then
echo "RunDevServer reached steady state before SIGTERM; fixture is too fast" >&2
cat "$log_file" >&2
exit 1
fi

kill -TERM "$devserver_pid"

for _ in $(seq 1 100); do
if ! kill -0 "$devserver_pid" 2>/dev/null; then
break
fi
sleep 0.1
done

if kill -0 "$devserver_pid" 2>/dev/null; then
echo "RunDevServer did not exit after SIGTERM" >&2
cat "$log_file" >&2
exit 1
fi

for _ in $(seq 1 20); do
if ! kill -0 "$ghci_pid" 2>/dev/null; then
break
fi
sleep 0.1
done

if kill -0 "$ghci_pid" 2>/dev/null; then
echo "Orphaned GHCi process survived RunDevServer SIGTERM" >&2
"$ps_bin" -o pid=,ppid=,command= -p "$ghci_pid" >&2 || true
cat "$log_file" >&2
exit 1
fi