Accessing a gen_server function from another process

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Accessing a gen_server function from another process

asdf asdf
Hello,

I am learning about gen_servers right now and I am confused on what I should do about calling a gen_server function that requires a pid.

Generally, gen_server:start_link returns a tuple of the form {ok, Pid}, where Pid is used for API calls to the server. Now, Erlang doesn’t have any good way of storing state, such as a variable to hold this Pid. So, If I have a function that every once in a while needs to call this API, but that API needs a Pid, and theres no way to get a Pid without starting a whole new server… how is this done?

I am sure the answer is not too complex, I greatly appreciate your replies as I am just learning Erlang(on ch 14 of Learn you some Erlang for Great Good).
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Accessing a gen_server function from another process

Martin Karlsson-3

>Generally, gen_server:start_link returns a tuple of the form {ok, Pid}, where Pid is used for API calls to the server. Now, Erlang doesn’t have any good way of storing state, such as a variable to hold this Pid. So, If I have a function that every once in a while needs to call this API, but that API needs a Pid, and theres no way to get a Pid without starting a whole new server… how is this done?
>

Hi,

Normally this is done in one of two ways (which really are the same but
one is built-in).

1) You register the process with a unique name which is an atom. Either
by yourself or by starting the gen_server as a named server.

gen_server:start_link({local, my_name}, Module, Args, Options).

Then:

gen_server:call(my_name, hello).

You can also register a pid with a name using erlang:register/2.

{ok, Pid} = gen_server:start_link(Module, Args, Options),
true = register(my_name, Pid).

This is normally used when you have static gen_servers which you want to
call throughout your code without caring about the pid.


2) You store the pid in a lookup registry of some sort and then lookup
the pid before you call your gen_server. This is often done if you have
more dynamic processes and process names or want to use something
different than an atom to lookup the pid.

The lookup registry in itself can be a named process (as per 1) where
you can register and lookup your pids. It is a pretty good exercise to
implement it.

For example:

{ok, Pid} = gen_server:start_link(Module, Args, Options),
ok = my_registry:register(<<"whatevername">>, Pid)

To call:

{ok, Pid} = my_registry:lookup(<<"whatevername">>),
gen_server:call(Pid, hello).

There are a number of process registry libraries out there; gproc
(https://github.com/uwiger/gproc) being one of the more well known. I'd
start by implementing my own though as it gives you a better
understanding how things work.

I haven't looked it up but I think that learnyousomeerlang will bring up
this subject too.
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Accessing a gen_server function from another process

Vance Shipley
In reply to this post by asdf asdf
On Fri, Jul 28, 2017 at 3:39 AM, code wiget <[hidden email]> wrote:
> Generally, gen_server:start_link returns a tuple of the form {ok, Pid}, where Pid is used for API calls to the server. Now, Erlang doesn’t have any good way of storing state, such as a variable to hold this Pid. So, If I have a function that every once in a while needs to call this API, but that API needs a Pid, and theres no way to get a Pid without starting a whole new server… how is this done?

In functional programming a function gets what it needs to know from
it's arguments. The way you store state is to keep passing it around,
like juggling balls.

     f() ->
          {ok, Pid} = gen_server:start(my_server, [], []),
          f1(PId).

    f1(Pid) ->
          ...
          f2(A, B, Pid).

    f2(A, B, Pid) ->
         ...

OTP behaviour callbacks have a State argument which does just that, it
keeps the state data in the air between calls. Need more than item of
state? Make State a tuple(), map(), gb_tree:tree(), etc..

The easy way out is to register a process so other's can find it
however that is not functional style. I prefer to arrange for each
process to learn what it needs and keep it in state. A common design
pattern is a supervisor with two children: a gen_server and a
simple_one_for_one supervisor. The gen_server receives requests and
calls supervisor:start_child/2 to start a worker, under it's sibling
supervisor, to handle the request. You could register the supervisor
so the server can use a hard coded name in start_child/2 but the
lookup will need to be done each time. Instead I have the server call
supervisor:which_children/1, to locate it's siblings, once after
initialization. This requires that the server know the pid() of it's
supervisor. How does it know that? You guessed it, it's passed as an
argument from it's parent supervisor in gen_server:start_link/3.

Many will argue that I'm being overly strict above, and that
registered processes are just fine. Sure, but embracing functional
style isn't that hard either.


--
     -Vance
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions