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
{ }; % Pick defaults
74 T
= etorrent_rate:now_secs(),
75 case ets:lookup(etorrent_recv_state
, {Id
, Who
}) of
78 [#rate_mgr
{ last_got
= unknown
}] ->
80 [#rate_mgr
{ last_got
= U
}] ->
81 T
- U
> ?DEFAULT_SNUB_TIME
85 fetch_recv_rate(Id
, Pid
) -> fetch_rate(etorrent_recv_state
, Id
, Pid
).
86 fetch_send_rate(Id
, Pid
) -> fetch_rate(etorrent_send_state
, Id
, Pid
).
88 recv_rate(Id
, Pid
, Rate
, Amount
) ->
89 recv_rate(Id
, Pid
, Rate
, Amount
, normal
).
91 recv_rate(Id
, Pid
, Rate
, Amount
, Update
) ->
92 gen_server:cast(?SERVER
, {recv_rate
, Id
, Pid
, Rate
, Amount
, Update
}).
94 send_rate(Id
, Pid
, Rate
, Amount
) ->
95 gen_server:cast(?SERVER
, {send_rate
, Id
, Pid
, Rate
, Amount
, normal
}).
98 gen_server:call(?SERVER
, global_rate
).
100 %%====================================================================
101 %% gen_server callbacks
102 %%====================================================================
104 %%--------------------------------------------------------------------
105 %% Function: init(Args) -> {ok, State} |
106 %% {ok, State, Timeout} |
109 %% Description: Initiates the server
110 %%--------------------------------------------------------------------
112 process_flag(trap_exit
, true
),
113 RTid
= ets:new(etorrent_recv_state
, [set
, protected
, named_table
,
114 {keypos
, #rate_mgr
.pid}]),
115 STid
= ets:new(etorrent_send_state
, [set
, protected
, named_table
,
116 {keypos
, #rate_mgr
.pid}]),
117 StTid
= ets:new(etorrent_peer_state
, [set
, protected
, named_table
,
118 {keypos
, #peer_state
.pid}]),
119 {ok
, #state
{ recv
= RTid
, send
= STid
, state
= StTid
,
120 global_recv
= etorrent_rate:init(?RATE_FUDGE
),
121 global_send
= etorrent_rate:init(?RATE_FUDGE
)}}.
123 %%--------------------------------------------------------------------
124 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
125 %% {reply, Reply, State, Timeout} |
126 %% {noreply, State} |
127 %% {noreply, State, Timeout} |
128 %% {stop, Reason, Reply, State} |
129 %% {stop, Reason, State}
130 %% Description: Handling call messages
131 %%--------------------------------------------------------------------
132 handle_call(global_rate
, _From
, S
) ->
133 #state
{ global_recv
= GR
, global_send
= GS
} = S
,
134 Reply
= { GR#peer_rate
.rate
, GS#peer_rate
.rate
},
136 handle_call(_Request
, _From
, State
) ->
138 {reply
, Reply
, State
}.
140 %%--------------------------------------------------------------------
141 %% Function: handle_cast(Msg, State) -> {noreply, State} |
142 %% {noreply, State, Timeout} |
143 %% {stop, Reason, State}
144 %% Description: Handling cast messages
145 %%--------------------------------------------------------------------
146 handle_cast({What
, Id
, Pid
}, S
) ->
147 ok
= alter_state(What
, Id
, Pid
),
149 handle_cast({What
, Id
, Who
, Rate
, Amount
, Update
}, S
) ->
150 NS
= alter_state(What
, Id
, Who
, Rate
, Amount
, Update
, S
),
152 handle_cast(_Msg
, State
) ->
155 %%--------------------------------------------------------------------
156 %% Function: handle_info(Info, State) -> {noreply, State} |
157 %% {noreply, State, Timeout} |
158 %% {stop, Reason, State}
159 %% Description: Handling all non call/cast messages
160 %%--------------------------------------------------------------------
161 handle_info({'DOWN', _Ref
, process, Pid
, _Reason
}, S
) ->
162 true
= ets:match_delete(etorrent_recv_state
, #rate_mgr
{ pid = {'_', Pid
}, _
='_'}),
163 true
= ets:match_delete(etorrent_send_state
, #rate_mgr
{ pid = {'_', Pid
}, _
='_'}),
164 true
= ets:match_delete(etorrent_peer_state
, #peer_state
{ pid = {'_', Pid
}, _
='_'}),
166 handle_info(_Info
, State
) ->
169 %%--------------------------------------------------------------------
170 %% Function: terminate(Reason, State) -> void()
171 %% Description: This function is called by a gen_server when it is about to
172 %% terminate. It should be the opposite of Module:init/1 and do any necessary
173 %% cleaning up. When it returns, the gen_server terminates with Reason.
174 %% The return value is ignored.
175 %%--------------------------------------------------------------------
176 terminate(_Reason
, S
) ->
177 true
= ets:delete(S#state
.recv
),
178 true
= ets:delete(S#state
.send
),
179 true
= ets:delete(S#state
.state
),
183 %%--------------------------------------------------------------------
184 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
185 %% Description: Convert process state when code is changed
186 %%--------------------------------------------------------------------
187 code_change(_OldVsn
, State
, _Extra
) ->
190 %%--------------------------------------------------------------------
191 %%% Internal functions
192 %%--------------------------------------------------------------------
194 alter_state(What
, Id
, Pid
) ->
195 _R
= case ets:lookup(etorrent_peer_state
, {Id
, Pid
}) of
197 ets:insert(etorrent_peer_state
,
199 #peer_state
{ pid = {Id
, Pid
},
200 choke_state
= choked
,
201 interest_state
= not_interested
,
202 local_choke
= true
})),
203 erlang:monitor(process, Pid
);
205 ets:insert(etorrent_peer_state
,
206 alter_record(What
, R
))
210 alter_record(What
, R
) ->
213 R#peer_state
{ choke_state
= choked
};
215 R#peer_state
{ choke_state
= unchoked
};
217 R#peer_state
{ interest_state
= interested
};
219 R#peer_state
{ interest_state
= not_interested
};
221 R#peer_state
{ local_choke
= true
};
223 R#peer_state
{ local_choke
= false
}
226 alter_state(What
, Id
, Who
, Rate
, Amount
, Update
, S
) ->
227 {T
, NS
} = case What
of
228 recv_rate
-> NR
= etorrent_rate:update(
231 { etorrent_recv_state
,
232 S#state
{ global_recv
= NR
}};
233 send_rate
-> NR
= etorrent_rate:update(
236 { etorrent_send_state
,
237 S#state
{ global_send
= NR
}}
239 _R
= case ets:lookup(T
, {Id
, Who
}) of
242 #rate_mgr
{ pid = {Id
, Who
},
243 last_got
= case Update
of
245 last_update
-> etorrent_rate:now_secs()
248 erlang:monitor(process, Who
);
250 ets:insert(T
, R#rate_mgr
{ rate
= Rate
})
254 fetch_rate(Where
, Id
, Pid
) ->
255 case ets:lookup(Where
, {Id
, Pid
}) of
258 [R
] -> R#rate_mgr
.rate