erlang semaphore (mutex) - erlang
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]).
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),
Can't start process when starting erlag node
I was trying to start a supervisor and a gen_server process while one node is created from the command-line, I use the following command to start the node: erl -name vm01#192.168.146.129 -s ets_sup start vm02#192.168.146.129 calc However, I found the process is undefined when I use whereis to check the process on the newly created node. I got no problems running ets_sup and ets_srv on the node shell directly, but starting the node from command line doesn't work. I want to know why this happening? ets_sup.erl: -module(ets_sup). -behaviour(supervisor). -export([start/1, start_link/1, init/1]). start([A, B]) -> start_link([A, B]). start_link([A, B]) -> supervisor:start_link({local, ?MODULE}, ?MODULE, [A, B]). init([A, B]) -> {ok, {{one_for_all, 0, 1}, [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}. ets_srv.erl: -module(ets_srv). -behaviour(gen_server). -compile(export_all). -record(state, {a, b}). start_link(A,B) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [A, B], []). init([A, B]) -> {ok, #state{a = A, b = B}}. check_sys() -> gen_server:call(?MODULE, check_sys). handle_call(check_sys, _From, #state{a = A, b = B} = State) -> {reply, {A, B}, State}. handle_info(_Info, State) -> {noreply, State}. handle_cast(_Req, State) -> {noreply, State}. code_change(_Ol, State, _Ex) -> {ok, State}. terminate(_R, _S) -> ok.
I think that you really start the function ets_sup:start/1 with your parameter list. You can verify it by adding an io:format(...) before the ets_sup:start_link/1. but the process who executes the function ets_sup:start/1 is an early one who dies very soon with reason shutdown, and as your supervisor is linked to it, it dies too, with all its children. You have to call this function from a process that will not die (usually, it is the role of the application manager). For example, do: start([A, B]) -> % spawn a new process spawn(fun () -> start_link([A, B]), % add a loop to keep it alive loop() end). loop() -> receive stop -> ok; _ -> loop() end. Edit, but not an answer I have modified your code: add process_flag(trap_exit, true), in the init server, in order to catch an exit message, add io:format("server terminate with reason ~p, process ~p~n",[_R,self()]), in the server terminate function in order to print an exit reason eventually sent by the supervisor (Note: if an exit message is sent by another process, handle_info will be triggered). add ets_srv:check_sys() in the supervisor, just after the start of the server in order to check if it did star correctly. Here is the modified code. -module(ets_sup). -behaviour(supervisor). -export([start/1, start_link/1, init/1]). start([A, B]) -> start_link([A, B]). start_link([A, B]) -> supervisor:start_link({local, ?MODULE}, ?MODULE, [A, B]), ets_srv:check_sys(). init([A, B]) -> {ok, {{one_for_all, 0, 1}, [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}. -module(ets_srv). -behaviour(gen_server). -compile(export_all). -record(state, {a, b}). start_link(A,B) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [A, B], []). init([A, B]) -> process_flag(trap_exit, true), {ok, #state{a = A, b = B}}. check_sys() -> gen_server:call(?MODULE, check_sys). handle_call(check_sys, _From, #state{a = A, b = B} = State) -> io:format("check_sys state ~p, process ~p~n",[State,self()]), {reply, {A, B}, State}. handle_info(_Info, State) -> {noreply, State}. handle_cast(_Req, State) -> {noreply, State}. code_change(_Ol, State, _Ex) -> {ok, State}. terminate(_R, _S) -> io:format("server terminate with reason ~p, process ~p~n",[_R,self()]), ok. Running this version shows that the supervisor starts the server correctly, and then it sends a shutdown message to it. This does not occurs if the supervisor is started in the shell. C:\src>erl -s ets_sup start vm02#192.168.146.129 calc check_sys state {state,'vm02#192.168.146.129',calc}, process <0.56.0> server terminate with reason shutdown, process <0.56.0> Eshell V8.2 (abort with ^G) 1> whereis(ets_srv). undefined 2> ets_sup:start(['vm02#192.168.146.129',calc]). check_sys state {state,'vm02#192.168.146.129',calc}, process <0.61.0> {'vm02#192.168.146.129',calc} 3> whereis(ets_srv). <0.61.0> 4> ets_srv:check_sys(). check_sys state {state,'vm02#192.168.146.129',calc}, process <0.61.0> {'vm02#192.168.146.129',calc} 5> exit(whereis(ets_srv),shutdown). true 6> whereis(ets_srv). <0.61.0> 7> exit(whereis(ets_srv),kill). ** exception exit: shutdown 8> whereis(ets_srv). undefined 9> I have verified that if you start an ordinary process (not a supervisor) the same way, using spawn_link, it doesn't receive any exit message. -module (st). -compile([export_all]). start(Arg) -> do_start(Arg). do_start(Arg) -> io:format("spawn from ~p~n",[self()]), register(?MODULE,spawn_link(fun () -> init(Arg) end)). init(Arg) -> io:format("init with ~p in ~p~n",[Arg,self()]), process_flag(trap_exit, true), Pid = self(), spawn(fun() -> monitor(process,Pid), receive M -> io:format("loop received ~p~n",[M]) end end), loop(Arg). loop(Arg) -> receive state -> io:format("state is ~p~n",[Arg]), loop(Arg); stop -> io:format("stopping~n"); _ -> loop(Arg) end. The execution gives: C:\src>erl -s st start vm02#192.168.146.129 calc spawn from <0.3.0> init with ['vm02#192.168.146.129',calc] in <0.55.0> Eshell V8.2 (abort with ^G) 1> whereis(st). <0.55.0> 2> exit(whereis(st),shutdown). true 3> whereis(st). <0.55.0> 4> st ! state. state is ['vm02#192.168.146.129',calc] state 5> st ! stop. stopping loop received {'DOWN',#Ref<0.0.4.66>,process,<0.55.0>,normal} stop 6> whereis(st). undefined 7> Edit, another way to "detach" the supervisor All the tests I have done show that the supervisor receives a shutdown message. I don't know why, usually I use the application mechanism to start a supervision tree, and I never met this situation. I propose you to unlink the supervisor from its parent, so it will not receive a shutdown message: -module(ets_sup). -behaviour(supervisor). -export([start/1, start_link/1, init/1]). start([A, B]) -> start_link([A, B]). start_link([A, B]) -> supervisor:start_link({local, ?MODULE}, ?MODULE, [A, B]), ets_srv:check_sys(). init([A, B]) -> {links,[Parent]} = process_info(self(),links), unlink(Parent), {ok, {{one_for_all, 0, 1}, [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}. An now it works: C:\src>erl -s ets_sup start vm02#192.168.146.129 calc check_sys state {state,'vm02#192.168.146.129',calc}, process <0.56.0> Eshell V8.2 (abort with ^G) 1> ets_srv:check_sys(). check_sys state {state,'vm02#192.168.146.129',calc}, process <0.56.0> {'vm02#192.168.146.129',calc} 2>
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.
recv message and write to file
I have made a server here which receives messages from the terminal. What I want to do is when the server receives the messages from the terminal it should write it into a file. But I dont know how I should do this. I thought of writing the file methods file:open, file:write in the receive functions but it didnt work out as I thought. -module(tcp). -behaviour(gen_server). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, start/0, stop/0, connect/0, send/1, recv/0]). -export([start_link/0]). %% ============================================================================= %% EXPORTED GEN_SERVER CALLBACKS %% ============================================================================= init([]) -> {ok, {}}. handle_call({send, Packet}, _From, State) -> gen_tcp:send(State, Packet), io:format("Send Working\n"), {reply, ok, State}; handle_call(recv, _From, State) -> Message = gen_tcp:recv(State, 0), io:format("~w~n", [Message]), {reply, Message, State}. handle_cast(connect, _) -> case gen_tcp:listen(6888, [binary]) of {ok, LSocket}-> io:format("~w~n", [LSocket]), case gen_tcp:accept(LSocket) of {ok, Socket} -> inet:setopts(Socket, [{active, false}]), io:format("accepted\n"), {noreply, Socket}; Other -> error_logger:error_report(["An error occurred which is",Other,"in line",?LINE,"of module",?MODULE]) end; {error, Reason} -> error_logger:error_report("An error occurred", Reason, [?LINE,?MODULE]) end; handle_cast(stop, State) -> {stop, normal, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. %% ============================================================================= %% EXPORTED CONVENIENCE FUNCTIONS TO START AND STOP THE SERVER %% ============================================================================= start() -> case gen_server:start({local, ?MODULE}, ?MODULE, [], []) of {ok, Pid} -> Pid; Reason -> error_logger:error_report("An error occurred", Reason, [?LINE,?MODULE]) end. stop() -> case gen_server:cast(?MODULE, stop) of ok -> ok; _ -> {error, stop_error} end. %% ============================================================================= %% PUBLIC API FUNCTIONS %% ============================================================================= connect() -> gen_server:cast(?MODULE, connect). send(Packet) -> gen_server:call(?MODULE, {send, Packet}). recv() -> gen_server:call(?MODULE, recv). write_file(Filename) -> {ok, IoDevice} = file:open(test.txt, [write, append]), ok. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). %% ============================================================================= %% LOCAL FUNCTIONS %% ============================================================================= I did as you suggested and implemented it as a handle_call but when i run i get an error, function handle_call/3 already defined error I quite dont understand why I get this error since i have 3 arguments for the handle_call and it should work. handle_call(write_file, Filename, S) -> {ok, File} = file:open(Filename, [append]), ok = file:write(File, S), ok = file:close(File). My API Function write_file() -> gen_server:call(?MODULE, write_file).
Your handle call doesn't work because you have two definitions of it. You have written: handle_call(...) -> do_something. handle_call(M, F, S) -> do_some_other_thing. You should make them into the same function, by changing: do_something. into: do_something; handle_call(M, F, S) -> do_some_other_thing.
write_file should do something like: write_file(Fname, S) -> ok = file:write_file(Fname, S, [append]). or write_file(Fname, S) -> {ok, File} = file:open(Fname, [append]), ok = file:write(File, S), ok = file:close(File).