Related
When I do make with my Erlang pjt, some errors come like: memcached.erl:44:10: syntax error before: '/'
Src code:
-module(Memcached).
%% External API
-export([set/3, set/5]).
-export([add/3, add/5]).
-export([replace/3, replace/5]).
-export([get/2]).
-export([delete/3, delete/2]).
-export([stats/1]).
%%====================================================================
%% Types
%%====================================================================
%% #type hostport() = {host, string(), port, integer()}. Tuple describing a host and port to connect to
%% #type socket() = {socket, port()}. Tuple describing an existing socket
%% #type memcached_connection() = hostport() | socket().
%% #type memcached_key() = list() | atom().
-type(hostport() :: {host, string(), port, integer()}).
-type(socket() :: {socket, port()}).
-type(memcached_connection() :: hostport() | socket()).
-type(memcached_key() :: list() | atom()).
%%====================================================================
%% External API
%%====================================================================
%% #doc Associate Bytes with Key.
%% #spec set(memcached_connection(), Key::memcached_key(), Bytes::any()) ->
%% ok | {error, not_stored}
-spec(set/3::(memcached_connection(), memcached_key(), any()) ->
ok | {error, not_stored}).
set({host, Host, port, Port}, Key, Bytes) ->
set({host, Host, port, Port}, Key, 0, 0, Bytes);
set({socket, Socket}, Key, Bytes) ->
set({socket, Socket}, Key, 0, 0, Bytes).
The error is located at -spec(set/3::(memcached_connection(), memcached_key(), any())
I checked many documents trying to solve it but the error is still there. Is there any mis-spelling or misuse of Erlang syntax?
My erl env info:
Erlang/OTP 24 [erts-12.1.5] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1]
Eshell V12.1.5 (abort with ^G)
1>
This syntax is obsolete:
-spec(set/3::(memcached_connection(), memcached_key(), any()) ->
ok | {error, not_stored}).
The current syntax is:
-spec set(memcached_connection(), memcached_key(), any()) ->
ok | {error, not_stored}.
I tried to figure out which version of Erlang dropped support for the old syntax, but I couldn't find it - must have been a long time ago.
I am using a gen_event behaviour and when i am trying to issue a gen_event:call i get the following error:
> =CRASH REPORT==== 22-Dec-2019::19:17:43.030000 === crasher:
> initial call: gen_event:init_it/6
> pid: <0.215.0>
> registered_name: hev
> exception exit: {undef,[{fm,state,[<0.215.0>],[]},
> {erl_eval,do_apply,6,
> [{file,"erl_eval.erl"},{line,684}]},
> {shell,exprs,7,[{file,"shell.erl"},{line,686}]},
> {shell,eval_exprs,7,
> [{file,"shell.erl"},{line,642}]},
> {shell,eval_loop,3,
> [{file,"shell.erl"},{line,627}]}]}
> in function gen_event:terminate_server/4 (gen_event.erl, line 354)
> ancestors: [<0.212.0>]
> message_queue_len: 1
> messages: [{'EXIT',<0.212.0>,normal}]
> links: []
> dictionary: []
> trap_exit: true
> status: running
> heap_size: 610
> stack_size: 27
> reductions: 279 neighbours:
My event manager and event handler get spawned and i can successfully issue notify (i get ok back) but i can not call:
Module
-module(hev).
-export([start/0,append/2,state/1]).
-export([init/1,terminate/2,code_change/3,handle_call/2,handle_event/2]).
-record(state,{
xs=[]
}).
-behaviour(gen_event).
%callbacks
init([])->
{ok,#state{xs=[1]}}.
**API**
start()->
{ok,Pid}=gen_event:start_link({local,?MODULE}),
gen_event:add_handler(Pid,some_handler,[]),
Pid.
append(Pid,Elem)->
gen_event:notify(Pid,{append,Elem}).
state(Pid)->
gen_event:call(Pid,state).
Handlers
handle_event({append,Elem},State=#state{xs=XS})->
{ok,#state{xs=[Elem|XS]}};
handle_call(state,State})->
{ok,State,State};
handle_call(Event,State)->
{ok,nada_for_you,State}.
P.S I have not posted all the requried methods (code_change,terminate..etc) but they exist.
I have not posted all the requried methods (code_change,terminate..etc) but they exist.
1) They are optional anyway. Check the big green Notes in the docs, e.g. terminate().
2) As for your error message:
pid: <0.215.0>
registered_name: hev
exception exit: {undef,[{fm,state,[<0.215.0>],[]},
it seems to be saying that there was a process (with pid=<0.215.0>), which was registered with the name hev, that tried to execute a function named fm:state() with one argument but there was no function fm:state/1 defined anywhere, hence the undef exception. Because you posted no module named fm, the error makes no sense in relation to the code you posted.
3) Your code also specifies a module named some_handler that does not exist:
gen_event:add_handler(Pid,some_handler,[]),
4) You have a basic syntax error here:
handle_call(state,State})->
5) You are calling a couple of functions with the wrong number of arguments.
You need to be more diligent about posting code that will actually produce the error you experienced.
Here's a simple example using gen_event to create a counter:
-module(counter).
-behaviour(gen_event).
-compile(export_all).
%% Callback functions:
init(StartingCount) -> % Called by gen_event:add_handler()
State = StartingCount,
{ok, State}.
terminate(_Reason, State) ->
io:format("Terminating state was: ~w~n", [State]).
% Calls to gen_event:notify() cause this function to execute:
handle_event({increase, Change}, State) ->
NewState = State+Change,
{ok, NewState};
handle_event({decrease, Change}, State) ->
NewState = State-Change,
{ok, NewState}.
% Calls to gen_event:call() cause this function to execute:
handle_call(get_count, State) ->
Reply = io_lib:format("Reply from handle_call(): count is ~w~n", [State]),
{ok, Reply, State};
handle_call({increase_and_get_count, Change}, State) ->
NewState = State+Change,
Reply = io_lib:format("Reply from handle_call(): count is ~w~n", [NewState]),
{ok, Reply, NewState}.
%% User interface functions:
start(StartingCount) ->
ServerName = gen_event_counter,
CallbackModule = counter,
{ok, _Pid} = gen_event:start_link({local, ServerName}),
%Name of process running gen_event server is: gen_event_counter
ok = gen_event:add_handler(ServerName, CallbackModule, StartingCount).
%StartingCount is passed to init() callback
stop() ->
ok = gen_event:stop(gen_event_counter),
stopped.
send_request_with_notify(Request) ->
gen_event:notify(gen_event_counter, Request). % returns immediately, does not wait for a reply.
% Request = {increase, 1}, {decrease, 2}, etc.
% Request is passed as first arg to handle_event().
send_request_with_call(Request) ->
Reply = gen_event:call(gen_event_counter, counter, Request), % waits for a reply
% Request is passed as first arg to handle_call()
io:format("send_request_with_call() returned => ~s", [Reply]).
In the shell:
~/erlang_programs/gen_event$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> c(counter).
counter.erl:3: Warning: export_all flag enabled - all functions will be exported
{ok,counter}
2> counter:start(0).
ok
3> counter:send_request_with_call(get_count).
send_request_with_call() returned => Reply from handle_call(): count is 0
ok
4> counter:send_request_with_notify({increase, 2}).
ok
5> counter:send_request_with_call(get_count).
send_request_with_call() returned => Reply from handle_call(): count is 2
ok
6> counter:send_request_with_call({increase_and_get_count, 5}).
send_request_with_call() returned => Reply from handle_call(): count is 7
ok
7> counter:stop().
Terminating state was: 7
stopped
8>
After fixing all the errors in your code:
-module(hev).
-compile(export_all).
-behaviour(gen_event).
-record(state,{
xs=[]
}).
%callbacks
init(no_args)->
{ok, #state{xs=[1]} }.
handle_event({append,Elem}, #state{xs=XS} ) ->
io:format("hev:handle_event() called~n"),
{ok, #state{xs=[Elem|XS]}}.
handle_call(get_state, State)->
Reply = State,
{ok, Reply, State};
handle_call(_Other, State)->
Reply = nada_for_you,
{ok, Reply, State}.
%**API**
start()->
gen_event:start_link({local, ?MODULE}), %Sets the gen_event server name to ?MODULE
% Server Callback Args for
% Name module init()
gen_event:add_handler(?MODULE, ?MODULE, no_args).
%Tells the gen_event server named ?MODULE to look for the callback functions
%in a module also named ?MODULE
append(Elem)->
% Server Request
% Name (matches against 1st arg in handle_event() )
gen_event:notify(?MODULE, {append, Elem}).
get_state()->
% Server Calback Request
% Name module (matches against 1st arg in handle_call() )
gen_event:call(?MODULE, ?MODULE, get_state).
other_calls() ->
gen_event:call(?MODULE, ?MODULE, {set_state, [1, 2, 3]}).
stop() ->
ok = gen_event:stop(?MODULE),
stopped.
In the shell:
~/erlang_programs/gen_event$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> c(hev).
hev.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,hev}
2> hev:start().
ok
3> hev:get_state().
{state,[1]}
4> hev:append(45).
ok
hev:handle_event() called
5> hev:get_state().
{state,[45,1]}
6> hev:other_calls().
nada_for_you
7> hev:stop().
stopped
8>
Note that notify() causes the handle_event() function in all the modules that were added with add_handler() to execute, whereas call() targets a specific module's handle_call() function.
I have a crude Erlang-to-Golang port example, passing data from Erlang to Golang and echoing the response.
Problem is the amount of data I can transfer seems to be limited to 2^8 bytes (see below). I thought the problem was probably on the Golang side (not creating a big enough buffer) but replacing bufio.NewReader with bufio.NewReaderSize didn't work. So am now thinking the problem is maybe on the Erlang side.
What do I need to do to increase the buffer size / be able to echo a message larger than 2^8 bytes ?
TIA
justin#justin-ThinkPad-X240:~/work/erlang_golang_port$ erl -pa ebin
Erlang/OTP 17 [erts-6.4.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]
Eshell V6.4.1 (abort with ^G)
1> port:start("./echo").
<0.35.0>
2> port:ping(65000).
65000
3> port:ping(66000).
** exception error: bad argument
in function port:call_port/1 (port.erl, line 20)
4> port:start("./echo").
<0.40.0>
5> port:ping(66000).
65536
Go
package main
import (
"bufio"
"os"
)
const Delimiter = '\n'
func main() {
// reader := bufio:NewReader(os.Stdin)
reader := bufio.NewReaderSize(os.Stdin, 1677216) // 2**24;
bytes, _ := reader.ReadBytes(Delimiter)
os.Stdout.Write(bytes[:len(bytes)-1])
}
Erlang
-module(port).
-export([start/1, stop/0, init/1]).
-export([ping/1]).
-define(DELIMITER, [10]).
start(ExtPrg) ->
spawn(?MODULE, init, [ExtPrg]).
stop() ->
myname ! stop.
ping(N) ->
Msg=[round(65+26*random:uniform()) || _ <- lists:seq(1, N)],
call_port(Msg).
call_port(Msg) ->
myname ! {call, self(), Msg},
receive
{myname, Result} ->
length(Result)
end.
init(ExtPrg) ->
register(myname, self()),
process_flag(trap_exit, true),
Port = open_port({spawn, ExtPrg}, []),
loop(Port).
loop(Port) ->
receive
{call, Caller, Msg} ->
Port ! {self(), {command, Msg++?DELIMITER}},
receive
{Port, {data, Data}} ->
Caller ! {myname, Data}
end,
loop(Port);
stop ->
Port ! {self(), close},
receive
{Port, closed} ->
exit(normal)
end;
{'EXIT', Port, _Reason} ->
exit(port_terminated)
end.
If you use start_link instead, you'll see that the port crashes after the first command:
1> port:start('go run port.go').
<0.118.0>
2> port:ping(65000).
65000
** exception error: port_terminated
If you change the Go code to run in a loop, this crash can be avoided:
func main() {
for {
// reader := bufio:NewReader(os.Stdin)
reader := bufio.NewReaderSize(os.Stdin, 1677216) // 2**24;
bytes, _ := reader.ReadBytes(Delimiter)
os.Stdout.Write(bytes[:len(bytes)-1])
}
}
Now we can see another interesting result:
33> c(port).
{ok,port}
40> port:ping(66000).
65536
41> port:ping(66000).
464
42> port:ping(66000).
65536
43> port:ping(66000).
464
Now we can see that no data is actually lost, it's just buffered in the port. Since you have not specified a framing protocol (using {packet, N} or {line, N} you are responsible yourself for collecting the data. It also seems that the internal buffer size of an Erlang port is 64K (although I found no documentation of this and no way to change it).
If you change your receive to get all data before returning, you'll every byte each time:
loop(Port) ->
receive
{call, Caller, Msg} ->
Port ! {self(), {command, Msg++?DELIMITER}},
Caller ! {myname, receive_all(Port, 10)},
loop(Port);
stop ->
Port ! {self(), close},
receive
{Port, closed} ->
exit(normal)
end;
{'EXIT', Port, _Reason} ->
exit(port_terminated)
end.
receive_all(Port, Timeout) -> receive_all(Port, Timeout, []).
receive_all(Port, Timeout, Data) ->
receive
{Port, {data, New}} ->
receive_all(Port, Timeout, [New|Data])
after Timeout ->
lists:flatten(lists:reverse(Data))
end.
Running this, we get:
1> c(port).
{ok,port}
2>
3> port:start('go run port.go').
<0.311.0>
4> port:ping(66000).
66000
5> port:ping(66000).
66000
6> port:ping(66000).
66000
2^8 is 256, not 65536 which is 2^16 (or 2 bytes).
For excluding golang program you can simply replace your echo with GNU cat
Default message max size for port communication is 64k, so when your port receives messages, the first one is leading 64k of the string. You can read port again to gain remaining data but you just drop them in your code.
If you really want to communicate on line-based protocol you should configure your port accordingly:
{line, L}
Messages are delivered on a per line basis. Each line
(delimited by the OS-dependent newline sequence) is delivered in one
single message. The message data format is {Flag, Line}, where Flag is
either eol or noeol and Line is the actual data delivered (without the
newline sequence).
L specifies the maximum line length in bytes. Lines longer than this
will be delivered in more than one message, with the Flag set to noeol
for all but the last message. If end of file is encountered anywhere
else than immediately following a newline sequence, the last line will
also be delivered with the Flag set to noeol. In all other cases,
lines are delivered with Flag set to eol.
The {packet, N} and {line, L} settings are mutually exclusive.
So your code would be
Port = open_port({spawn, ExtPrg}, [{line, ?PACKET_SIZE]),
%%...
{call, Caller, Msg} ->
Port ! {self(), {command, Msg++?DELIMITER}},
D = read_data(Port, []),
Caller ! {myname, D},
loop(Port);
%%...
read_data(Port, Prefix) ->
receive
{Port, {data, {noeol, Data}}} ->
read_data(Port, Prefix ++ Data);
{Port, {data, {eol, Data}}} ->
Prefix ++ Data
end.
I have been struggling with the similar problem.
Here the complete code of pipe module.
It allows sent text data to port and read all replies.
-module(apr_pipe).
-export([open_pipe/2,send/2,close/1]).
-export([loop/1,status/1,init/1]).
-include_lib("kernel/include/logger.hrl").
-define(MAX_LINE_LEN,4096).
open_pipe(Path,Cmd) ->
State = #{path => Path, cmd => Cmd},
Pid = spawn(?MODULE,init,[State]),
Pid.
init(State) ->
#{path := Path,cmd := Cmd} = State,
FullFn = filename:join(Path,Cmd),
Settings = [{line,?MAX_LINE_LEN},use_stdio,stderr_to_stdout,hide,binary,exit_status],
Port = erlang:open_port({spawn_executable,FullFn},Settings),
State2 = State#{port => Port, data => #{}},
loop(State2).
send(Pid,Data) -> Pid!{self(),send,Data}.
close(Pid) -> Pid!{self(),send,close}.
status(Pid) -> Pid!{self(),status}.
get_eol() -> <<"\n">>.
loop(State) ->
receive
{_Pid,send,close} ->
?LOG(notice,"got cmd: Close",[]),
Port = maps:get(port,State),
port_close(Port),
exit(normal);
{Pid,send,Data} ->
?LOG(notice,"Send Data ...",[]),
Port = maps:get(port,State),
port_command(Port,Data),
port_command(Port,get_eol()),
State2 = State#{status => data_sent, client => Pid},
loop(State2);
{Pid,status} ->
Port = maps:get(port,State),
?LOG(notice,"Status: Port: ~p State: ~p",[Port,State]),
Pid!{status,Port,State},
loop(State);
% port messages.
{Port, {data,{noeol,Data}}} ->
?LOG(notice,"Port: ~p Data: ~p",[Port,Data]),
CurData = maps:get(cur_data,State,[]),
State2 = State#{cur_data => [Data | CurData]},
loop(State2);
{Port, {data, {eol,Data}}} ->
?LOG(notice,"Port: ~p Data: ~p",[Port,Data]),
CurData = [Data | maps:get(cur_data,State,[])],
CurData2 = lists:reverse(CurData),
Reply = list_to_binary(CurData2),
Client = maps:get(client,State,undefined),
State2 = State#{cur_data => [], client => undefined},
case Client of
undefined -> ?LOG(error,"can not sent reply. Client: ~p Reply: ~p", [Client,Reply]),
loop(State2);
_ -> Client!{reply,Reply},
loop(State2)
end;
{_Port, closed} ->
?LOG(warning, "Port: ~p closed",[]),
exit(normal);
{'EXIT', Port, Reason} ->
?LOG(notice,"Port: ~p exit. Reason: ~p",[Port,Reason]),
exit(Reason);
_Other -> ?LOG(error,"unexpected message: ~p",[_Other]),
exit({error,{unexpected_message,_Other}})
end.
In a parallel application mimicking distributed inference, I would like to have an "initialization step" where all the "slaves" receive some initial information from the "master" then start their task.
At the moment I have a working implementation based on the sendTo function (the code was found here on stack overflow) but I don't think it guarantees that the worker won't start its task before it has received the initial objects.
Here's a rough MWE
function sendTo(p::Int; args...)
for (nm, val) in args
#spawnat(p, eval(Main, Expr(:(=), nm, val)))
end
end
a = 5
addprocs(4)
[sendTo(worker,a=a+randn()) for worker in workers()]
#everywhere begin
println(a)
end
The above "works" but how can I be sure that the commands in the #everywhere block does not get executed before the worker has received the definition of a?
Rmk: for the context I'm working in, I would like to keep two distinct blocks, one that spreads the data and one that does stuff on it.
Other rmk: apologies if this is trivial, I'm quite new to dealing with parallelism (and quite new to Julia too)
you can just fetch the results for every process. See the example in the docs
function sendTo(p::Int; args...)
r = []
for (nm, val) in args
s = #spawnat(p, eval(Main, Expr(:(=), nm, val)))
vcat([s],r)
end
end
#...
[fetch(r) for r in [sendTo(worker,a=a+randn()) for worker in workers()]]
I have the following code that I put together for a simple Ruby TFTP server. It works fine in that it listens to port 69 and my TFTP client connects to it and I am able to write the packets to the test.txt, but instead of just writing packets, I want to be able to TFTP a file from my client to the /temp directory.
Thanks in advance for your help!
require 'socket.so'
class TFTPServer
def initialize(port)
#port = port
end
def start
#socket = UDPSocket.new
#socket.bind('', #port)
while true
packet = #socket.recvfrom(1024)
puts packet
File.open('/temp/test.txt', 'w') do |p|
p.puts packet
end
end
end
end
server = TFTPServer.new(69)
server.start
Instead of writing to the /temp/test.txt you can use ruby's Tempfile class
So in your example:
require 'socket.so'
require 'tempfile'
class TFTPServer
def initialize(port)
#port = port
end
def start
#socket = UDPSocket.new
#socket.bind('', #port)
while true
packet = #socket.recvfrom(1024)
puts packet
Tempfile.new('tftpserver') do |p|
p.puts process_packet( packet )
end
end
end
end
server = TFTPServer.new(69)
server.start
This will create a guaranteed unique temporary file in your /tmp directory with a name based off of 'tftpserver'.
EDIT: I noticed you wanted to write to /temp (not /tmp) to do this you can do Tempfile.new('tftpserver', '/temp') to specify a specific temporary directory.
Edit 2: For anyone interested there is a library that will do this https://github.com/spiceworks/net-tftp
you'll not get it so easily, the tftp protocol is relatively easy, but put/get is not stateless, or at least if the file does not fit in a single packet, that is something like 512, but some extensions allow a bigger packet
the file on the wire is splited and you'll get a sequence of packets
each packet has a sequence number so the other end can send error for a specific packet
you should take a look at wikipedia page:
http://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol
here a sample code I wrote in 2005 but id does the specular thing (it sends a file)
it's python but reasonably similar to ruby :D
def send_file(self, addr, filesend, filesize, blocksize):
print '[send_file] Sending %s (size: %d - blksize: %d) to %s:%d' % (filesend, filesize, blocksize, addr[0], addr[1])
fd = open(filesend, 'rb')
for c in range(1, (filesize / blocksize) + 2):
hdr = pack('!H', DATA) + pack('!H', c)
indata = fd.read(blocksize)
if debug > 5: print '[send_file] [%s] Sending block %d size %d' % (filesend, c, len(indata))
self.s.sendto(hdr + indata, addr)
data, addr = self.s.recvfrom(1024)
res = unpack('!H', data[:2])[0]
data = data[2:]
if res != ACK:
print '[send_file] Transfer aborted: %s' % errors[res]
break
if debug > 5:
print '[send_file] [%s] Received ack for block %d' % (filesend, unpack('>H', data[:2])[0] + 1)
fd.close()
## End Transfer
pkt = pack('!H', DATA) + pack('>H', c) + NULL
self.s.sendto(pkt, addr)
if debug: print '[send_file] File send Done (%d)' % c
you can find constants in arpa/tftp.h (you need a unix or search online)
the sequence number is a big endian (network order) short !H format for struct pack
ruby has something like python struct in String class