PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/bspawner.erl

http://bspawner.googlecode.com/
Erlang | 336 lines | 154 code | 45 blank | 137 comment | 0 complexity | 0118bc17f7c57e1fc07e266c2c053992 MD5 | raw file
  1. %%%---------------------------------------------------------------------------------------
  2. %%% @author Bhasker V Kode <kode@returnable.org>
  3. %%% @copyright 2007 Bhasker V Kode
  4. %%% @doc Module for load-balancing and handling distributed spawn creation across multiple recognized nodes
  5. %%% @reference See <a href="http://bspawner.googlecode.com" target="_top">http://bspawner.googlecode.com</a> for more information.
  6. %%% Sourcecode from http://www.erlang.org/doc/getting_started/conc_prog.html located on <a href="http://erlang.org">erlang.org</a>
  7. %%% included in this project fall under the <a href="http://www.erlang.org/EPLICENSE">http://www.erlang.org/EPLICENSE</a> licence .
  8. %%% @end
  9. %%%---------------------------------------------------------------------------------------
  10. -module(bspawner).
  11. -author('bosky101@gmail.com').
  12. %% bspawner API
  13. -export([
  14. bmessage/2, %% alternative API for mesage,but balanced
  15. bspawn/1, %% can balance any M/F/A across nodes
  16. start_bspawner/0, %% alias handlers
  17. bspawner_state_loop/2, %%concurrency handlers
  18. indexof/2,moduloN/2 %% utilities
  19. ]).
  20. %% messenger/message-passing API
  21. -export([start_server/0, server/1, logon/1, logoff/0, message/2, client/2]).
  22. %%--------------------------------------------------------------------
  23. %% @spec () -> any()
  24. %% @doc Required to set aliases for process's
  25. %% @end
  26. %%--------------------------------------------------------------------
  27. start_bspawner()->
  28. A = spawn(?MODULE,bspawner_state_loop,[[],{0,0}]),
  29. global:unregister_name(spawn_bspawner),
  30. _AR= global:register_name(spawn_bspawner,A).
  31. %%--------------------------------------------------------------------
  32. %% @doc balanced-spawn creating function with API similiar to
  33. %% messenger:message(User,Message)
  34. %% @end
  35. %%--------------------------------------------------------------------
  36. bmessage(To,Data)->
  37. ?MODULE:bspawn({bspawner_request,{?MODULE,message,Data}}).
  38. %%--------------------------------------------------------------------
  39. %% @doc balanced-spawn creating function for any {Module,Function,Args}
  40. %% @end
  41. %%--------------------------------------------------------------------
  42. bspawn(Args)->
  43. global:send(spawn_bspawner,{self(),Args}),
  44. receive
  45. _R ->
  46. io:format("~n back in bspawner with result= ~p",[_R])
  47. end,
  48. io:format("~nbspawner done",[]).
  49. %%--------------------------------------------------------------------
  50. %% @doc maintains state information required to load-balance in a
  51. %% round-robin manner
  52. %% @end
  53. %%--------------------------------------------------------------------
  54. bspawner_state_loop(NodesList,{N,Index}) ->
  55. receive
  56. {From,{reset}} ->
  57. From!{ok},
  58. bspawner_state_loop([],{0,0});
  59. {From,{add_node,Node}} ->
  60. From!{ok},
  61. bspawner_state_loop([NodesList|Node],{N+1,Index});
  62. {From,{remove_node,Node}} ->
  63. %%Not implemented
  64. %%TODO implementation for removing node from nodelist
  65. EditedNodes = NodesList,
  66. From!{ok},
  67. bspawner_state_loop(EditedNodes,{N-1,Index});
  68. {From,{simulate}}->
  69. %%io:format("~n nodeslist is ~p",[NodesList]),
  70. From!{ok},
  71. bspawner_state_loop([servernode1,node1],{2,0});
  72. {From,{list}}->
  73. io:format("~n nodeslist is ~p",[NodesList]),
  74. From!{ok},
  75. bspawner_state_loop(NodesList,{N,Index});
  76. %%main spawn load-balancing
  77. {From,{bspawn,{M,F,A}}} ->
  78. TheChosenOne = indexof(NodesList, moduloN(Index,N) ),
  79. ?MODULE:message(TheChosenOne, {bspawner_request,servernode1 ,{M,F,A}} ),
  80. From!{ok,TheChosenOne},
  81. bspawner_state_loop(NodesList,{N,Index+1});
  82. _Else ->
  83. io:format("~n in bspawner else",[]),
  84. bspawner_state_loop(NodesList,{N,Index})
  85. end.
  86. %% utilities
  87. %% @doc utilities : To be able to select one of N-1 elements for any X
  88. %% @end
  89. moduloN(X,N)->
  90. %%X rem N.
  91. %%OR
  92. %%X - ( (X div N)*N).
  93. X rem N.
  94. %% utilities
  95. %% @doc utilities : to get the nth element of a list
  96. %% @end
  97. indexof([],_)->
  98. 0;
  99. indexof(L,N)->
  100. indexof(L,N,0).
  101. indexof([H|T],N,Ctr) when N =:= Ctr ->
  102. H;
  103. indexof([H|T],N,Ctr)->
  104. indexof(T,N,Ctr+1);
  105. indexof([],N,Ctr) ->
  106. -1.
  107. %%% Modified "messenger.erl"
  108. %%% Original available from http://www.erlang.org/doc/getting_started/conc_prog.html
  109. %%% Look for %%[bspawner]%% to indetify edits/modifications
  110. %%% Message passing utility.
  111. %%% User interface:
  112. %%% logon(Name)
  113. %%% One user at a time can log in from each Erlang node in the
  114. %%% system messenger: and choose a suitable Name. If the Name
  115. %%% is already logged in at another node or if someone else is
  116. %%% already logged in at the same node, login will be rejected
  117. %%% with a suitable error message.
  118. %%% logoff()
  119. %%% Logs off anybody at at node
  120. %%% message(ToName, Message)
  121. %%% sends Message to ToName. Error messages if the user of this
  122. %%% function is not logged on or if ToName is not logged on at
  123. %%% any node.
  124. %%%
  125. %%% One node in the network of Erlang nodes runs a server which maintains
  126. %%% data about the logged on users. The server is registered as "messenger"
  127. %%% Each node where there is a user logged on runs a client process registered
  128. %%% as "mess_client"
  129. %%%
  130. %%% Protocol between the client processes and the server
  131. %%% ----------------------------------------------------
  132. %%%
  133. %%% To server: {ClientPid, logon, UserName}
  134. %%% Reply {messenger, stop, user_exists_at_other_node} stops the client
  135. %%% Reply {messenger, logged_on} logon was successful
  136. %%%
  137. %%% To server: {ClientPid, logoff}
  138. %%% Reply: {messenger, logged_off}
  139. %%%
  140. %%% To server: {ClientPid, logoff}
  141. %%% Reply: no reply
  142. %%%
  143. %%% To server: {ClientPid, message_to, ToName, Message} send a message
  144. %%% Reply: {messenger, stop, you_are_not_logged_on} stops the client
  145. %%% Reply: {messenger, receiver_not_found} no user with this name logged on
  146. %%% Reply: {messenger, sent} Message has been sent (but no guarantee)
  147. %%%
  148. %%% To client: {message_from, Name, Message},
  149. %%%
  150. %%% Protocol between the "commands" and the client
  151. %%% ----------------------------------------------
  152. %%%
  153. %%% Started: messenger:client(Server_Node, Name)
  154. %%% To client: logoff
  155. %%% To client: {message_to, ToName, Message}
  156. %%%
  157. %%% Configuration: change the server_node() function to return the
  158. %%% name of the node where the messenger server runs
  159. %%@doc
  160. %% Change the function below to return the name of the node where the
  161. %% messenger server runs
  162. %%@end
  163. server_node() ->
  164. %%[bspawner]%%
  165. servernode1@bhasker.
  166. %% @doc
  167. %% This is the server process for the "messenger"
  168. %% the user list has the format [{ClientPid1, Name1},{ClientPid22, Name2},...]
  169. %% @end
  170. server(User_List) ->
  171. receive
  172. {From, logon, Name} ->
  173. New_User_List = server_logon(From, Name, User_List),
  174. server(New_User_List);
  175. {From, logoff} ->
  176. New_User_List = server_logoff(From, User_List),
  177. server(New_User_List);
  178. {From, message_to, To, Message} ->
  179. server_transfer(From, To, Message, User_List),
  180. io:format("~nserver:message_to:list is now: ~p~n", [User_List]),
  181. server(User_List);
  182. %%[bspawner]%%
  183. %%added custom guard
  184. {_From, bspawner_request, _To,{M,F,A}}->
  185. io:format("~nserver:bspawner_request:list is now ~p~n",[User_List]),
  186. global:send( spawn_bspawner, {spawn,M,F,A} ),
  187. server(User_List)
  188. end.
  189. %% @doc
  190. %% Start the messaging server
  191. %% @end
  192. start_server() ->
  193. %%[bspawner]%%
  194. %%setting M as ?MODULE rather than hardcoding
  195. register(messenger, spawn(?MODULE, server, [[]])),
  196. %%also adding start/alises required for spawn_bspawner
  197. start_bspawner().
  198. %% @doc Server adds a new user to the user list
  199. %% @end
  200. server_logon(From, Name, User_List) ->
  201. %% check if logged on anywhere else
  202. case lists:keymember(Name, 2, User_List) of
  203. true ->
  204. From ! {messenger, stop, user_exists_at_other_node}, %reject logon
  205. User_List;
  206. false ->
  207. From ! {messenger, logged_on},
  208. [{From, Name} | User_List] %add user to the list
  209. end.
  210. %% @doc Server deletes a user from the user list
  211. %% @end
  212. server_logoff(From, User_List) ->
  213. lists:keydelete(From, 1, User_List).
  214. %% @doc Server transfers a message between user
  215. %% @end
  216. server_transfer(From, To, Message, User_List) ->
  217. %% check that the user is logged on and who he is
  218. case lists:keysearch(From, 1, User_List) of
  219. false ->
  220. From ! {messenger, stop, you_are_not_logged_on};
  221. {value, {From, Name}} ->
  222. server_transfer(From, Name, To, Message, User_List)
  223. end.
  224. %% @doc If the user exists, send the message
  225. %% @end
  226. server_transfer(From, Name, To, Message, User_List) ->
  227. %% Find the receiver and send the message
  228. case lists:keysearch(To, 2, User_List) of
  229. false ->
  230. From ! {messenger, receiver_not_found};
  231. {value, {ToPid, To}} ->
  232. ToPid ! {message_from, Name, Message},
  233. From ! {messenger, sent}
  234. end.
  235. %% @doc To add/check a user with the userlist
  236. %% @end
  237. logon(Name) ->
  238. case whereis(mess_client) of
  239. undefined ->
  240. register(mess_client,
  241. spawn(?MODULE, client, [server_node(), Name]));
  242. %%[bspawner]%%
  243. %%global:send(spawn_bspawner, {add_node,Name});
  244. _ -> already_logged_on
  245. end.
  246. %% @doc Logoff a user
  247. %% @end
  248. logoff() ->
  249. mess_client ! logoff.
  250. %% @doc API to send message to a node
  251. %% @end
  252. message(ToName, Message) ->
  253. case whereis(mess_client) of % Test if the client is running
  254. undefined ->
  255. not_logged_on;
  256. _ -> mess_client ! {message_to, ToName, Message},
  257. ok
  258. end.
  259. %% @doc The client process which runs on each server node
  260. %% @end
  261. client(Server_Node, Name) ->
  262. {messenger, Server_Node} ! {self(), logon, Name},
  263. await_result(),
  264. client(Server_Node).
  265. client(Server_Node) ->
  266. receive
  267. logoff ->
  268. {messenger, Server_Node} ! {self(), logoff},
  269. exit(normal);
  270. {message_to, ToName, Message} ->
  271. {messenger, Server_Node} ! {self(), message_to, ToName, Message},
  272. await_result();
  273. %%[bspawner]%%
  274. %% custom receive of M,F,A on target as decide by bspawner_state_loop%%
  275. {message_from,FromName, {bspawner_request,ToNode,{M,F,A} }= CustomMessage } ->
  276. LocalResult = apply(M,F,A),
  277. io:format("~nHURRAY!!!~nbspawner_request message was ~p, and its appled local result is ~p",[CustomMessage,LocalResult]);
  278. {message_from, FromName, Message} ->
  279. io:format("~nclient:Message from ~p: ~p~n", [FromName, Message])
  280. end,
  281. client(Server_Node).
  282. %% @doc wait for a response from the server
  283. %% @end
  284. await_result() ->
  285. receive
  286. {messenger, stop, Why} -> % Stop the client
  287. io:format("~p~n", [Why]),
  288. exit(normal);
  289. {messenger, What} -> % Normal response
  290. io:format("~p~n", [What])
  291. end.