I am trying to perform an action based on changes in the folder which are detected by synrc/fs library. I want to receive this notification every time the changes captured by fs & perform an action e.g. printing the changed filename.
I tried below code but executes only first time!
say_hello() ->
fs:start_link(fs_watcher, "/Users/foldername"),
fs:subscribe(fs_watcher),
receive
{Watcher_process, {Fs, File_event}, {ChangedFile, Type}} ->
io:format("~p was ~p ~n",[ChangedFile,File_event])
end.
Any useful help is appreciated along with link & description if possible! Thanks :)
If you want the function to keep receiving the same kind of messages you could use recursion:
say_hello() ->
fs:start_link(fs_watcher, "/Users/foldername"),
fs:subscribe(fs_watcher),
recur().
recur()->
receive
{Watcher_process, {Fs, File_event}, {ChangedFile, Type}} ->
io:format("~p was ~p ~n",[ChangedFile,File_event]),
recur()
end.
You would have to then think about a way to finalise the function.
You need to recursively call receive:
say_hello() ->
fs:start_link(fs_watcher, "/Users/foldername"),
fs:subscribe(fs_watcher),
loop().
loop() ->
receive
{Watcher_process, {Fs, File_event}, {ChangedFile, Type}} ->
io:format("~p was ~p ~n",[ChangedFile,File_event]),
loop()
end.
Related
When I read Erlang OTP Action book, I found this reminder on page 117:
With your RPC server, you can try calling any function exported from any module available on the server side, except one: your own tr_server:get_count/0. In general, a server can’t call its own API functions. Suppose you make a synchronous call to the same server from within one of the callback functions: for example, if handle_info/2 tries to use the get_count/0 API function. It will then perform a gen_server:call(...) to itself. But that request will be queued up until after the current call to handle_info/2 has finished, resulting in a circular wait—the server is deadlocked.
But I looked at the tr_server sample code :
get_count() ->
gen_server:call(?SERVER, get_count).
stop() ->
gen_server:cast(?SERVER, stop).
handle_info({tcp, Socket, RawData}, State) ->
do_rpc(Socket, RawData),
RequestCount = State#state.request_count,
{noreply, State#state{request_count = RequestCount + 1}};
......
do_rpc(Socket, RawData) ->
try
{M, F, A} = split_out_mfa(RawData),
Result = apply(M, F, A), % tr_server process -> handle_info -> do_rpc ->call & cast
gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
catch
_Class:Err ->
gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
end.
I found the examples and cautions in the book inconsistent , the gen_server:call and gen_server:cast by tr_server process ownself.
Am I misinterpreting this?
Calling gen_server:cast from within the server process is fine, because it is asynchronous: it adds the message to the mailbox of the process and then continues, returning ok. Only gen_server:call has this problem, because it makes the process wait for an answer from itself.
I have the gen_server shown below. It works for the most part. However when I start it from the shell the replies come right back to the shell prompt. I would have expected them to be sent as messages back to the shells pid and then I would use flush() to see them.
What do I have to change in order to have the foo_worker send its replies as messages ?
-module(foo_worker).
-behaviour(gen_server).
%% API
-export([start_link/1, start/1, init/1, send/3, die/1]).
-export([handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
%%%-------------------------------------------------------------------
send(Worker, Ref, Counter) ->
gen_server:call(Worker, {inc, Ref, Counter}).
die(Worker) ->
gen_server:cast(Worker, die).
%%%-------------------------------------------------------------------
start_link(Limit) ->
gen_server:start_link(?MODULE, [Limit], []).
start(Limit) ->
gen_server:start(?MODULE, [Limit], []).
init([Limit]) ->
{ok, Limit}.
handle_call(_, _, Limit) when Limit =< 0 ->
exit({worker, eol});
handle_call({inc, Ref, Data}, From, Limit) ->
io:format("From ~p~n", [From]),
{reply, {Ref, updated, Data+1}, Limit - 1}.
handle_cast(die, _) ->
io:format("~p Dying ~n",[self()]),
exit(normal).
handle_info(Info, State) ->
io:format("Unkown message ~p for state ~p~n", [Info, State]).
terminate(Reason, State) ->
io:format("~p Died because ~p with state ~p~n", [self(), Reason, State]).
The whole point of gen_server:call/2,3 is to wrap into a function call the passing of a message into a gen_server process and the reception of its reply. If you want to deal only with messages, don't use gen_server:call/2,3 but rather have the caller invoke gen_server:cast/2 and include the caller pid in the message:
send(Worker, Ref, Counter) ->
gen_server:cast(Worker, {inc, Ref, Counter, self()}).
Then have gen_server:handle_cast/2 understand that message and use the pid send the reply back to the caller:
handle_cast({inc, Ref, Data, From}, Limit) ->
From ! {Ref, updated, Data+1},
{noreply, Limit-1}.
By the way, note that when you choose this sort of approach, you need to deal with possible failure. If you pass a message to the gen_server process but it dies before it sends you a reply, you need to make sure the caller doesn't sit and wait forever for a reply that will never arrive. The best way to do this is with a monitor — you can have the caller monitor the gen_server process before sending it a message and demonitor it once it receives the reply. If the gen_server process dies, the caller will get a DOWN message instead (see the monitor documentation for details). Also note that by doing this you're reimplementing a bunch of what gen_server:call/2,3 already does for you.
E.g. suppose I have a list that looks something roughly like this:
Handlers = [{foo, FooHandler}, {bar, BarHandler} | Etc()]
The best that I can come up with is this:
receive
Message ->
Handler = find_matching_handler(Message, Handlers),
Handler(Message)
end
The problem with this is that if Message does not match anything in Handlers, it's too late: I've taken it out of the mailbox.
I guess if there's a way to put a message back into the mailbox (into the save queue) without reordering, then that would take care of it. Simply resending to self() would reorder. It would also not restart the receive, and even if it did, you might get stuck in a spin loop until a message of interest arrives. Is there a way to put a message into the mailbox's save queue?
Another near solution that I thought of was to use match guard, but IIUC, you can only use BIFs in guards, which seems to preclude using find_matching_handler (unless there is a BIF for that).
Another near solution: map matching:
receive
M when Handlers#{M := Handler} -> Handler(M) % booyah?
end
Alas, I have not found an incantation that satisfies Erlang...
Match on the message:
loop() ->
receive
{foo, Data} ->
handle_foo(Data),
loop();
{bar, Data} ->
handle_bar(Data),
loop()
end.
This is the basic way of distinguishing between message forms.
You can also be less direct and match in a function head you pass all messages to:
loop() ->
receive
Message ->
handle_message(Message),
loop()
end.
handle_message({foo, Data}) ->
foo(Data),
ok;
handle_message({bar, Data}) ->
bar(Data),
ok.
A combination of the first and second forms is sort of the way gen_server type callback modules are structured in OTP. The message handlers receive a slightly more complex set of arguments and exist in their own module (the part you write), and the actual receive occurs in the generic gen_server module.
You can use a selective receive pattern to periodcally scan the mailbox for handler messages. Something like this:
check_msg_handlers(Handlers) ->
[check_handler(X) || X <- Handlers],
timer:sleep(500),
check_msg_handlers(Handlers).
check_handler(Handler) ->
receive
{_Handler={M,F}, Msg} ->
M:F(Msg)
after
0 ->
no_msg
end.
Note the receive X -> Y after -> N no_msg end, this is the selective receive. When using a timeout of N=0 it effectively becomes a scan of the mailbox to see if the X message is present or not, i.e. it becomes a non-blocking receive. The order of the messages is preserved after the scan as required in your case.
The LYSE chapter More On Multiprocessing has a section on selective receives that is very good.
I'm having some trouble with an Erlang module. Here is the one that I wrote:
-module(basic_gen_server).
-export([start/1, call/2, cast/2]).
start(Module) ->
register(server, spawn(basic_gen_server,gen_server_loop,[Module, Module:init()])), server.
call(Pid,Request) ->
Pid ! {call, self(), Request},
receive
Reply -> Reply
end.
cast(Pid,Request) ->
Pid ! {cast, self(), Request},
receive
_ -> ok
end.
gen_server_loop(Module, CurrentState) ->
io:fwrite("gen_server_loop~n", []),
receive
{call, CallPid, Request} ->
{reply, Reply, NewState} = Module:handle_call(Request,self(),CurrentState),
CallPid ! Reply,
gen_server_loop(Module, NewState);
{cast, CastPid, Request} ->
{noReply, NewState} = Module:handle_cast(Request, CurrentState),
CastPid ! noReply,
gen_server_loop(Module, NewState)
end.
And here is the callback module that was defined:
% Written by Caleb Helbling
% Last updated Oct 10, 2014
-module(name_server).
-export([init/0, add/3, whereis/2, handle_cast/2,
handle_call/3, handle_swap_code/1]).
%% client routines
add(ServerPid, Person, Place) ->
basic_gen_server:cast(ServerPid, {add, Person, Place}).
whereis(ServerPid, Person) ->
basic_gen_server:call(ServerPid, {whereis, Person}).
%% callback routines
init() ->
maps:new().
handle_cast({add, Person, Place}, State) ->
NewState = maps:put(Person, Place, State),
{noreply, NewState}.
handle_call({whereis, Person}, _From, State) ->
Reply = case maps:find(Person, State) of
{ok, Place} -> Place;
error -> error
end,
NewState = State,
{reply, Reply, NewState}.
handle_swap_code(State) ->
{ok, State}.
Upon trying to initialize the server with the following command:
MyServer = basic_gen_server:start(name_server).
I get the following debug output:
=ERROR REPORT==== 29-Oct-2014::12:41:42 ===
Error in process <0.70.0> with exit value: {undef,[{basic_gen_server,gen_server_loop,[name_server,#{}],[]}]}
Conceptually, I understand the notion of making serial code into a basic server system, but I believe that I have a syntax error that I haven't been able to find using either syntax highlighting or Google. Thanks in advance for the help!
Function gen_server_loop is not exported. So you can not call basic_gen_server:gen_server_loop(Module, Module:init()), which is what is happening inside spawn(basic_gen_server,gen_server_loop,[Module, Module:init()]).
If you read your error message it tells you that function you are trying to call in undefined (trougn undef atom). Function being {basic_gen_server,gen_server_loop,[name_server,#{}],[]}, or where you have {Module, Function, ListOfArgs, ...}. You always should check that
there are no types module or function name
function arity match number of arguments in call (List in error message)
function is exported
All local calls (like loop(SomeArgs), without module specified) will not compile if function is not defined. And you can do local call dynamically (FuntionName(SomeArgs) again without module name).
EDIT after comment about need of local calls.
You actually could use lambda for this. There is spawn/1 funciton, which takes lambda (or fun if you like), so you can call spawn( fun local_functino/0).. Only issue with that is fact that your fun can not take any arguments, but there is a way around it, with use of closures.
spawn(fun () ->
gen_server_loop(Module, Module:init())
end).
And gen_serve_loop stays local call.
As I learn Erlang, I'm trying to solve ex. 4.1 ("An Echo server") from "Erlang Programming" book (by O'Reilly) and I have a problem.
My code looks like that:
-module(echo).
-export([start/0, print/1, stop/0, loop/0]).
start() ->
register(echo, spawn(?MODULE, loop, [])),
io:format("Server is ready.~n").
loop() ->
receive
{print, Msg} ->
io:format("You sent a message: ~w.~n", [Msg]),
start();
stop ->
io:format("Server is off.~n");
_ ->
io:format("Unidentified command.~n"),
loop()
end.
print(Msg) -> ?MODULE ! {print, Msg}.
stop() -> ?MODULE ! stop.
Unfortunatelly, I have some problems. Turning on works as expected, it spawns a new process and display "Server is ready" message. But when I try to use print function (like echo:print("Some message."). that, for example) I got result, but it doesn't work like I'd like to. It prints my message as a list (not as a string) and it generates
=ERROR REPORT==== 18-Jul-2010::01:06:27 ===
Error in process <0.89.0> with exit value: {badarg,[{erlang,register,[echo,<0.93.0>]},{echo,start,0}]}
error message.
Moreover, when I try to stop server by echo:stop() I got another error
** exception error: bad argument
in function echo:stop/0
Could anybody explain me, what's going on here ? I am new to Erlang and it seems to be quite difficult to grasp for me at this time.
When your loop/0 function receive print message you call start/0 again which spawns new process and trying to register it as echo again. It causes your server dies and new one is not registered as echo, so you can't send message to it by print/1 function any more.
loop() ->
receive
{print, Msg} ->
io:format("You sent a message: ~w.~n", [Msg]),
loop(); % <-- just here!
stop ->
io:format("Server is off.~n");
_ ->
io:format("Unidentified command.~n"),
loop()
end.