concurrencyerlangelixirspawn

Why does this Elixir script exit before completing its job?


I wrote a module SC in my Elixir script, wget.exs. The module SC utilizes Erlang's OS module to execute complex commands and print their output on the screen. I use the module in my script to download many packages at once with wget:

#!/usr/bin/env elixir

defmodule SC do
  def run(cmmnd) do
    output = :os.cmd(String.to_charlist(cmmnd))    
    IO.puts output
  end
end

defmodule Downloader do
  def download_packages do
    lst_f_pckgs = "wget-list-sysv"

    #System.cmd("wget", ["https://www.linuxfromscratch.org/lfs/view/stable/#{lst_f_pckgs}"])
    SC.run("wget -c https://www.linuxfromscratch.org/lfs/view/stable/#{lst_f_pckgs}")
    
    {:ok, file_content} = File.read(lst_f_pckgs)
    arr_f_pckgs = String.split(file_content, ~r/\s+/)

    tasks =
      arr_f_pckgs
      |> Enum.map(fn l ->
        #Task.async(fn -> System.cmd("wget", [l]) end)
        Task.async(fn -> SC.run("wget -c #{l}") end)
      end)

    tasks
    |> Enum.each(&Task.await/1)

    IO.puts "All of them are downloaded."
  end
end

Downloader.download_packages()


The script is meant fist to download a text file that contains a list of packages to download, this part of the script works fine, and then in meant to proceed to download all the packages in parallel. When utilizing my module SC the scripts starts downloading but then it exits and I get the following output:

  5550K .......... .......... .......... .......... .......... 98% 11.6M 0s
  5600K .......... .......... .......... .......... .......... 99%  457K 0s
  5650K .......... .......... ..                              100%  145M=8.1s

2024-01-15 13:38:05 (702 KB/s) - ‘coreutils-9.3.tar.xz’ saved [5808696/5808696]


--2024-01-15 13:37:56--  https://github.com/systemd/systemd/archive/v254/systemd-254.tar.gz
Resolving github.com (github.com)... 140.82.121.3
Connecting to github.com (github.com)|140.82.121.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/systemd/systemd/tar.gz/refs/tags/v254 [following]
--2024-01-15 13:37:56--  https://codeload.github.com/systemd/systemd/tar.gz/refs/tags/v254
Resolving codeload.github.com (codeload.github.com)... 140.82.121.10
Connecting to codeload.github.com (codeload.github.com)|140.82.121.10|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/x-gzip]
Saving to: ‘systemd-254.tar.gz’

     0K .......... .......... .......... .......... ..........  267K
    50K .......... .......... .......... .......... .......... 27.2M
   100K .......... .......... .......... .......... ..........  442K
   150K .......... .......... .......... .......... ..........  202K
[many similar lines]
8.10M 0s
  6200K .......... .......... .......... .......... .......... 97% 31.7M 0s
  6250K .......... .......... .......... .......... .......... 98% 1.48M 0s
  6300K .......... .......... .......... .......... .......... 98%  700K 0s
  6350K .......... .......... .......... .......... .......... 99% 5.73M 0s
  6400K .......... .......... .......                         100%  460M=10s

2024-01-15 13:38:07 (639 KB/s) - ‘grub-2.06.tar.xz’ saved [6581924/6581924]


** (exit) exited in: Task.await(%Task{mfa: {:erlang, :apply, 2}, owner: #PID<0.96.0>, pid: #PID<0.110.0>, ref: #Reference<0.1214714622.3043557380.75212>}, 5000)
    ** (EXIT) time out
    (elixir 1.14.0) lib/task.ex:830: Task.await/2
    (elixir 1.14.0) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
    wget.exs:28: Downloader.download_packages/0

Whereas if I utilize Elixir's System module the script does its job and prints "All of them are downloaded."` to the screen.


Solution

  • The error returned is:

    exited in: Task.await(..., 5000)
    ** (EXIT) time out
    

    The default timeout for Task.await is 5 seconds. Presumably downloading a bunch of files takes longer than that.

    Increase the timout to some reasonable value for your use case or :infinity if you don't want to timeout.

    |> Enum.each(&Task.await(&1, :infinity))