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