-patch from #1972 to display disconnects instead of exiting
[oweals/gnunet.git] / src / ats / ats_api_peer_change_preference.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file ats/ats_api_peer_change_preference.c
22  * @brief automatic transport selection API, preference management
23  * @author Christian Grothoff
24  * @author Matthias Wachs
25  *
26  * TODO:
27  * - write test case
28  * - extend API to get performance data
29  * - implement simplistic strategy based on say 'lowest latency' or strict ordering
30  * - extend API to get peer preferences, implement proportional bandwidth assignment
31  * - re-implement API against a real ATS service (!)
32  */
33 #include "platform.h"
34 #include "gnunet_ats_service.h"
35 #include "ats_api.h"
36
37 struct GNUNET_ATS_InformationRequestContext
38 {
39
40   /**
41    * Our connection to the service.
42    */
43   struct GNUNET_ATS_SchedulingHandle *h;
44
45   int32_t amount;
46
47   uint64_t preference;
48
49   GNUNET_ATS_PeerConfigurationInfoCallback info;
50
51   void *info_cls;
52
53   struct GNUNET_PeerIdentity peer;
54
55   GNUNET_SCHEDULER_TaskIdentifier task;
56
57 };
58
59
60 static void
61 exec_pcp (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
62 {
63   struct GNUNET_ATS_InformationRequestContext *irc = cls;
64   int32_t want_reserv;
65   int32_t got_reserv;
66   struct GNUNET_TIME_Relative rdelay;
67   struct AllocationRecord *ar;
68
69   rdelay = GNUNET_TIME_UNIT_ZERO;
70   want_reserv = irc->amount;
71   ar = GNUNET_CONTAINER_multihashmap_get (irc->h->peers, &irc->peer.hashPubKey);
72   if (NULL == ar)
73   {
74     /* attempt to change preference on peer that is not connected */
75     /* FIXME: this can happen if the 'service' didn't yet tell us about
76      * a new connection, fake it! */
77     irc->info (irc->info_cls, &irc->peer, want_reserv, rdelay);
78     GNUNET_free (irc);
79     return;
80   }
81   if (want_reserv < 0)
82   {
83     got_reserv = want_reserv;
84   }
85   else if (want_reserv > 0)
86   {
87     rdelay =
88         GNUNET_BANDWIDTH_tracker_get_delay (&ar->available_recv_window,
89                                             want_reserv);
90     if (rdelay.rel_value == 0)
91       got_reserv = want_reserv;
92     else
93       got_reserv = 0;           /* all or nothing */
94   }
95   else
96     got_reserv = 0;
97   GNUNET_BANDWIDTH_tracker_consume (&ar->available_recv_window, got_reserv);
98
99   irc->info (irc->info_cls, &irc->peer, got_reserv, rdelay);
100   GNUNET_free (irc);
101 }
102
103
104 /**
105  * Obtain statistics and/or change preferences for the given peer.
106  *
107  * @param h core handle
108  * @param peer identifies the peer
109  * @param amount reserve N bytes for receiving, negative
110  *                amounts can be used to undo a (recent) reservation;
111  * @param preference increase incoming traffic share preference by this amount;
112  *                in the absence of "amount" reservations, we use this
113  *                preference value to assign proportional bandwidth shares
114  *                to all connected peers
115  * @param info function to call with the resulting configuration information
116  * @param info_cls closure for info
117  * @return NULL on error
118  */
119 struct GNUNET_ATS_InformationRequestContext *
120 GNUNET_ATS_peer_change_preference (struct GNUNET_ATS_SchedulingHandle *h,
121                                    const struct GNUNET_PeerIdentity *peer,
122                                    int32_t amount, uint64_t preference,
123                                    GNUNET_ATS_PeerConfigurationInfoCallback
124                                    info, void *info_cls)
125 {
126   struct GNUNET_ATS_InformationRequestContext *irc;
127
128   irc = GNUNET_malloc (sizeof (struct GNUNET_ATS_InformationRequestContext));
129   irc->h = h;
130   irc->peer = *peer;
131   irc->amount = amount;
132   irc->preference = preference;
133   irc->info = info;
134   irc->info_cls = info_cls;
135   irc->task = GNUNET_SCHEDULER_add_now (&exec_pcp, irc);
136   return irc;
137 }
138
139
140 /**
141  * Cancel request for getting information about a peer.
142  * Note that an eventual change in preference, trust or bandwidth
143  * assignment MAY have already been committed at the time,
144  * so cancelling a request is NOT sure to undo the original
145  * request.  The original request may or may not still commit.
146  * The only thing cancellation ensures is that the callback
147  * from the original request will no longer be called.
148  *
149  * @param irc context returned by the original GNUNET_ATS_peer_get_info call
150  */
151 void
152 GNUNET_ATS_peer_change_preference_cancel (struct
153                                           GNUNET_ATS_InformationRequestContext
154                                           *irc)
155 {
156   GNUNET_SCHEDULER_cancel (irc->task);
157   GNUNET_free (irc);
158 }
159
160
161 #if 0
162 /* old CORE API implementation follows for future reference */
163 struct GNUNET_ATS_InformationRequestContext
164 {
165
166   /**
167    * Our connection to the service.
168    */
169   struct GNUNET_ATS_SchedulingHandle *h;
170
171   /**
172    * Link to control message, NULL if CM was sent.
173    */
174   struct ControlMessage *cm;
175
176   /**
177    * Link to peer record.
178    */
179   struct PeerRecord *pr;
180 };
181
182
183 /**
184  * CM was sent, remove link so we don't double-free.
185  *
186  * @param cls the 'struct GNUNET_ATS_InformationRequestContext'
187  * @param success were we successful?
188  */
189 static void
190 change_preference_send_continuation (void *cls, int success)
191 {
192   struct GNUNET_ATS_InformationRequestContext *irc = cls;
193
194   irc->cm = NULL;
195 }
196
197
198 /**
199  * Obtain statistics and/or change preferences for the given peer.
200  *
201  * @param h core handle
202  * @param peer identifies the peer
203  * @param amount reserve N bytes for receiving, negative
204  *                amounts can be used to undo a (recent) reservation;
205  * @param preference increase incoming traffic share preference by this amount;
206  *                in the absence of "amount" reservations, we use this
207  *                preference value to assign proportional bandwidth shares
208  *                to all connected peers
209  * @param info function to call with the resulting configuration information
210  * @param info_cls closure for info
211  * @return NULL on error
212  */
213 struct GNUNET_ATS_InformationRequestContext *
214 GNUNET_ATS_peer_change_preference (struct GNUNET_ATS_SchedulingHandle *h,
215                                    const struct GNUNET_PeerIdentity *peer,
216                                    int32_t amount, uint64_t preference,
217                                    GNUNET_ATS_PeerConfigurationInfoCallback
218                                    info, void *info_cls)
219 {
220   struct GNUNET_ATS_InformationRequestContext *irc;
221   struct PeerRecord *pr;
222   struct RequestInfoMessage *rim;
223   struct ControlMessage *cm;
224
225   pr = GNUNET_CONTAINER_multihashmap_get (h->peers, &peer->hashPubKey);
226   if (NULL == pr)
227   {
228     /* attempt to change preference on peer that is not connected */
229     GNUNET_assert (0);
230     return NULL;
231   }
232   if (pr->pcic != NULL)
233   {
234     /* second change before first one is done */
235     GNUNET_break (0);
236     return NULL;
237   }
238   irc = GNUNET_malloc (sizeof (struct GNUNET_ATS_InformationRequestContext));
239   irc->h = h;
240   irc->pr = pr;
241   cm = GNUNET_malloc (sizeof (struct ControlMessage) +
242                       sizeof (struct RequestInfoMessage));
243   cm->cont = &change_preference_send_continuation;
244   cm->cont_cls = irc;
245   irc->cm = cm;
246   rim = (struct RequestInfoMessage *) &cm[1];
247   rim->header.size = htons (sizeof (struct RequestInfoMessage));
248   rim->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_REQUEST_INFO);
249   rim->rim_id = htonl (pr->rim_id = h->rim_id_gen++);
250   rim->reserved = htonl (0);
251   rim->reserve_inbound = htonl (amount);
252   rim->preference_change = GNUNET_htonll (preference);
253   rim->peer = *peer;
254 #if DEBUG_ATS
255   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
256               "Queueing CHANGE PREFERENCE request for peer `%s' with RIM %u\n",
257               GNUNET_i2s (peer), (unsigned int) pr->rim_id);
258 #endif
259   GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
260                                     h->control_pending_tail, cm);
261   pr->pcic = info;
262   pr->pcic_cls = info_cls;
263   pr->pcic_ptr = irc;           /* for free'ing irc */
264   if (NULL != h->client)
265     trigger_next_request (h, GNUNET_NO);
266   return irc;
267 }
268
269
270 /**
271  * Cancel request for getting information about a peer.
272  * Note that an eventual change in preference, trust or bandwidth
273  * assignment MAY have already been committed at the time,
274  * so cancelling a request is NOT sure to undo the original
275  * request.  The original request may or may not still commit.
276  * The only thing cancellation ensures is that the callback
277  * from the original request will no longer be called.
278  *
279  * @param irc context returned by the original GNUNET_ATS_peer_get_info call
280  */
281 void
282 GNUNET_ATS_peer_change_preference_cancel (struct
283                                           GNUNET_ATS_InformationRequestContext
284                                           *irc)
285 {
286   struct GNUNET_ATS_SchedulingHandle *h = irc->h;
287   struct PeerRecord *pr = irc->pr;
288
289   GNUNET_assert (pr->pcic_ptr == irc);
290   if (irc->cm != NULL)
291   {
292     GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
293                                  h->control_pending_tail, irc->cm);
294     GNUNET_free (irc->cm);
295   }
296   pr->pcic = NULL;
297   pr->pcic_cls = NULL;
298   pr->pcic_ptr = NULL;
299   GNUNET_free (irc);
300 }
301 #endif
302
303 /* end of ats_api_peer_change_preference.c */