Related
I'm very new to OTP, I'm trying to create simple example to understand supervisor behaviour:
Here is simple increment server
-module( inc_serv ).
-behaviour( gen_server ).
-export( [ start/0, inc/1, stop/0 ] ).
-export( [ init/1, handle_call/3, terminate/2 ] ).
start() ->
gen_server:start_link( { local, ?MODULE }, ?MODULE, no_args, [] ).
stop() ->
gen_server:call( ?MODULE, stop ).
inc( Num ) ->
gen_server:call( ?MODULE, { num, Num } ).
init( no_args ) ->
io:format( "~p~n", [ "Increment server started :)" ] ),
{ ok, no_state }.
handle_call( { num, Num }, _From, no_state ) ->
{ reply, Num + 1, no_state };
handle_call( stop, _From, no_state ) ->
{ stop, normal, ok, no_state }.
terminate( Reason, no_state ) ->
io:format( "~p~n", [ "Increment server stopped" ] ).
And I'd like to make it supervised by this module:
-module( supervisor_inc ).
-behaviour( supervisor ).
-export( [ start/0 ] ).
-export( [ init/1 ] ).
start() ->
supervisor:start_link( { local, ?MODULE }, ?MODULE, no_args ).
init( no_args ) ->
process_flag( trap_exit, true ),
Supervisor_Spec = { one_for_one, 1, 1 },
IncServ_Spec = {
inc_serv,
{ inc_serv, start, [] },
permanent, 2000, worker, [ inc_serv ] },
{ ok, { Supervisor_Spec, [ IncServ_Spec ] } }.
After that I've performed in erlang shell next steps:
1>
1> c(inc_serv).
{ok,inc_serv}
2>
2> c(supervisor_inc).
{ok,supervisor_inc}
3>
3> supervisor_inc:start().
"Increment server started :)"
{ok,<0.43.0>}
4>
4> inc_serv:inc( 7 ).
8
5> inc_serv:inc( 8 ).
9
After this I've tried next (as I expected I've got error):
6> inc_serv:inc( bad_arg ).
"Increment server stopped"
"Increment server started :)"
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server supervisor_inc terminating
** Last message in was {'EXIT',<0.31.0>,
{{{badarith,
[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,
[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,
[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,
[{file,"shell.erl"},{line,608}]}]}}
** When Server state == {state,
{local,supervisor_inc},
one_for_one,
[{child,<0.48.0>,inc_serv,
{inc_serv,start,[]},
permanent,2000,worker,
[inc_serv]}],
undefined,1,1,
[{1345,739526,107495}],
supervisor_inc,no_args}
** Reason for termination ==
** {{{badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,608}]}]}
** exception exit: {{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
After this I've expected - my supervisor to restart inc_serv. But it didn't:
7> inc_serv:inc( 8 ).
** exception exit: {noproc,{gen_server,call,[inc_serv,{num,8}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
Could you help me to understand what happened? And how should I rewrite my supervisor, to make it able to restart inc_serv
Thanks
This is actually a kind of race condition.
As you might know, the Erlang shell itself is a normal Erlang process. When you start your supervisor from the shell, the supervisor is linked to the shell (because you use supervisor:start_link/3).
When you call your gen_server process, that process crashes (and is correctly restarted by the supervisor, as you can see by the subsequent "Increment server started :)" output).
However, at the same time, your call to gen_server:call/2 will result in the same crash (a gen_server crashing during the call will emit the same crash through the gen_server:call/2 function). This then crashes the shell process, which is linked to your supervisor, which in turn crashes with the same reason (badarith).
Basically, your supervisor is backstabbed by your shell process, after it loyally restarted your gen_server. Like so:
+---------(6)exit----------+ +---------(5)restart---------+
| | | |
| v | v
Shell ---(1)start_link---> supervisor ---(2)start_link---> gen_server
| ^ ^ | ^ | ^
| | | | | | |
| | | +---------(7)exit---------+ | |
| | | | |
| +-------------------------+--------------(4)exit------------+ |
| |
+---------------------------(3)call--------------------------------+
You can avoid this by calling catch inc_serv:inc(bad_arg). in your shell:
90> inc_serv:inc(7).
8
91> catch inc_serv:inc(bad_arg).
"Increment server stopped"
=ERROR REPORT==== 23-Aug-2012::22:10:02 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
"Increment server started :)"
{'EXIT',{{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}}
92> inc_serv:inc(7).
8
Let's say I have a process p1 and p2. When p1 ends, I also want p2 to end (p2 is waiting for an input from the user using io:get_line() when p1 ends). I can successfully end p1 by just letting the function end itself (no more code to execute), but how am I suppose to end p2 that is waiting for an input using p1?
These are the codes for the two processes:
chat1(2,Chat_Node,Name) ->
timer:sleep(100),
Message = io:get_line(io_lib:format("~s: ", [string:strip(Name,right,$\n)])),
case re:replace(Message, "\\s+", "", [global,{return,list}]) == [$b,$y,$e] of
true ->
Chat_Node ! bye;
false ->
Chat_Node ! {Name, Message},
chat1(2, Chat_Node, Name)
end.
chat2(2,Chat_Node,Name) ->
timer:sleep(100),
Message = io:get_line(io_lib:format("~s: ", [string:strip(Name,right,$\n)])),
case re:replace(Message, "\\s+", "", [global,{return,list}]) == [$b,$y,$e] of
true ->
{msg1, Chat_Node} ! bye;
false ->
{msg1, Chat_Node} ! {Name, Message},
chat2(2, Chat_Node, Name)
end.
After receiving an input from the user, the message is sent to the processes msg1 and msg2(Chat_Node). If the message is "bye", the process that has "bye" will end.
the process P1 can monitor the process P2 using the function: MonitorRef = erlang:monitor(process, P2)
Doing so, it will receive the message {'DOWN', MonitorRef, process, P2, Reason} when P2 will terminate and then perform the appropriate actions before finishing.
Note: a link wouldn't work if P2 ends normally (no more code to execute). It could work if P2 exits with another reason than normal
Edit
A small example in the shell (need to adapt in a module)
1> F = fun() -> io:format("Hello~n"),timer:sleep(2000) end.
#Fun<erl_eval.20.52032458>
2> G = fun() -> P2 = spawn(F),
2> MonitorRef = erlang:monitor(process,P2),
2> io:format("P1 waiting for P2 end~n"),
2> receive
2> {'DOWN', MonitorRef, process, P2, Reason} -> io:format("P2 finished with reason ~p~n",[Reason])
2> end,
2> io:format("P1 got message from P2~n")
2> end.
#Fun<erl_eval.20.52032458>
3> spawn(G).
P1 waiting for P2 end
Hello
<0.73.0>
P2 finished with reason normal
P1 got message from P2
4>
Edit 2
in this new example P2 get a float and transmit it to P1. if the input is a float, P2 exits with reason normal after sending the message {ok,Value} to P1 which in turn returns only Value. If the conversion from string to float fails, P2 exits with an error message which is catch by the second clause in the receive block. P1 recall itself (silly error management, just for illustration :o)
1> F = fun(From) ->
1> String = io:get_line("enter a float : "),
1> Float = list_to_float(string:strip(String,both,$\n)),
1> From ! {ok,Float}
1> end.
#Fun<erl_eval.20.52032458>
2> G = fun G() ->
2> P1 = self(),
2> P2 = spawn(fun() -> F(P1) end),
2> MonitorRef = erlang:monitor(process,P2),
2> receive
2> {ok,Float} -> Float;
2> {'DOWN', MonitorRef, process, P2, Reason} ->
2> io:format("this is not a float!"),
2> G()
2> end
2> end.
#Fun<erl_eval.20.52032458>
3> G().
enter a float : 5.2
5.2
4> G().
enter a float : hello
this is not a float!
=ERROR REPORT==== 3-Oct-2016::15:57:03 ===
Error in process <0.66.0> with exit value:
{badarg,[{erlang,list_to_float,["hello"],[]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,674}]},
{erl_eval,expr,5,[{file,"erl_eval.erl"},{line,438}]},
{erl_eval,exprs,5,[{file,"erl_eval.erl"},{line,122}]}]}
enter a float : 5.2
5.2
5>
In the code below, why is the module prime_server not loading?
-module(prime_server).
-behaviour(gen_server).
-export([new_prime/1, start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
new_prime(N) ->
%% 20000 is a timeout (ms)
gen_server:call(?MODULE, {prime, N}, 20000).
init([]) ->
%% Note we must set trap_exit = true if we
%% want terminate/2 to be called when the application
%% is stopped
process_flag(trap_exit, true),
io:format("~p starting~n" ,[?MODULE]),
{ok, 0}.
handle_call({prime, K}, _From, N) ->
{reply, make_new_prime(K), N+1}.
handle_cast(_Msg, N) -> {noreply, N}.
handle_info(_Info, N) -> {noreply, N}.
terminate(_Reason, _N) ->
io:format("~p stopping~n" ,[?MODULE]),
ok.
code_change(_OldVsn, N, _Extra) -> {ok, N}.
make_new_prime(K) ->
if
K > 100 ->
alarm_handler:set_alarm(tooHot),
N = lib_primes:make_prime(K),
alarm_handler:clear_alarm(tooHot),
N;
true ->
lib_primes:make_prime(K)
end.
OUTPUT:
$ erl -boot start_sasl
1>c(prime_server).
{ok,prime_server}
2>c(my_alarm_handler).
my_alarm_handler.erl:2: Warning: undefined callback function code_change/3 (behaviour 'gen_event')
my_alarm_handler.erl:19: Warning: variable 'Reply' is unused
{ok,my_alarm_handler}
3> {ok, Pid} = prime_server:start_link().
prime_server starting
{ok, <0.73.0>}
4> prime_server:new_prime(6).
prime_server stopping
=ERROR REPORT==== 24-Mar-2014::10:59:39 ===
** Generic server prime_server terminating
** Last message in was {prime,6}
** When Server state == 0
** Reason for termination ==
** {'module could not be loaded',
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
=CRASH REPORT==== 24-Mar-2014::10:59:39 ===
crasher:
initial call: prime_server:init/1
pid: <0.73.0>
registered_name: prime_server
exception exit: {undef,
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,
[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]}
in function gen_server:terminate/6 (gen_server.erl, line 744)
ancestors: [<0.71.0>]
messages: []
links: [<0.71.0>]
dictionary: []
trap_exit: true
status: running
heap_size: 610
stack_size: 27
reductions: 181
neighbours:
neighbour: [{pid,<0.71.0>},
{registered_name,[]},
{initial_call,{erlang,apply,2}},
{current_function,{gen,do_call,4}},
{ancestors,[]},
{messages,[]},
{links,[<0.27.0>,<0.73.0>]},
{dictionary,[]},
{trap_exit,false},
{status,waiting},
{heap_size,610},
{stack_size,36},
{reductions,1303}]
** exception exit: undef
in function lib_primes:make_prime/1
called as lib_primes:make_prime(6)
in call from prime_server:handle_call/3 (prime_server.erl, line 21)
in call from gen_server:handle_msg/5 (gen_server.erl, line 585)
in call from proc_lib:init_p_do_apply/3 (proc_lib.erl, line 239)
9> gen_server:call(prime_server, 6).
** exception exit: {noproc,{gen_server,call,[prime_server,6]}}
in function gen_server:call/2 (gen_server.erl, line 180)
10> gen_server:call(prime_server, new_prime, 6).
** exception exit: {noproc,{gen_server,call,[prime_server,new_prime,6]}}
in function gen_server:call/3 (gen_server.erl, line 188)
11> gen_server:call(prime_server, new_prime(5), []).
** exception error: undefined shell command new_prime/1
12> gen_server:call(prime_server, {prime, 4}, []).
** exception exit: {{function_clause,
[{gen,call,
[prime_server,'$gen_call',{prime,4},[]],
[{file,"gen.erl"},{line,147}]},
{gen_server,call,3,
[{file,"gen_server.erl"},{line,184}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,573}]},
{shell,exprs,7,[{file,"shell.erl"},{line,674}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,629}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,614}]}]},
{gen_server,call,[prime_server,{prime,4},[]]}}
{'module could not be loaded',
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
From the log, you use this module lib_primes, do you have this file lib_primes.erl?
I think you should compiule and load this module lib_primes first.
I'm very new to OTP, I'm trying to create simple example to understand supervisor behaviour:
Here is simple increment server
-module( inc_serv ).
-behaviour( gen_server ).
-export( [ start/0, inc/1, stop/0 ] ).
-export( [ init/1, handle_call/3, terminate/2 ] ).
start() ->
gen_server:start_link( { local, ?MODULE }, ?MODULE, no_args, [] ).
stop() ->
gen_server:call( ?MODULE, stop ).
inc( Num ) ->
gen_server:call( ?MODULE, { num, Num } ).
init( no_args ) ->
io:format( "~p~n", [ "Increment server started :)" ] ),
{ ok, no_state }.
handle_call( { num, Num }, _From, no_state ) ->
{ reply, Num + 1, no_state };
handle_call( stop, _From, no_state ) ->
{ stop, normal, ok, no_state }.
terminate( Reason, no_state ) ->
io:format( "~p~n", [ "Increment server stopped" ] ).
And I'd like to make it supervised by this module:
-module( supervisor_inc ).
-behaviour( supervisor ).
-export( [ start/0 ] ).
-export( [ init/1 ] ).
start() ->
supervisor:start_link( { local, ?MODULE }, ?MODULE, no_args ).
init( no_args ) ->
process_flag( trap_exit, true ),
Supervisor_Spec = { one_for_one, 1, 1 },
IncServ_Spec = {
inc_serv,
{ inc_serv, start, [] },
permanent, 2000, worker, [ inc_serv ] },
{ ok, { Supervisor_Spec, [ IncServ_Spec ] } }.
After that I've performed in erlang shell next steps:
1>
1> c(inc_serv).
{ok,inc_serv}
2>
2> c(supervisor_inc).
{ok,supervisor_inc}
3>
3> supervisor_inc:start().
"Increment server started :)"
{ok,<0.43.0>}
4>
4> inc_serv:inc( 7 ).
8
5> inc_serv:inc( 8 ).
9
After this I've tried next (as I expected I've got error):
6> inc_serv:inc( bad_arg ).
"Increment server stopped"
"Increment server started :)"
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server supervisor_inc terminating
** Last message in was {'EXIT',<0.31.0>,
{{{badarith,
[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,
[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,
[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,
[{file,"shell.erl"},{line,608}]}]}}
** When Server state == {state,
{local,supervisor_inc},
one_for_one,
[{child,<0.48.0>,inc_serv,
{inc_serv,start,[]},
permanent,2000,worker,
[inc_serv]}],
undefined,1,1,
[{1345,739526,107495}],
supervisor_inc,no_args}
** Reason for termination ==
** {{{badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,608}]}]}
** exception exit: {{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
After this I've expected - my supervisor to restart inc_serv. But it didn't:
7> inc_serv:inc( 8 ).
** exception exit: {noproc,{gen_server,call,[inc_serv,{num,8}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
Could you help me to understand what happened? And how should I rewrite my supervisor, to make it able to restart inc_serv
Thanks
This is actually a kind of race condition.
As you might know, the Erlang shell itself is a normal Erlang process. When you start your supervisor from the shell, the supervisor is linked to the shell (because you use supervisor:start_link/3).
When you call your gen_server process, that process crashes (and is correctly restarted by the supervisor, as you can see by the subsequent "Increment server started :)" output).
However, at the same time, your call to gen_server:call/2 will result in the same crash (a gen_server crashing during the call will emit the same crash through the gen_server:call/2 function). This then crashes the shell process, which is linked to your supervisor, which in turn crashes with the same reason (badarith).
Basically, your supervisor is backstabbed by your shell process, after it loyally restarted your gen_server. Like so:
+---------(6)exit----------+ +---------(5)restart---------+
| | | |
| v | v
Shell ---(1)start_link---> supervisor ---(2)start_link---> gen_server
| ^ ^ | ^ | ^
| | | | | | |
| | | +---------(7)exit---------+ | |
| | | | |
| +-------------------------+--------------(4)exit------------+ |
| |
+---------------------------(3)call--------------------------------+
You can avoid this by calling catch inc_serv:inc(bad_arg). in your shell:
90> inc_serv:inc(7).
8
91> catch inc_serv:inc(bad_arg).
"Increment server stopped"
=ERROR REPORT==== 23-Aug-2012::22:10:02 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
"Increment server started :)"
{'EXIT',{{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}}
92> inc_serv:inc(7).
8
i found example of mutex in erlang
Can somebody modify it to use it with count like semaphore?
-module(mutex).
-export([start/0, stop/0]).
-export([wait/0, signal/0]).
-export([init/0]).
start() ->
register(mutex, spawn(?MODULE, init, [])).
stop() ->
mutex ! stop.
wait() ->
mutex ! {wait, self()},
receive ok -> ok end.
signal() ->
mutex ! {signal, self()},
ok.
init() ->
free().
free() ->
receive
{wait, Pid} ->
Pid ! ok,
busy(Pid);
stop ->
terminate()
end.
busy(Pid) ->
receive
{signal, Pid} ->
free()
end.
terminate() ->
receive
{wait, Pid} ->
exit(Pid, kill),
terminate()
after
0 -> ok
end.
Thats not the best semaphore.. could use some random stuff, but hopefully you will get the idea.
-module(sem1).
-export([semaphor/2, start/1, start/0]).
semaphor(0,0) ->
io:format("err");
semaphor(0,Full)->
receive
{increment,PID} ->
io:format("UP ~w ~w~n", [0,PID]),
PID ! {incGranted},
semaphor(1,Full)
end;
semaphor(Full,Full)->
receive
{decrement,PID} ->
io:format("DOWN ~w ~w~n", [Full,PID]),
PID ! {decGranted},
semaphor(Full -1 , Full)
end;
semaphor(N,Full)->
receive
{decrement,PID} ->
io:format("DOWN ~w ~w~n", [N,PID]),
PID ! {decGranted},
semaphor(N -1 , Full);
{increment,PID} ->
io:format("UP ~w ~w~n", [N,PID]),
PID ! {incGranted},
semaphor(N + 1 , Full)
end.
start(PID) ->
PID ! {decrement, self()},
receive
{decGranted} -> true
end,
timer:sleep(100),
PID ! {increment, self()},
receive
{incGranted} -> true
end,
start(PID).
start() ->
PID = spawn(sem1, semafor, [1,5]),
spawn(sem1,start,[PID]),
spawn(sem1,start,[PID]),
spawn(sem1,start,[PID]),
spawn(sem1,start,[PID]),
spawn(sem1,start,[PID]),
spawn(sem1,start,[PID]).