Skip to content

Commit

Permalink
Adding examples on how to get/store callback state
Browse files Browse the repository at this point in the history
  • Loading branch information
bigkevmcd committed Jan 9, 2011
1 parent 40789f4 commit 7d5ad78
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 21 deletions.
79 changes: 59 additions & 20 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -28,46 +28,85 @@ creating the listen socket. These options will also be inherited by the client c
for [gen_tcp](http://www.erlang.org/doc/man/gen_tcp.html "gen_tcp manpage") and [inet](http://www.erlang.org/doc/man/inet.html "inet manpage") for more information on
socket options.

2b. <code>new_connection/2</code> is called every time a new connection is accepted. It is called with the newly
2b. <code>new_connection/4</code> is called every time a new connection is accepted. It is called with the newly
connected socket and the server's current state.

Here's a complete example which should give you an idea on how to use gen_nb_server:

<pre>
-module(example).

-export([start_link/2]).
-export([start_link/0,
add_listener/3,
remove_listener/3]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-export([terminate/2, sock_opts/0, new_connection/2]).
-export([init/2, handle_call/3, handle_cast/2, handle_info/2]).
-export([terminate/2, sock_opts/0, new_connection/4]).

-behavior(gen_nb_server).

start_link(IpAddr, Port) ->
gen_nb_server:start_link(?MODULE, IpAddr, Port, []).

init([]) ->
{ok, []}.

start_link() ->
gen_nb_server:start_link(?MODULE, []).

add_listener(Pid, IpAddr, Port) ->
gen_server:call(Pid, {add_listener, IpAddr, Port}).

remove_listener(Pid, IpAddr, Port) ->
gen_server:call(Pid, {remove_listener, IpAddr, Port}).

init([], State) ->
{ok, State}.

handle_call({add_listener, IpAddr, Port}, _From, State) ->
case gen_nb_server:add_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
{reply, ok, State1};
Error ->
{reply, Error, State}
end;
handle_call({remove_listener, IpAddr, Port}, _From, State) ->
case gen_nb_server:remove_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
{reply, ok, State1};
Error ->
{reply, Error, State}
end;
handle_call(_Msg, _From, State) ->
{reply, ignored, State}.
{reply, ignored, State}.

handle_cast(_Msg, State) ->
{noreply, State}.
{noreply, State}.

handle_info({tcp, Sock, Data}, State) ->
Me = self(),
P = spawn(fun() -> worker(Me, Sock, Data) end),
gen_tcp:controlling_process(Sock, P),

This comment has been minimized.

Copy link
@eriksoe

eriksoe Sep 14, 2011

Isn't there a race between the two calls of controlling_process() - the one in the nb_server and the one in the worker?
If the server process is scheduled out right after spawning, and the worker runs to conclusion before the server continues, then the (now extinct) worker ends up controlling the socket.

{noreply, State};

handle_info(_Msg, State) ->
{noreply, State}.
{noreply, State}.

terminate(_Reason, _State) ->
ok.
ok.

sock_opts() ->
[binary, {active, once}, {packet, 0}].

new_connection(Sock, State) ->
gen_tcp:send(Sock, list_to_binary(io_lib:format("No soup for you!~n", []))),
gen_tcp:close(Sock),
{ok, State}.
[binary, {active, once}, {packet, 0}].

new_connection(_IpAddr, _Port, Sock, State) ->
Me = self(),
P = spawn(fun() -> worker(Me, Sock) end),
gen_tcp:controlling_process(Sock, P),
{ok, State}.

worker(Owner, Sock) ->
gen_tcp:send(Sock, "Hello\n"),
inet:setopts(Sock, [{active, once}]),
gen_tcp:controlling_process(Sock, Owner).

worker(Owner, Sock, Data) ->
gen_tcp:send(Sock, Data),
inet:setopts(Sock, [{active, once}]),
gen_tcp:controlling_process(Sock, Owner).
</pre>

Note: This code is also available in priv/example.
Binary file modified priv/example/ebin/example.beam
Binary file not shown.
7 changes: 6 additions & 1 deletion priv/example/src/example.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ remove_listener(Pid, IpAddr, Port) ->
gen_server:call(Pid, {remove_listener, IpAddr, Port}).

init([], State) ->
{ok, State}.
%% Example storing callback module specific state
%% This modifies the server state
{ok, gen_nb_server:store_cb_state([], State)}.

handle_call({add_listener, IpAddr, Port}, _From, State) ->
%% Example of getting callback module state
%% Not used here, but just an example
[] = gen_nb_server:get_cb_state(State),
case gen_nb_server:add_listen_socket({IpAddr, Port}, State) of
{ok, State1} ->
{reply, ok, State1};
Expand Down

0 comments on commit 7d5ad78

Please sign in to comment.