/src/bspawner.erl
Erlang | 336 lines | 154 code | 45 blank | 137 comment | 0 complexity | 0118bc17f7c57e1fc07e266c2c053992 MD5 | raw file
- %%%---------------------------------------------------------------------------------------
- %%% @author Bhasker V Kode <kode@returnable.org>
- %%% @copyright 2007 Bhasker V Kode
- %%% @doc Module for load-balancing and handling distributed spawn creation across multiple recognized nodes
- %%% @reference See <a href="http://bspawner.googlecode.com" target="_top">http://bspawner.googlecode.com</a> for more information.
- %%% Sourcecode from http://www.erlang.org/doc/getting_started/conc_prog.html located on <a href="http://erlang.org">erlang.org</a>
- %%% included in this project fall under the <a href="http://www.erlang.org/EPLICENSE">http://www.erlang.org/EPLICENSE</a> licence .
- %%% @end
-
- %%%---------------------------------------------------------------------------------------
-
- -module(bspawner).
- -author('bosky101@gmail.com').
-
- %% bspawner API
- -export([
- bmessage/2, %% alternative API for mesage,but balanced
- bspawn/1, %% can balance any M/F/A across nodes
- start_bspawner/0, %% alias handlers
- bspawner_state_loop/2, %%concurrency handlers
- indexof/2,moduloN/2 %% utilities
-
- ]).
-
- %% messenger/message-passing API
- -export([start_server/0, server/1, logon/1, logoff/0, message/2, client/2]).
-
-
- %%--------------------------------------------------------------------
- %% @spec () -> any()
- %% @doc Required to set aliases for process's
- %% @end
- %%--------------------------------------------------------------------
- start_bspawner()->
- A = spawn(?MODULE,bspawner_state_loop,[[],{0,0}]),
- global:unregister_name(spawn_bspawner),
- _AR= global:register_name(spawn_bspawner,A).
-
- %%--------------------------------------------------------------------
- %% @doc balanced-spawn creating function with API similiar to
- %% messenger:message(User,Message)
- %% @end
- %%--------------------------------------------------------------------
- bmessage(To,Data)->
- ?MODULE:bspawn({bspawner_request,{?MODULE,message,Data}}).
-
- %%--------------------------------------------------------------------
- %% @doc balanced-spawn creating function for any {Module,Function,Args}
- %% @end
- %%--------------------------------------------------------------------
- bspawn(Args)->
- global:send(spawn_bspawner,{self(),Args}),
- receive
- _R ->
- io:format("~n back in bspawner with result= ~p",[_R])
- end,
- io:format("~nbspawner done",[]).
-
- %%--------------------------------------------------------------------
- %% @doc maintains state information required to load-balance in a
- %% round-robin manner
- %% @end
- %%--------------------------------------------------------------------
- bspawner_state_loop(NodesList,{N,Index}) ->
- receive
- {From,{reset}} ->
- From!{ok},
- bspawner_state_loop([],{0,0});
- {From,{add_node,Node}} ->
- From!{ok},
- bspawner_state_loop([NodesList|Node],{N+1,Index});
- {From,{remove_node,Node}} ->
- %%Not implemented
- %%TODO implementation for removing node from nodelist
- EditedNodes = NodesList,
- From!{ok},
- bspawner_state_loop(EditedNodes,{N-1,Index});
- {From,{simulate}}->
- %%io:format("~n nodeslist is ~p",[NodesList]),
- From!{ok},
- bspawner_state_loop([servernode1,node1],{2,0});
- {From,{list}}->
- io:format("~n nodeslist is ~p",[NodesList]),
- From!{ok},
- bspawner_state_loop(NodesList,{N,Index});
- %%main spawn load-balancing
- {From,{bspawn,{M,F,A}}} ->
- TheChosenOne = indexof(NodesList, moduloN(Index,N) ),
- ?MODULE:message(TheChosenOne, {bspawner_request,servernode1 ,{M,F,A}} ),
- From!{ok,TheChosenOne},
- bspawner_state_loop(NodesList,{N,Index+1});
- _Else ->
- io:format("~n in bspawner else",[]),
- bspawner_state_loop(NodesList,{N,Index})
- end.
-
-
- %% utilities
- %% @doc utilities : To be able to select one of N-1 elements for any X
- %% @end
- moduloN(X,N)->
- %%X rem N.
- %%OR
- %%X - ( (X div N)*N).
- X rem N.
-
- %% utilities
- %% @doc utilities : to get the nth element of a list
- %% @end
- indexof([],_)->
- 0;
-
- indexof(L,N)->
- indexof(L,N,0).
-
- indexof([H|T],N,Ctr) when N =:= Ctr ->
- H;
-
- indexof([H|T],N,Ctr)->
- indexof(T,N,Ctr+1);
-
- indexof([],N,Ctr) ->
- -1.
-
- %%% Modified "messenger.erl"
- %%% Original available from http://www.erlang.org/doc/getting_started/conc_prog.html
- %%% Look for %%[bspawner]%% to indetify edits/modifications
-
- %%% Message passing utility.
- %%% User interface:
- %%% logon(Name)
- %%% One user at a time can log in from each Erlang node in the
- %%% system messenger: and choose a suitable Name. If the Name
- %%% is already logged in at another node or if someone else is
- %%% already logged in at the same node, login will be rejected
- %%% with a suitable error message.
- %%% logoff()
- %%% Logs off anybody at at node
- %%% message(ToName, Message)
- %%% sends Message to ToName. Error messages if the user of this
- %%% function is not logged on or if ToName is not logged on at
- %%% any node.
- %%%
- %%% One node in the network of Erlang nodes runs a server which maintains
- %%% data about the logged on users. The server is registered as "messenger"
- %%% Each node where there is a user logged on runs a client process registered
- %%% as "mess_client"
- %%%
- %%% Protocol between the client processes and the server
- %%% ----------------------------------------------------
- %%%
- %%% To server: {ClientPid, logon, UserName}
- %%% Reply {messenger, stop, user_exists_at_other_node} stops the client
- %%% Reply {messenger, logged_on} logon was successful
- %%%
- %%% To server: {ClientPid, logoff}
- %%% Reply: {messenger, logged_off}
- %%%
- %%% To server: {ClientPid, logoff}
- %%% Reply: no reply
- %%%
- %%% To server: {ClientPid, message_to, ToName, Message} send a message
- %%% Reply: {messenger, stop, you_are_not_logged_on} stops the client
- %%% Reply: {messenger, receiver_not_found} no user with this name logged on
- %%% Reply: {messenger, sent} Message has been sent (but no guarantee)
- %%%
- %%% To client: {message_from, Name, Message},
- %%%
- %%% Protocol between the "commands" and the client
- %%% ----------------------------------------------
- %%%
- %%% Started: messenger:client(Server_Node, Name)
- %%% To client: logoff
- %%% To client: {message_to, ToName, Message}
- %%%
- %%% Configuration: change the server_node() function to return the
- %%% name of the node where the messenger server runs
-
-
-
- %%@doc
- %% Change the function below to return the name of the node where the
- %% messenger server runs
- %%@end
- server_node() ->
- %%[bspawner]%%
- servernode1@bhasker.
-
- %% @doc
- %% This is the server process for the "messenger"
- %% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...]
- %% @end
- server(User_List) ->
- receive
- {From, logon, Name} ->
- New_User_List = server_logon(From, Name, User_List),
- server(New_User_List);
- {From, logoff} ->
- New_User_List = server_logoff(From, User_List),
- server(New_User_List);
-
- {From, message_to, To, Message} ->
- server_transfer(From, To, Message, User_List),
- io:format("~nserver:message_to:list is now: ~p~n", [User_List]),
- server(User_List);
-
- %%[bspawner]%%
- %%added custom guard
- {_From, bspawner_request, _To,{M,F,A}}->
- io:format("~nserver:bspawner_request:list is now ~p~n",[User_List]),
- global:send( spawn_bspawner, {spawn,M,F,A} ),
- server(User_List)
-
- end.
-
- %% @doc
- %% Start the messaging server
- %% @end
-
- start_server() ->
- %%[bspawner]%%
- %%setting M as ?MODULE rather than hardcoding
- register(messenger, spawn(?MODULE, server, [[]])),
- %%also adding start/alises required for spawn_bspawner
- start_bspawner().
-
-
- %% @doc Server adds a new user to the user list
- %% @end
- server_logon(From, Name, User_List) ->
- %% check if logged on anywhere else
- case lists:keymember(Name, 2, User_List) of
- true ->
- From ! {messenger, stop, user_exists_at_other_node}, %reject logon
- User_List;
- false ->
- From ! {messenger, logged_on},
- [{From, Name} | User_List] %add user to the list
- end.
-
- %% @doc Server deletes a user from the user list
- %% @end
- server_logoff(From, User_List) ->
- lists:keydelete(From, 1, User_List).
-
-
- %% @doc Server transfers a message between user
- %% @end
- server_transfer(From, To, Message, User_List) ->
- %% check that the user is logged on and who he is
- case lists:keysearch(From, 1, User_List) of
- false ->
- From ! {messenger, stop, you_are_not_logged_on};
- {value, {From, Name}} ->
- server_transfer(From, Name, To, Message, User_List)
- end.
-
- %% @doc If the user exists, send the message
- %% @end
- server_transfer(From, Name, To, Message, User_List) ->
- %% Find the receiver and send the message
- case lists:keysearch(To, 2, User_List) of
- false ->
- From ! {messenger, receiver_not_found};
- {value, {ToPid, To}} ->
- ToPid ! {message_from, Name, Message},
- From ! {messenger, sent}
- end.
-
-
- %% @doc To add/check a user with the userlist
- %% @end
- logon(Name) ->
- case whereis(mess_client) of
- undefined ->
- register(mess_client,
- spawn(?MODULE, client, [server_node(), Name]));
-
- %%[bspawner]%%
- %%global:send(spawn_bspawner, {add_node,Name});
- _ -> already_logged_on
- end.
-
- %% @doc Logoff a user
- %% @end
- logoff() ->
- mess_client ! logoff.
-
- %% @doc API to send message to a node
- %% @end
- message(ToName, Message) ->
- case whereis(mess_client) of % Test if the client is running
- undefined ->
- not_logged_on;
- _ -> mess_client ! {message_to, ToName, Message},
- ok
- end.
-
-
- %% @doc The client process which runs on each server node
- %% @end
- client(Server_Node, Name) ->
- {messenger, Server_Node} ! {self(), logon, Name},
- await_result(),
- client(Server_Node).
-
- client(Server_Node) ->
- receive
- logoff ->
- {messenger, Server_Node} ! {self(), logoff},
- exit(normal);
- {message_to, ToName, Message} ->
- {messenger, Server_Node} ! {self(), message_to, ToName, Message},
- await_result();
- %%[bspawner]%%
- %% custom receive of M,F,A on target as decide by bspawner_state_loop%%
- {message_from,FromName, {bspawner_request,ToNode,{M,F,A} }= CustomMessage } ->
- LocalResult = apply(M,F,A),
- io:format("~nHURRAY!!!~nbspawner_request message was ~p, and its appled local result is ~p",[CustomMessage,LocalResult]);
- {message_from, FromName, Message} ->
- io:format("~nclient:Message from ~p: ~p~n", [FromName, Message])
-
- end,
- client(Server_Node).
-
- %% @doc wait for a response from the server
- %% @end
- await_result() ->
- receive
- {messenger, stop, Why} -> % Stop the client
- io:format("~p~n", [Why]),
- exit(normal);
- {messenger, What} -> % Normal response
- io:format("~p~n", [What])
- end.
-