db501b761fd008979cfe09ed098f9b5dd722b41a
[oweals/gnunet.git] / src / fs / gnunet-service-fs_pe.c
1 /*
2      This file is part of GNUnet.
3      (C) 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 /**
22  * @file fs/gnunet-service-fs_pe.c
23  * @brief API to manage query plan
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-fs_cp.h"
28 #include "gnunet-service-fs_pe.h"
29
30 /**
31  * Hash map from peer identities to GNUNET_CONTAINER_Heap's with
32  * pending requests as entries.
33  */
34 static struct GNUNET_CONTAINER_MultiHashMap *plans;
35
36
37 /**
38  * Get the size of the request queue for the given peer.
39  *
40  * @param cp connected peer to query 
41  * @return number of entries in this peer's request queue
42  */
43 static struct GNUNET_CONTAINER_Heap *
44 get_heap (const struct GSF_ConnectedPeer *cp)
45 {
46   struct GNUNET_PeerIdentity id;
47
48   GSF_connected_peer_get_identity_ (cp, &id);
49   return GNUNET_CONTAINER_multihashmap_get (plans,
50                                             &id.hashPubKey);
51 }
52
53
54 /**
55  * Create a new query plan entry.
56  *
57  * @param cp peer with the entry
58  * @param pr request with the entry
59  * @param weight determines position of the entry in the cp queue,
60  *        lower weights are earlier in the queue
61  */
62 void
63 GSF_plan_add_ (const struct GSF_ConnectedPeer *cp,
64                struct GSF_PendingRequest *pr,
65                GNUNET_CONTAINER_HeapCostType weight)
66 {
67   struct GNUNET_PeerIdentity id;
68   struct GNUNET_CONTAINER_Heap *h;
69
70   GSF_connected_peer_get_identity_ (cp, &id);
71   h = GNUNET_CONTAINER_multihashmap_get (plans,
72                                          &id.hashPubKey);
73   if (NULL == h)
74     {
75       h = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
76       GNUNET_CONTAINER_multihashmap_put (plans,
77                                          &id.hashPubKey,
78                                          h,
79                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
80     }
81   GNUNET_CONTAINER_heap_insert (h,
82                                 pr,
83                                 weight);
84 }
85
86
87 /**
88  * Notify the plan about a peer being no longer available;
89  * destroy all entries associated with this peer.
90  *
91  * @param cp connected peer 
92  */
93 void
94 GSF_plan_notify_peer_disconnect_ (const struct GSF_ConnectedPeer *cp)
95 {
96   struct GNUNET_PeerIdentity id;
97   struct GNUNET_CONTAINER_Heap *h;
98
99   GSF_connected_peer_get_identity_ (cp, &id);
100   h = GNUNET_CONTAINER_multihashmap_get (plans,
101                                          &id.hashPubKey);
102   GNUNET_CONTAINER_multihashmap_remove (plans,
103                                         &id.hashPubKey,
104                                         h);
105   GNUNET_CONTAINER_heap_destroy (h);
106 }
107
108
109 /**
110  * Closure for 'find_request'.
111  */
112 struct FindRequestClosure
113 {
114   /**
115    * Place to store the node that was found (NULL for none).
116    */
117   struct GNUNET_CONTAINER_HeapNode *node;
118
119   /**
120    * Value we're looking for
121    */
122   const struct GSF_PendingRequest *pr;
123 };
124
125
126 /**
127  * Find a heap node where the value matches the
128  * pending request given in the closure.
129  *
130  * @param cls the 'struct FindRequestClosure'
131  * @param node heap structure we're looking for on a match
132  * @param element the pending request stored in the heap
133  * @param cost weight of the request
134  * @return GNUNET_YES to continue looking
135  */
136 static int
137 find_request (void *cls,
138               struct GNUNET_CONTAINER_HeapNode *node,
139               void *element,
140               GNUNET_CONTAINER_HeapCostType cost)
141 {
142   struct FindRequestClosure *frc = cls;
143   struct GSF_PendingRequest *pr = element;
144
145   if (pr == frc->pr)
146     {
147       frc->node = node;
148       return GNUNET_NO;
149     }
150   return GNUNET_YES;
151 }
152
153
154 /**
155  * Remove the given request from all heaps. * 
156  *
157  * @param cls 'struct GSF_PendingRequest' to purge
158  * @param key identity of the peer we're currently looking at (unused)
159  * @param value request heap for the given peer to search for the 'cls'
160  * @return GNUNET_OK (continue iteration)
161  */
162 static int
163 remove_request (void *cls,
164                 const GNUNET_HashCode *key,
165                 void *value)
166 {
167   const struct GSF_PendingRequest *pr = cls;
168   struct GNUNET_CONTAINER_Heap *h = value;
169   struct FindRequestClosure frc;
170
171   frc.pr = pr;
172   do
173     {
174       frc.node = NULL;
175       GNUNET_CONTAINER_heap_iterate (h, &find_request, &frc);
176       if (frc.node != NULL)
177         GNUNET_CONTAINER_heap_remove_node (h, frc.node);
178     }
179   while (NULL != frc.node);
180   return GNUNET_OK;
181 }
182
183
184 /**
185  * Notify the plan about a request being done; destroy all entries
186  * associated with this request.  Note that this implementation is
187  * currently terribly inefficient (O(n)) and could instead be done in
188  * O(1).  But for now, I first want to see it work correctly...
189  *
190  * @param pr request that is done
191  */
192 void
193 GSF_plan_notify_request_done_ (const struct GSF_PendingRequest *pr)
194 {
195   GNUNET_CONTAINER_multihashmap_iterate (plans,
196                                          &remove_request,
197                                          (void*) pr);
198 }
199
200
201 /**
202  * Get the lowest-weight entry for the respective peer
203  * from the plan.  Removes the entry from the plan's queue.
204  *
205  * @param cp connected peer to query for the next request
206  * @return NULL if the queue for this peer is empty
207  */
208 struct GSF_PendingRequest *
209 GSF_plan_get_ (const struct GSF_ConnectedPeer *cp)
210 {
211   struct GNUNET_CONTAINER_Heap *h;
212
213   h = get_heap (cp);
214   if (NULL == h)
215     return NULL;
216   return GNUNET_CONTAINER_heap_remove_root (h);
217 }
218
219
220 /**
221  * Get the size of the request queue for the given peer.
222  *
223  * @param cp connected peer to query 
224  * @return number of entries in this peer's request queue
225  */
226 unsigned int
227 GSF_plan_size_ (const struct GSF_ConnectedPeer *cp)
228 {
229   struct GNUNET_CONTAINER_Heap *h;
230
231   h = get_heap (cp);
232   if (NULL == h)
233     return 0;
234   return GNUNET_CONTAINER_heap_get_size (h);
235 }
236
237
238
239 /**
240  * Initialize plan subsystem.
241  */
242 void
243 GSF_plan_init ()
244 {
245   plans = GNUNET_CONTAINER_multihashmap_create (256);
246 }
247
248
249 /**
250  * Shutdown plan subsystem.
251  */
252 void
253 GSF_plan_done ()
254 {
255   GNUNET_assert (0 == 
256                  GNUNET_CONTAINER_multihashmap_size (plans));
257   GNUNET_CONTAINER_multihashmap_destroy (plans);
258 }
259
260
261
262 /* end of gnunet-service-fs_pe.h */