From 2e8eba448388437b7275ce1a060ed9d1d2cbccec Mon Sep 17 00:00:00 2001 From: Michael Crumm Date: Fri, 30 Jul 2021 14:29:52 -0700 Subject: [PATCH] Wrap sass execution in a bash script to address zombie processes (#1) * Simplify path functions * Add wrapper bash script to address zombie processes when --watch is given --- CHANGELOG.md | 4 ++++ lib/dart_sass.ex | 41 ++++++++++++++++++++++++----------------- priv/dart_sass.bash | 31 +++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 17 deletions(-) create mode 100755 priv/dart_sass.bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 375a94e..01a3d99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v0.1.1 (Unreleased) + + * Add wrapper script to address zombie processes + ## v0.1.0 (2021-07-25) * First release diff --git a/lib/dart_sass.ex b/lib/dart_sass.ex index 6e4d158..db2bca7 100644 --- a/lib/dart_sass.ex +++ b/lib/dart_sass.ex @@ -89,16 +89,6 @@ defmodule DartSass do end end - @doc """ - Returns the path to the executable. - - The executable may not be available if it was not yet installed. - """ - def bin_path do - {path, _snapshot} = bin_paths() - path - end - @doc """ Returns the path to the executable and optional snapshot. @@ -130,6 +120,12 @@ defmodule DartSass do Path.join(Path.dirname(Mix.Project.build_path()), "dart") end + # TODO: Remove when dart-sass will exit when stdin is closed. + @doc false + def script_path() do + Path.join(:code.priv_dir(:dart_sass), "dart_sass.bash") + end + @doc """ Returns the version of the dart_sass executable. @@ -148,9 +144,20 @@ defmodule DartSass do end defp sass(args) do - case bin_paths() do - {sass, nil} -> {sass, args} - {vm, snapshot} -> {vm, [snapshot] ++ args} + {path, args} = + case bin_paths() do + {sass, nil} -> {sass, args} + {vm, snapshot} -> {vm, [snapshot] ++ args} + end + + # TODO: Remove when dart-sass will exit when stdin is closed. + # Link: https://github.com/sass/dart-sass/pull/1411 + cond do + "--watch" in args and not match?({:win32, _}, :os.type()) -> + {script_path(), [path] ++ args} + + true -> + {path, args} end end @@ -172,9 +179,9 @@ defmodule DartSass do stderr_to_stdout: true ] - {sass_path, args} = sass(args ++ extra_args) + {path, args} = sass(args ++ extra_args) - sass_path + path |> System.cmd(args, opts) |> elem(1) end @@ -210,7 +217,7 @@ defmodule DartSass do other -> raise "couldn't unpack archive: #{inspect(other)}" end - bin_path = DartSass.bin_path() + sass_path = DartSass.sass_path() snapshot_path = DartSass.snapshot_path() vm_path = DartSass.vm_path() @@ -224,7 +231,7 @@ defmodule DartSass do File.cp!(Path.join([tmp_dir, "dart-sass", "src", "sass.snapshot"]), snapshot_path) _ -> - File.cp!(Path.join([tmp_dir, "dart-sass", "sass"]), bin_path) + File.cp!(Path.join([tmp_dir, "dart-sass", "sass"]), sass_path) end end diff --git a/priv/dart_sass.bash b/priv/dart_sass.bash new file mode 100755 index 0000000..0f97f90 --- /dev/null +++ b/priv/dart_sass.bash @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# This script was taken from the Elixir Port guide. It is used to ensure +# graceful termination of the `sass` process when it detects that stdin +# has been closed. +# Link: https://hexdocs.pm/elixir/Port.html#module-zombie-operating-system-processes +# +# This script is required until dart-sass supports listening on stdin and +# gracefully terminating when stdin is closed. There is currently a PR for +# this behaviour: https://github.com/sass/dart-sass/pull/1411 +# +# Start the program in the background +exec "$@" & +pid1=$! + +# Silence warnings from here on +exec >/dev/null 2>&1 + +# Read from stdin in the background and +# kill running program when stdin closes +exec 0<&0 $( + while read; do :; done + kill -KILL $pid1 +) & +pid2=$! + +# Clean up +wait $pid1 +ret=$? +kill -KILL $pid2 +exit $ret