dc0d

dc0d

Stale/Deadlock Process

Assume there is a function like:

def sample do
  lab_pid = self()

  spawn_link(fn ->
    spawn_link(fn ->
      Process.flag(:trap_exit, true)

      receive do
        {:EXIT, _pid, :normal} ->
          send(lab_pid, :done)
      end
    end)

    :timer.sleep(100)
  end)

  receive do
    msg ->
      msg
  end
end

This works. But if we remove the sleep, it becomes stale/deadlock. Why?

Most Liked

sasajuric

sasajuric

Author of Elixir In Action

I wouldn’t call it a deadlock, but rather a race condition. Too see why it happens, let’s first name these processes:

def sample do
  # process A
  lab_pid = self()

  spawn_link(fn ->
    # process B

    spawn_link(fn ->
      # process C

      Process.flag(:trap_exit, true)

      receive do
        {:EXIT, _pid, :normal} ->
          send(lab_pid, :done)
      end
    end)
  end)

  receive do
    msg ->
      msg
  end
end

So without a timeout, it’s possible that process B stops before Process.flag in process C has been invoked. This happens because spawn and spawn_link are asynchronous. When these functions return, the process has been created, but it’s possible that they still haven’t executed any instruction.

So in this scenario, if process C starts trapping exits after process B had stopped, the corresponding exit signal won’t be converted into a message. So now, you have process C waiting forever for a message which will never arrive, and consequently, process A is also waiting forever for a message which process C will never send.

The solution would be to use synchronous start, e.g. with :proc_lib:

def sample do
  lab_pid = self()

  spawn_link(fn ->
    :proc_lib.start_link(Kernel, :apply, [fn ->
      Process.flag(:trap_exit, true)

      # `:proc_lib.start_link` will return after this function is invoked
      :proc_lib.init_ack({:ok, self()})

      receive do
        {:EXIT, _pid, :normal} ->
          send(lab_pid, :done)
      end
    end, []])
  end)

  receive do
    msg ->
      msg
  end
end
sasajuric

sasajuric

Author of Elixir In Action

This is what :proc_lib.start_link and :proc_lib.init_ack do under the hood too :slight_smile: See here and here.

OvermindDL1

OvermindDL1

This is also a good example as to why monitors are often better then links+traps. :slight_smile:

OvermindDL1

OvermindDL1

Oh yes, that, I was reading it as the other way (that the parent would die when it’s the parent calling monitor on the child)! Yes, that is why monitor’s should be used, they are always ‘safe’, you will always receive a message of :DOWN, even if it went down before the monitor was started. Monitors are much better than links for watching process lifetimes, links should only be used to actually link and die.

Where Next?

Popular in Questions Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
Kagamiiiii
Student & New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
mathew4509
I have a list say x = ["23gh", "56kh", "97mh"] I would like to pass each element to Val in each iteration. Say, in iteration 1 -------...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Other popular topics Top

Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement