How do I calculate the time in which the process completes the request - erlang

First of all thank you for looking at my question. Create a server that manages a list of available frequencies(say PROCESS [1,2,3,4,5,6,7,8,9,10]. The client process should be able to request any available frequency and release it. When the client releases the frequency, the server would calculate the amount (5 Rs/Sec) based upon the time the frequency was used by the client and would send the bill to the client.
I really do appreciate your help even if you can’t solve. I have been trying this for 1 whole month and I’m honestly so down and hopeless. Thank you so much.
-module(b).
-compile([export_all, nowarn_export_all]).
run() ->
file:delete("example.dets"),
case dets:info(example, owner) of
undefined ->
dets:open_file(example, [{file, "example.dets"}]);
_ ->
skip
end,
spawn(fun() -> init() end),
timer:sleep(100),
Client = self(),
example_server ! {allocate, Client, aaa, 5},
example_server ! {allocate, Client, bbb, 5},
example_server ! {allocate, Client, aaa, 5},
timer:sleep(1000),
example_server ! {release, Client, aaa, 5},
example_server ! {release, Client, aaa, 5},
timer:sleep(100),
io:format("~p~n", [receive_server_msg([])]),
example_server ! {allocate, Client, aaa, 5},
timer:sleep(100),
exit(whereis(example_server), kill),
spawn(fun() -> init() end),
timer:sleep(100),
example_server ! {allocate, Client, bbb, 5},
example_server ! {allocate, Client, aaa, 5},
timer:sleep(1000),
example_server ! {release, Client, aaa, 5},
example_server ! {release, Client, aaa, 5},
timer:sleep(100),
io:format("~p~n", [receive_server_msg([])]),
example_server ! {stop},
dets:close(example).
receive_server_msg(List) ->
receive
Reply ->
receive_server_msg([Reply|List])
after 0 ->
lists:reverse(List)
end.
init() ->
erlang:register(example_server, self()),
State = other_data_you_need,
manager(State).
manager(State) ->
receive
{stop} -> ok;
{allocate, Requestor, Name, Frequency} ->
case check_allocate_request_and_modify_state(Name, Frequency, State) of
{ok, State1} ->
Requestor ! {grant, Frequency},
manager(State1);
{error, Reason} ->
Requestor ! {error, Reason},
manager(State)
end;
{release, Requestor, Name, Frequency} ->
case check_release_request_and_modify_state(Name, Frequency, State) of
{ok, Cost, State1} ->
Requestor ! {bill, Cost},
manager(State1);
{error, Reason} ->
Requestor ! {error, Reason},
manager(State)
end;
UnknownMsg ->
logger:error("unknown message: ~p", [UnknownMsg]),
manager(State)
end.
check_allocate_request_and_modify_state(Name, Frequency, State) ->
case lists:member(Frequency, [1,2,3,4,5,6,7,8,9,10]) of
true ->
case dets:lookup(example, Frequency) of
[] ->
dets:insert(example, {Frequency, Name, erlang:system_time(second)}),
{ok, State};
[{_, Name, _}] ->
{error, using};
_ ->
{error, other_using}
end;
_ ->
{error, bad_request}
end.
check_release_request_and_modify_state(Name, Frequency, State) ->
case dets:lookup(example, Frequency) of
[{_, Name, StartTime}] ->
dets:delete(example, Frequency),
Cost = (erlang:system_time(second) - StartTime),% * Price,
{ok, Cost, State};
_ ->
{error, not_using}
end.
If possible could you help me correct this and help me with the right format or right answer.

Related

Testing a gen_server module using Common Test

I have this (very simple) gen_server implementation:
-module(rand_gen).
-behaviour(gen_server).
-define(BASE, 1000).
%% Module Functionality
-export([start/0]).
-export([stop/1]).
-export([uniform/1, uniform/2]).
%% Callback Functions
-export([code_change/3]).
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
%%%=======================================================================
%%% Export
%%%=======================================================================
-spec start() -> {ok, pid()} | {error, Reason :: term()}.
start() ->
gen_server:start(?MODULE, [], []).
-spec stop(Pid) -> ok when Pid :: pid().
stop(Pid) ->
gen_server:stop(Pid).
-spec uniform(Pid, Max) -> number() when Pid :: pid(), Max :: non_neg_integer().
uniform(Pid, Max) ->
gen_server:call(Pid, {uniform, #{max => Max}}).
-spec uniform(Pid) -> number() when Pid :: pid().
uniform(Pid) ->
gen_server:call(Pid, uniform).
%%%=======================================================================
%%% Callback Functions
%%%=======================================================================
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handle_call({uniform, Input = #{max := Max}}, _From, _State) ->
{reply, rand:uniform(Max), Input};
handle_call(uniform, _From, State = #{max := Max}) ->
{reply, rand:uniform(Max), State};
handle_call(Msg, From, State) ->
io:format("Unexpected call [~p] from [~p]~n", [Msg, From]),
{reply, {error, no_call, Msg}, State}.
handle_cast(Msg, State) ->
io:format("Unexpected cast [~p]~n", [Msg]),
{noreply, State}.
handle_info(Msg, State) ->
io:format("Unexpected message ~p~n", [Msg]),
{noreply, State}.
init([]) ->
io:format("Crypto strong seed initialized! Using [~p] as base value.~n", [?BASE]),
<<I1:32/unsigned-integer, I2:32/unsigned-integer, I3:32/unsigned-integer>> = crypto:strong_rand_bytes(?BASE),
_ = rand:seed(exsplus, {I1, I2, I3}),
{ok, #{max => ?BASE}}.
terminate(_Reason, _StateData) ->
ok.
%%%=======================================================================
%%% Internal
%%%=======================================================================
Now I want to write a Common Test suite for testing it ‒ I just started learning this.
-module(rand_gen_SUITE).
-include_lib("common_test/include/ct.hrl").
-compile(export_all).
%%%=======================================================================
%%% Setup
%%%=======================================================================
all() ->
[
{group, generate}
].
groups() ->
[
{generate,
%%[parallel, {repeat, 3}],
[uniform]
}
].
init_per_suite(Config) ->
{ok, Pid} = rand_gen:start(),
[{pid, Pid} | Config].
end_per_suite(_Config) ->
ok.
%%%=======================================================================
%%% Test Cases
%%%=======================================================================
uniform(Config) ->
Pid = ?config(pid, Config),
true = is_number(rand_gen:uniform(Pid)).
...but every time I run the (super-small) suite/file I get back a weird message saying:
===> Running Common Test suites...
%%% rand_gen_SUITE ==> init_per_suite: FAILED
%%% rand_gen_SUITE ==> {{badmatch,<<3,217,51,126,122,17,213,209,222,82,29,106,213,128,218,98,129,12,
123,22,27,194,123,120,74,110,1,254,132,243,147,87,181,74,43,159,
163,94,58,224,18,40,46,190,134,198,43,225,57,81,109,216,64,245,
103,131,46,47,234,165,210,2,78,97,52,44,131,49,0,94,168,207,176,
214,38,69,235,105,116,240,92,37,112,17,16,47,86,0,179,4,167,61,
61,167,88,84,227,247,74,132,86,64,33,92,175,51,130,242,155,174,
167,126,142,100,123,35,77,159,198,113,105,182,43,61,130,173,169,
155,135,0,6,19,90,65,139,70,236,217,253,76,223,86,228,175,145,
198,89,40,251,158,6,193,177,66,202,199,110,39,130,232,28,37,176,
215,45,251,65,220,229,124,204,99,126,132,119,41,83,226,235,117,
152,69,179,211,10,97,194,128,147,134,26,240,175,218,188,204,82,
55,34,35,49,195,183,78,150,125,137,141,76,92,253,1,46,203,37,142,
224,236,8,252,94,129,89,108,84,133,88,213,56,127,255,48,172,26,
246,51,29,79,56,33,59,122,135,80,137,63,37,124,168,90,76,46,12,
155,241,182,16,218,147,227,16,110,9,51,213,74,216,18,51,219,12,
111,229,159,231,33,12,87,114,134,113,79,147,35,122,227,114,154,
52,164,223,95,66,162,136,231,174,47,93,10,66,62,63,76,196,232,
100,240,49,16,122,81,200,222,66,180,212,16,185,117,31,111,152,
216,10,125,212,138,203,219,31,67,94,170,181,160,225,189,182,10,
110,207,197,177,198,199,83,227,121,235,12,186,90,212,160,102,187,
99,61,127,123,255,76,36,63,190,197,175,167,175,132,230,187,162,
68,183,220,9,198,29,18,191,199,182,10,227,139,38,25,113,153,199,
80,244,13,55,62,153,250,165,218,137,211,210,129,123,35,67,226,20,
153,85,25,193,46,53,40,49,134,67,92,198,106,151,195,242,46,153,
223,187,163,100,205,108,253,191,192,220,65,101,15,32,161,41,25,
148,203,153,134,55,33,88,107,222,49,120,44,173,167,82,238,185,
213,107,164,45,64,28,180,253,34,131,92,49,112,202,188,1,36,59,81,
223,0,200,93,34,253,3,214,240,211,51,135,43,70,105,98,125,186,
131,129,85,246,147,167,238,32,176,54,92,250,200,73,63,246,72,159,
64,105,119,246,58,5,153,54,126,4,33,82,153,245,79,163,187,61,45,
172,173,30,45,148,252,230,74,80,213,132,45,52,202,101,234,98,30,
68,18,117,175,231,41,153,243,243,132,184,98,234,72,134,140,229,
239,205,136,228,223,52,189,118,250,195,178,126,33,56,142,247,242,
235,160,129,75,221,73,21,148,139,110,194,87,127,147,9,71,25,111,
218,6,206,38,3,208,154,128,236,127,96,155,223,25,164,139,80,170,
82,230,130,118,88,198,254,109,249,252,146,143,6,108,134,49,29,
152,215,67,74,96,240,109,6,97,36,145,250,234,48,145,17,144,217,
198,236,60,68,213,30,7,120,228,117,41,231,72,208,245,73,153,251,
64,195,16,201,250,12,143,23,138,23,215,74,5,180,228,133,236,61,
227,123,188,100,81,77,254,187,69,213,127,22,38,143,84,184,4,47,
47,124,245,230,221,105,105,36,220,28,52,200,195,104,7,78,155,250,
36,8,42,228,166,212,106,147,247,101,21,230,78,236,91,217,115,84,
76,125,193,18,136,103,85,104,33,102,7,235,250,197,139,238,187,
154,12,84,152,174,133,92,165,72,76,45,70,90,115,32,161,156,251,
197,103,70,141,168,220,223,251,252,14,249,172,40,122,53,69,137,
45,29,129,144,146,238,181,104,28,117,166,19,238,44,162,174,117,
250,21,62,155,39,149,221,207,48,168,19,35,209,53,188,31,122,99,
83,74,138,164,103,72,106,27,175,239,240,3,100,118,152,180,225,82,
227,60,108,66,223,165,173,183,46,166,23,85,236,141,238,166,160,
114,31,7,14,23,220,90,226,79,217,203,240,126,128,241,41,149,224,
148,130,86,130,244,12,27,237,123,234,0,68,164,165,250,235,87,243,
217,253,215,249,13,206,155,197,138,192,249,154,104,131,109,229,
126,77,216,127,254,55,170,192,100,90,49,30,78,209,249,140,184,81,
121,164,178,50,204,131,144,214,8,12,130,32,164,149,92,200,235,
145,48,162,244,238,114,47,251,31,79,242,186,111,117,207,16,178,
212,234,209,161,143,21,241,111,218,32,255,191,70,177,203,5,117,
182,148,199,231,93,118,76,72>>},
[{rand_gen,init,1,
[{file,"/home/x80486/Workshop/erlang/erlang_basics/_build/test/lib/erlang_basics/src/rand_gen.erl"},
{line,74}]},
{rand_gen_SUITE,init_per_suite,1,
[{file,"/home/x80486/Workshop/erlang/erlang_basics/_build/test/lib/erlang_basics/test/rand_gen_SUITE.erl"},
{line,34}]},
{test_server,ts_tc,3,[{file,"test_server.erl"},{line,1546}]},
{test_server,run_test_case_eval1,6,[{file,"test_server.erl"},{line,1147}]},
{test_server,run_test_case_eval,9,[{file,"test_server.erl"},{line,994}]}]}
Any clues? Thanks in advance!
That’s because the result of crypto:strong_rand_bytes(?BASE) doesn’t match <<I1:32/unsigned-integer, I2:32/unsigned-integer, I3:32/unsigned-integer>> since it’s (as you can see in the error) longer than that.
You can try changing that line in your code to something like:
<<I1:32/unsigned-integer, I2:32/unsigned-integer, I3:32/unsigned-integer, _/bitstring>> = crypto:strong_rand_bytes(?BASE),

Starting a child with simple_one_for_one supervisor

How do you start a child with simple_one_for_one supervisor?
I'm going through the LYSE book and currently on the Dynamic Supervision section: http://learnyousomeerlang.com/supervisors#dynamic-supervision
The author starts the child as follows, but I have no idea where djembe is defined:
1> supervisor:start_child(band_supervisor, [djembe, good]).
Musician Janet Tennelli, playing the djembe entered the room
{ok,<0.690.0>}
2> supervisor:start_child(band_supervisor, [djembe, good]).
{error,{already_started,<0.690.0>}}
Here is my attempt:
2> supervisor:start_link(band_supervisor, jamband).
{ok,<0.40.0>}
3> supervisor:start_child(band_supervisor, [djembe, good]).
=ERROR REPORT==== 28-Feb-2016::03:52:56 ===
** Generic server <0.40.0> terminating
** Last message in was {'EXIT',<0.33.0>,
{{noproc,
{gen_server,call,
[band_supervisor,
{start_child,[djembe,good]},
infinity]}},
[{gen_server,call,3,
[{file,"gen_server.erl"},{line,212}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,673}]},
{shell,exprs,7,[{file,"shell.erl"},{line,686}]},
{shell,eval_exprs,7,
[{file,"shell.erl"},{line,641}]},
{shell,eval_loop,3,
[{file,"shell.erl"},{line,626}]}]}}
** When Server state == {state,
{<0.40.0>,band_supervisor},
simple_one_for_one,
[{child,undefined,jam_musician,
{musicians,start_link,[]},
temporary,1000,worker,
[musicians]}],
undefined,3,60,[],band_supervisor,jamband}
** Reason for termination ==
** {{noproc,{gen_server,call,
[band_supervisor,
{start_child,[djembe,good]},
infinity]}},
[{gen_server,call,3,[{file,"gen_server.erl"},{line,212}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,673}]},
{shell,exprs,7,[{file,"shell.erl"},{line,686}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,641}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,626}]}]}
** exception exit: {noproc,{gen_server,call,
[band_supervisor,
{start_child,[djembe,good]},
infinity]}}
in function gen_server:call/3 (gen_server.erl, line 212)
Here is how the supervisor looks:
-module(band_supervisor).
-behaviour(supervisor).
-export([start_link/1]).
-export([init/1]).
start_link(Type) ->
supervisor:start_link({local,?MODULE}, ?MODULE, Type).
%% The band supervisor will allow its band members to make a few
%% mistakes before shutting down all operations, based on what
%% mood he's in. A lenient supervisor will tolerate more mistakes
%% than an angry supervisor, who'll tolerate more than a
%% complete jerk supervisor
init(lenient) ->
init({one_for_one, 3, 60});
init(angry) ->
init({rest_for_one, 2, 60});
init(jerk) ->
init({one_for_all, 1, 60});
init(jamband) ->
{ok, {{simple_one_for_one, 3, 60},
[{jam_musician,
{musicians, start_link, []},
temporary, 1000, worker, [musicians]}
]}};
init({RestartStrategy, MaxRestart, MaxTime}) ->
{ok, {{RestartStrategy, MaxRestart, MaxTime},
[{singer,
{musicians, start_link, [singer, good]},
permanent, 1000, worker, [musicians]},
{bass,
{musicians, start_link, [bass, good]},
temporary, 1000, worker, [musicians]},
{drum,
{musicians, start_link, [drum, bad]},
transient, 1000, worker, [musicians]},
{keytar,
{musicians, start_link, [keytar, good]},
transient, 1000, worker, [musicians]}
]}}.
here are the musicians:
-module(musicians).
-behaviour(gen_server).
-export([start_link/2, stop/1]).
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, code_change/3, terminate/2]).
-record(state, {name="", role, skill=good}).
-define(DELAY, 750).
start_link(Role, Skill) ->
gen_server:start_link({local, Role}, ?MODULE, [Role, Skill], []).
stop(Role) -> gen_server:call(Role, stop).
init([Role, Skill]) ->
%% To know when the parent shuts down
process_flag(trap_exit, true),
%% sets a seed for random number generation for the life of the process
%% uses the current time to do it. Unique value guaranteed by now()
random:seed(now()),
TimeToPlay = random:uniform(3000),
Name = pick_name(),
StrRole = atom_to_list(Role),
io:format("Musician ~s, playing the ~s entered the room~n",
[Name, StrRole]),
{ok, #state{name=Name, role=StrRole, skill=Skill}, TimeToPlay}.
handle_call(stop, _From, S=#state{}) ->
{stop, normal, ok, S};
handle_call(_Message, _From, S) ->
{noreply, S, ?DELAY}.
handle_cast(_Message, S) ->
{noreply, S, ?DELAY}.
handle_info(timeout, S = #state{name=N, skill=good}) ->
io:format("~s produced sound!~n",[N]),
{noreply, S, ?DELAY};
handle_info(timeout, S = #state{name=N, skill=bad}) ->
case random:uniform(5) of
1 ->
io:format("~s played a false note. Uh oh~n",[N]),
{stop, bad_note, S};
_ ->
io:format("~s produced sound!~n",[N]),
{noreply, S, ?DELAY}
end;
handle_info(_Message, S) ->
{noreply, S, ?DELAY}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(normal, S) ->
io:format("~s left the room (~s)~n",[S#state.name, S#state.role]);
terminate(bad_note, S) ->
io:format("~s sucks! kicked that member out of the band! (~s)~n",
[S#state.name, S#state.role]);
terminate(shutdown, S) ->
io:format("The manager is mad and fired the whole band! "
"~s just got back to playing in the subway~n",
[S#state.name]);
terminate(_Reason, S) ->
io:format("~s has been kicked out (~s)~n", [S#state.name, S#state.role]).
%% Yes, the names are based off the magic school bus characters
%% 10 names!
pick_name() ->
%% the seed must be set for the random functions. Use within the
%% process that started with init/1
lists:nth(random:uniform(10), firstnames())
++ " " ++
lists:nth(random:uniform(10), lastnames()).
firstnames() ->
["Valerie", "Arnold", "Carlos", "Dorothy", "Keesha",
"Phoebe", "Ralphie", "Tim", "Wanda", "Janet"].
lastnames() ->
["Frizzle", "Perlstein", "Ramon", "Ann", "Franklin",
"Terese", "Tennelli", "Jamal", "Li", "Perlstein"].
learnyousomeerlang.com is a good source of information but sometimes I find it too elaborate. I learned that part of OTP directly by reading the supervisor OTP documentation. You probably don't need the gen_server if you just want to understand dynamic children. See my simple implementation:
-module(estp_proj_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1, add/2]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
init([]) ->
Child = ?WORKER(estp_project),
{ok, {{simple_one_for_one, 3, 30}, [Child]}}.
add(Name, Cfg) ->
Server = estp_project:server_name(Name),
State = [{name, Name}|Cfg],
case whereis(Server) of
undefined ->
add_child(Server, State);
Pid ->
delete_child(Server, Pid),
add_child(Server, State)
end.
add_child(Server, State) ->
supervisor:start_child(?SERVER, [Server, State]).
delete_child(Server, Pid) ->
ok = supervisor:terminate_child(?SERVER, Pid).
where the dynamic child name estp_project:server_name/1 is created like this:
server_name(Name) ->
Module = atom_to_binary(?MODULE, utf8),
Binary = atom_to_binary(Name, utf8),
binary_to_atom(<<Module/binary, <<"$">>/binary, Binary/binary>>, utf8).
and worker is defined as:
-define(SHUTDOWN_TIMEOUT, 5000).
-define(WORKER(I), {I, {I, start_link, []}, permanent, ?SHUTDOWN_TIMEOUT, worker, [I]}).
You then add children simply by calling estp_proj_sup:add(Name, Cfg) like in this code:
process(Res) ->
%% Res contains parsed list of project names and their configurations
[set_project(Name, Cfg) || {Name, {ok, Cfg}} <- Res].
set_project(Name, Cfg) ->
case estp_proj_sup:add(Name, Cfg) of
{ok, _Pid} -> ok;
{error, _} = Err -> Err
end.
Anyways, I tried your example and it seems to be working:
4> {ok, S} = band_supervisor:start_link(jamband).
{ok,<0.47.0>}
5> supervisor:start_child(band_supervisor, [djembe, good]).
Musician Wanda Terese, playing the djembe entered the room
{ok,<0.49.0>}
Wanda Terese produced sound!
Wanda Terese produced sound!
Wanda Terese produced sound!
Wanda Terese produced sound!
Wanda Terese produced sound!
6> supervisor:start_child(band_supervisor, [djembe, good]).
{error,{already_started,<0.49.0>}}
Wanda Terese produced sound!
Wanda Terese produced sound!

RabbitMQ work queue is blocking consumers

I'm following the example here
https://www.rabbitmq.com/tutorials/tutorial-two-python.html
only in Erlang with amqp_client
The full code is here
https://github.com/jhw/rabbit_worker
As I understand it, to get worker- queue style behaviour you need to define your RabbitMQ topology as follows -
'direct' exchange
single queue to which multiple consumers (workers) bind
routing key equal to queue name
workers to ack' each message
pre- fetch count of 1
Now I have this working fine, and can see the exchange passing messages round- robin fashion to the workers
The only problem is that there's no parallelism; only one worker is called at a time; it's as if a request to one worker is blocking the exchange from sending messages to any of the others
So I think I probably have my RabbitMQ topology set up incorrectly; but the problem is I don't know where.
Any thoughts ?
TIA.
Core code below -
-module(pool_router).
-behaviour(gen_server).
-include_lib("amqp_client/include/amqp_client.hrl").
%% API.
-export([start_link/0]).
-export([subscribe/1]).
-export([unsubscribe/1]).
-export([publish/2]).
-export([acknowledge/1]).
%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).
-record(state, {rabbit_conn, rabbit_chan, queues}).
-define(RABBIT_USERNAME, <<"guest">>).
-define(RABBIT_PASSWORD, <<"Hufton123">>).
-define(EXCHANGE_NAME, <<"worker_exchange">>).
-define(EXCHANGE_TYPE, <<"direct">>).
-define(QUEUE_NAMES, [<<"worker_queue">>]).
%% API.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%% [stak_worker_sup:spawn_worker() || _ <- lists:seq(1, 3)].
%% [pool_router:publish(<<"worker_queue">>, {<<"fibonacci">>, <<"fibonacci">>, [40]}) || _ <- lists:seq(1, 9)].
publish(QueueName, MFA) ->
gen_server:call(?MODULE, {publish, {QueueName, MFA}}).
acknowledge(Tag) ->
gen_server:call(?MODULE, {acknowledge, Tag}).
subscribe(QueueName) ->
gen_server:call(?MODULE, {subscribe, QueueName}).
unsubscribe(Tag) ->
gen_server:call(?MODULE, {unsubscribe, Tag}).
%% gen_server.
init([]) ->
RabbitParams=#amqp_params_network{username=?RABBIT_USERNAME,
password=?RABBIT_PASSWORD},
{ok, RabbitConn}=amqp_connection:start(RabbitParams),
{amqp_channel, {ok, RabbitChan}}={amqp_channel,
amqp_connection:open_channel(RabbitConn)},
Exchange=#'exchange.declare'{exchange=?EXCHANGE_NAME,
type=?EXCHANGE_TYPE,
auto_delete=false},
#'exchange.declare_ok'{}=amqp_channel:call(RabbitChan, Exchange),
InitQueueFn=fun(QueueName) ->
Queue=#'queue.declare'{queue=QueueName},
#'queue.declare_ok'{}=amqp_channel:call(RabbitChan, Queue),
Binding=#'queue.bind'{queue=Queue#'queue.declare'.queue,
exchange=?EXCHANGE_NAME,
routing_key=QueueName},
#'queue.bind_ok'{}=amqp_channel:call(RabbitChan, Binding),
Queue
end,
Queues=[{QueueName, InitQueueFn(QueueName)} || QueueName <- ?QUEUE_NAMES],
#'basic.qos_ok'{}=amqp_channel:call(RabbitChan, #'basic.qos'{prefetch_count=1}),
io:format("router started~n"),
{ok, #state{rabbit_conn=RabbitConn,
rabbit_chan=RabbitChan,
queues=Queues}}.
handle_call({publish, {QueueName, {Mod, Fn, Args}=MFA}}, {_From, _}, #state{rabbit_chan=RabbitChan}=State) ->
io:format("Publishing ~p~n", [MFA]),
Payload=jsx:encode([{<<"M">>, Mod},
{<<"F">>, Fn},
{<<"A">>, Args}]),
Publish=#'basic.publish'{exchange=?EXCHANGE_NAME,
routing_key=QueueName},
ok=amqp_channel:cast(RabbitChan, Publish, #amqp_msg{payload=Payload}),
{reply, ok, State};
handle_call({acknowledge, Tag}, {From, _}, #state{rabbit_chan=RabbitChan}=State) ->
ok=amqp_channel:cast(RabbitChan, #'basic.ack'{delivery_tag=Tag}),
io:format("~p [~p] acknowledged~n", [From, Tag]),
{reply, ok, State};
handle_call({subscribe, QueueName}, {From, _}, #state{queues=Queues, rabbit_chan=RabbitChan}=State) ->
io:format("~p subscribed~n", [From]),
{_, Queue}=proplists:lookup(QueueName, Queues), %% NB no error checking
#'basic.consume_ok'{consumer_tag=Tag}=amqp_channel:subscribe(RabbitChan, #'basic.consume'{queue=Queue#'queue.declare'.queue}, From),
{reply, {ok, Tag}, State};
handle_call({unsubscribe, Tag}, {From, _}, #state{rabbit_chan=RabbitChan}=State) ->
io:format("~p [~p] unsubscribed~n", [From, Tag]),
#'basic.cancel_ok'{}=amqp_channel:call(RabbitChan, #'basic.cancel'{consumer_tag=Tag}),
{reply, ok, State};
handle_call(_Request, _From, State) ->
{reply, ignored, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
ok=amqp_channel:close(State#state.rabbit_chan),
ok=amqp_connection:close(State#state.rabbit_conn),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% internal functions
-module(stak_worker).
-behaviour(gen_server).
-include_lib("amqp_client/include/amqp_client.hrl").
%% API.
-export([start_link/0]).
-export([stop/1]).
%% gen_server.
-export([init/1]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).
-record(state, {rabbit_tag}).
-define(QUEUE_NAME, <<"worker_queue">>).
%% API.
%% {ok, Pid}=stak_worker:start_link().
%% stak_worker:stop(Pid).
start_link() ->
gen_server:start_link(?MODULE, [], []).
stop(Pid) ->
gen_server:cast(Pid, stop).
%% gen_server.
%% don't run request automatically
%% workers should subscribe to router on startup and unsubscribe on termination
%% router then routes messages to workers
init([]) ->
io:format("starting worker ~p~n", [self()]),
{ok, #state{}, 0}.
handle_call(_Request, _From, State) ->
{reply, ignored, State}.
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({#'basic.deliver'{delivery_tag=Tag}, #amqp_msg{payload=Payload}=_Content}, State) ->
%% io:format("~p received ~p~n", [self(), Payload]),
Struct=jsx:decode(Payload),
{_, ModBin}=proplists:lookup(<<"M">>, Struct),
{_, FnBin}=proplists:lookup(<<"F">>, Struct),
{_, Args}=proplists:lookup(<<"A">>, Struct),
Mod=list_to_atom(binary_to_list(ModBin)),
Fn=list_to_atom(binary_to_list(FnBin)),
Mod:Fn(Args),
ok=pool_router:acknowledge(Tag),
{noreply, State};
handle_info(timeout, State) ->
%% io:format("~p subscribing~n", [self()]),
{ok, RabbitTag}=pool_router:subscribe(?QUEUE_NAME),
{noreply, State#state{rabbit_tag=RabbitTag}};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
io:format("~p shutting down~n", [self()]),
ok=pool_router:unsubscribe(State#state.rabbit_tag),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Avoid use of prefetch_count; it limits the number of un- ack'ed messages on a single channel to one; so if have a worker pool, thus will cause it to operate sequentially rather than in parallel (because every request sent to a worker is only ack'ed once the worker has completed; ie you have multiple un- ack'ed messages at a given point in time)

receive specific amount of data from gen_tcp socket

I have the following code using a gen_tcp socket, which should receive using {active, true} some binary commands that are always of size 5 (e.g. Command = <<"hello">>)
{ok, ListenSocket} = gen_tcp:listen(5555, [{active, true}, {mode, binary},
{packet, 0}]),
{ok, Socket} = gen_tcp:accept(ListenSocket),
loop().
receive() ->
{tcp, _Socket, Message:4/binary} ->
io:format("Success~n", []),
loop();
{tcp, _Socket, Other} ->
io:format("Received: ~p~n", [Other]),
loop()
end.
But if I try to connect as a client like this:
1> {ok, S} = gen_tcp:connect("localhost", 5555, []).
{ok,#Port<0.582>}
2> gen_tcp:send(S, <<"hello">>).
ok
I have in the receiving part:
Received: {tcp,#Port<0.585>,<<"hello">>}
so I suppose my error is in the pattern matching...but where?
Your receive clause doesn't look right, it should be:
{tcp, _Socket, <<Message:5/binary>>} ->
....
As far as I understood you need gen_tcp:recv function. From documentation:
recv(Socket, Length) -> {ok, Packet} | {error, Reason}
In your code it will be something like:
...
{ok, Socket} = gen_tcp:accept(ListenSocket),
loop(Socket).
loop(Socket) ->
case gen_tcp:recv(Socket, 5) of
{ok, Data} ->
io:format("Success~n", []),
loop(Socket);
{error, closed} ->
ok
end.

erlang semaphore (mutex)

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]).

Resources