How to make a call to the current process in GenServer?

I know that I can name GenServer as follows

GenServer.call(pid, request) # using a pid 

or how is it

 GenServer.call(registered_name, request) # if I registered the process with a name 

But is there a way to release GenServer.call inside the module without knowing the pid / registered name? (i.e. is there something like GenServer.call(__CURRENT_PROCESS__, request) ?)

+5
source share
4 answers

It just won't work. A GenServer.call simply sends a message to this process, and then waits for another message (response) to block the current process. If you send a message to self , this way, the process will not be free to process this message, because it will be blocked waiting for a response. That way, call will always be absent.

I suspect that you just need to extract the functionality you want into the function and directly call it. Alternatively, you can send cast to the current process, effectively telling it to do something "later."

+5
source

I'm not sure the best way to go, but what you are looking for is Kernel.self / 0

EDIT:

According to Sasha, the code will look like this:

 GenServer.call(self, request) 

EDIT:

At the excellent point made by Pavel Obrok, you should never call the current process. If you need to send a message to the current process, you must do this in this fasion:

 GenServer.cast(self, request) 

NB is a casting versus a call.

+4
source

It depends. If you run only one GenServer process per node, you can call it like this:

 @doc """ If you want to call the server only from the current module. """ def local_call(message) do GenServer.call(__MODULE__, message) end 

or

 @doc """ If you want to call the server from another node on the network. """ def remote_call(message, server \\ nil) do server = if server == nil, do: node(), else: server GenServer.call({__MODULE__, server}, message) end 

If you have several processes from the same module, you need an additional identifier (for example, if you have a supervisor strategy :simple_one_for_one to generate a GenServer on demand).

For something like this, I would recommend using:

  • :gproc to name the processes.
  • :ets if you need more information to identify your processes.

:gproc is awesome and works with GenServer . You usually name your processes using an atom as a registered name. :gproc allows you to call your processes an arbitrary member, i.e. tuple.

Say, in my function call, I have a complex identifier for my server, for example {:id, id :: term} , where id can be a string, for example. I could start my GenServer as:

 defmodule MyServer do use GenServer def start_link(id) do name = {:n, :l, {:id, id}} GenServer.start_link(__MODULE__, %{}, name: {:via, :gproc, name}) end (...) end 

And look at my process for something other than an atom, as I said, for example, on a string. Therefore, if I start my server, for example:

 MyServer.start_link("My Random Hash") 

And I have a function like:

 def f(id, message) do improved_call(id, message) end defp improved_call(id, message, timeout \\ 5000) do name = {:n, :l, {:id, id}} case :gproc.where(name) do undefined -> :error pid -> GenServer.call(pid, message, timeout) end 

You can use it to call processes, such as:

 MyServer.f("My Random Hash", {:message, "Hello"}) 

If the naming process is more complex, you can use :ets to store a more complex state.

If you want to use :gproc , you can add it to your mix.exs file, for example:

 (...) defp deps do [{:gproc, github: "uwiger/gproc"}] end (...) 

On a side note, never call GenServer.call/3 from handle_call/3 . It will time out and do DOS on other GenServer.call . handle_call/3 processes one message at a time.

+2
source

From what I understand from your comment, you are trying to write a public API function for your GenServer instead of creating a call from the GenServer process. AFAIK there is no way to do this without knowing the PID. However, for GenServers , from which you create only one instance, there is an idiom for this case: you name a single instance of your GenServer name of the module that implements it. This can be done easily with the __MODULE__ macro:

 def start_link do GenServer.start_link(__MODULE__, nil, name: __MODULE__) end 

And then in your API functions, you simply use __MODULE__ instead of PID:

 def api_function do GenServer.call(__MODULE__, :api_function) end 

This property has the good property that __MODULE__ will always display the correct name of the closing module, even if you rename it.

+1
source

Source: https://habr.com/ru/post/1242880/


All Articles