1 %%%-------------------------------------------------------------------
2 %%% File : etorrent_rate_mgr.erl
3 %%% Author : Jesper Louis Andersen <jlouis@ogre.home>
4 %%% Description : Rate management process
6 %%% Created : 17 Jul 2008 by Jesper Louis Andersen <jlouis@ogre.home>
7 %%%-------------------------------------------------------------------
8 -module(etorrent_rate_mgr
).
10 -include("peer_state.hrl").
11 -include("rate_mgr.hrl").
12 -include("etorrent_rate.hrl").
14 -behaviour(gen_server
).
16 -define(DEFAULT_SNUB_TIME
, 30).
19 -export([start_link
/0,
21 choke
/2, unchoke
/2, interested
/2, not_interested
/2,
22 local_choke
/2, local_unchoke
/2,
24 recv_rate
/5, recv_rate
/4, send_rate
/4,
34 %% gen_server callbacks
35 -export([init
/1, handle_call
/3, handle_cast
/2, handle_info
/2,
36 terminate
/2, code_change
/3]).
38 -record(state
, { recv
,
45 -define(SERVER
, ?MODULE
).
47 %%====================================================================
49 %%====================================================================
50 %%--------------------------------------------------------------------
51 %% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
52 %% Description: Starts the server
53 %%--------------------------------------------------------------------
55 gen_server:start_link({local
, ?SERVER
}, ?MODULE
, [], []).
57 %% Send state information
58 choke(Id
, Pid
) -> gen_server:cast(?SERVER
, {choke
, Id
, Pid
}).
59 unchoke(Id
, Pid
) -> gen_server:cast(?SERVER
, {unchoke
, Id
, Pid
}).
60 interested(Id
, Pid
) -> gen_server:cast(?SERVER
, {interested
, Id
, Pid
}).
61 not_interested(Id
, Pid
) -> gen_server:cast(?SERVER
, {not_interested
, Id
, Pid
}).
62 local_choke(Id
, Pid
) -> gen_server:cast(?SERVER
, {local_choke
, Id
, Pid
}).
63 local_unchoke(Id
, Pid
) -> gen_server:cast(?SERVER
, {local_unchoke
, Id
, Pid
}).
65 select_state(Id
, Who
) ->
66 case ets:lookup(etorrent_peer_state
, {Id
, Who
}) of
68 #peer_state
{ interest_state
= not_interested
,
76 T
= etorrent_rate:now_secs(),
77 case ets:lookup(etorrent_recv_state
, {Id
, Who
}) of
80 [#rate_mgr
{ last_got
= unknown
}] ->
82 [#rate_mgr
{ last_got
= U
}] ->
83 T
- U
> ?DEFAULT_SNUB_TIME
87 fetch_recv_rate(Id
, Pid
) -> fetch_rate(etorrent_recv_state
, Id
, Pid
).
88 fetch_send_rate(Id
, Pid
) -> fetch_rate(etorrent_send_state
, Id
, Pid
).
90 recv_rate(Id
, Pid
, Rate
, Amount
) ->
91 recv_rate(Id
, Pid
, Rate
, Amount
, normal
).
93 recv_rate(Id
, Pid
, Rate
, Amount
, Update
) ->
94 gen_server:cast(?SERVER
, {recv_rate
, Id
, Pid
, Rate
, Amount
, Update
}).
96 send_rate(Id
, Pid
, Rate
, Amount
) ->
97 gen_server:cast(?SERVER
, {send_rate
, Id
, Pid
, Rate
, Amount
, normal
}).
100 gen_server:call(?SERVER
, global_rate
).
102 %%====================================================================
103 %% gen_server callbacks
104 %%====================================================================
106 %%--------------------------------------------------------------------
107 %% Function: init(Args) -> {ok, State} |
108 %% {ok, State, Timeout} |
111 %% Description: Initiates the server
112 %%--------------------------------------------------------------------
114 process_flag(trap_exit
, true
),
115 RTid
= ets:new(etorrent_recv_state
, [set
, protected
, named_table
,
116 {keypos
, #rate_mgr
.pid}]),
117 STid
= ets:new(etorrent_send_state
, [set
, protected
, named_table
,
118 {keypos
, #rate_mgr
.pid}]),
119 StTid
= ets:new(etorrent_peer_state
, [set
, protected
, named_table
,
120 {keypos
, #peer_state
.pid}]),
121 {ok
, #state
{ recv
= RTid
, send
= STid
, state
= StTid
,
122 global_recv
= etorrent_rate:init(?RATE_FUDGE
),
123 global_send
= etorrent_rate:init(?RATE_FUDGE
)}}.
125 %%--------------------------------------------------------------------
126 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
127 %% {reply, Reply, State, Timeout} |
128 %% {noreply, State} |
129 %% {noreply, State, Timeout} |
130 %% {stop, Reason, Reply, State} |
131 %% {stop, Reason, State}
132 %% Description: Handling call messages
133 %%--------------------------------------------------------------------
134 handle_call(global_rate
, _From
, S
) ->
135 #state
{ global_recv
= GR
, global_send
= GS
} = S
,
136 Reply
= { GR#peer_rate
.rate
, GS#peer_rate
.rate
},
138 handle_call(_Request
, _From
, State
) ->
140 {reply
, Reply
, State
}.
142 %%--------------------------------------------------------------------
143 %% Function: handle_cast(Msg, State) -> {noreply, State} |
144 %% {noreply, State, Timeout} |
145 %% {stop, Reason, State}
146 %% Description: Handling cast messages
147 %%--------------------------------------------------------------------
148 handle_cast({What
, Id
, Pid
}, S
) ->
149 ok
= alter_state(What
, Id
, Pid
),
151 handle_cast({What
, Id
, Who
, Rate
, Amount
, Update
}, S
) ->
152 NS
= alter_state(What
, Id
, Who
, Rate
, Amount
, Update
, S
),
154 handle_cast(_Msg
, State
) ->
157 %%--------------------------------------------------------------------
158 %% Function: handle_info(Info, State) -> {noreply, State} |
159 %% {noreply, State, Timeout} |
160 %% {stop, Reason, State}
161 %% Description: Handling all non call/cast messages
162 %%--------------------------------------------------------------------
163 handle_info({'DOWN', _Ref
, process, Pid
, _Reason
}, S
) ->
164 true
= ets:match_delete(etorrent_recv_state
, #rate_mgr
{ pid = {'_', Pid
}, _
='_'}),
165 true
= ets:match_delete(etorrent_send_state
, #rate_mgr
{ pid = {'_', Pid
}, _
='_'}),
166 true
= ets:match_delete(etorrent_peer_state
, #peer_state
{ pid = {'_', Pid
}, _
='_'}),
168 handle_info(_Info
, State
) ->
171 %%--------------------------------------------------------------------
172 %% Function: terminate(Reason, State) -> void()
173 %% Description: This function is called by a gen_server when it is about to
174 %% terminate. It should be the opposite of Module:init/1 and do any necessary
175 %% cleaning up. When it returns, the gen_server terminates with Reason.
176 %% The return value is ignored.
177 %%--------------------------------------------------------------------
178 terminate(_Reason
, S
) ->
179 true
= ets:delete(S#state
.recv
),
180 true
= ets:delete(S#state
.send
),
181 true
= ets:delete(S#state
.state
),
185 %%--------------------------------------------------------------------
186 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
187 %% Description: Convert process state when code is changed
188 %%--------------------------------------------------------------------
189 code_change(_OldVsn
, State
, _Extra
) ->
192 %%--------------------------------------------------------------------
193 %%% Internal functions
194 %%--------------------------------------------------------------------
196 alter_state(What
, Id
, Pid
) ->
197 _R
= case ets:lookup(etorrent_peer_state
, {Id
, Pid
}) of
199 ets:insert(etorrent_peer_state
,
201 #peer_state
{ pid = {Id
, Pid
},
202 choke_state
= choked
,
203 interest_state
= not_interested
,
204 local_choke
= true
})),
205 erlang:monitor(process, Pid
);
207 ets:insert(etorrent_peer_state
,
208 alter_record(What
, R
))
212 alter_record(What
, R
) ->
215 R#peer_state
{ choke_state
= choked
};
217 R#peer_state
{ choke_state
= unchoked
};
219 R#peer_state
{ interest_state
= interested
};
221 R#peer_state
{ interest_state
= not_interested
};
223 R#peer_state
{ local_choke
= true
};
225 R#peer_state
{ local_choke
= false
}
228 alter_state(What
, Id
, Who
, Rate
, Amount
, Update
, S
) ->
229 {T
, NS
} = case What
of
230 recv_rate
-> NR
= etorrent_rate:update(
233 { etorrent_recv_state
,
234 S#state
{ global_recv
= NR
}};
235 send_rate
-> NR
= etorrent_rate:update(
238 { etorrent_send_state
,
239 S#state
{ global_send
= NR
}}
241 _R
= case ets:lookup(T
, {Id
, Who
}) of
244 #rate_mgr
{ pid = {Id
, Who
},
245 last_got
= case Update
of
247 last_update
-> etorrent_rate:now_secs()
250 erlang:monitor(process, Who
);
252 ets:insert(T
, R#rate_mgr
{ rate
= Rate
})
256 fetch_rate(Where
, Id
, Pid
) ->
257 case ets:lookup(Where
, {Id
, Pid
}) of
260 [R
] -> R#rate_mgr
.rate