6b714b5e6060687f42556555210cba3957b4d788
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_cache.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2008--2013 GNUnet e.V.
4
5   GNUnet is free software: you can redistribute it and/or modify it
6   under the terms of the GNU Affero General Public License as published
7   by the Free Software Foundation, either version 3 of the License,
8   or (at your 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   Affero General Public License for more details.
14  
15   You should have received a copy of the GNU Affero General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file testbed/gnunet-service-testbed_cache.c
21  * @brief testbed cache implementation
22  * @author Sree Harsha Totakura
23  */
24 #include "gnunet-service-testbed.h"
25
26 /**
27  * Redefine LOG with a changed log component string
28  */
29 #ifdef LOG
30 #undef LOG
31 #endif
32 #define LOG(kind,...)                                   \
33   GNUNET_log_from (kind, "testbed-cache", __VA_ARGS__)
34
35
36 /**
37  * Cache entry
38  */
39 struct CacheEntry
40 {
41   /**
42    * DLL next ptr for least recently used cache entries
43    */
44   struct CacheEntry *next;
45
46   /**
47    * DLL prev ptr for least recently used cache entries
48    */
49   struct CacheEntry *prev;
50
51   /**
52    * The HELLO message
53    */
54   struct GNUNET_MessageHeader *hello;
55
56   /**
57    * The id of the peer this entry corresponds to
58    */
59   unsigned int peer_id;
60 };
61
62
63 /**
64  * Hashmap to maintain cache
65  */
66 static struct GNUNET_CONTAINER_MultiHashMap32 *cache;
67
68 /**
69  * DLL head for least recently used cache entries; least recently used
70  * cache items are at the head. The cache enties are added to this queue when
71  * their demand becomes zero. They are removed from the queue when they are
72  * needed by any operation.
73  */
74 static struct CacheEntry *cache_head;
75
76 /**
77  * DLL tail for least recently used cache entries; recently used cache
78  * items are at the tail.The cache enties are added to this queue when
79  * their demand becomes zero. They are removed from the queue when they are
80  * needed by any operation.
81  */
82 static struct CacheEntry *cache_tail;
83
84 /**
85  * Maximum number of elements to cache
86  */
87 static unsigned int cache_size;
88
89
90 /**
91  * Looks up in the cache and returns the entry
92  *
93  * @param peer_id the peer identity of the peer whose corresponding entry has to
94  *          be looked up
95  * @return the HELLO message; NULL if not found
96  */
97 static struct CacheEntry *
98 cache_lookup (unsigned int peer_id)
99 {
100   struct CacheEntry *entry;
101
102   GNUNET_assert (NULL != cache);
103   entry = GNUNET_CONTAINER_multihashmap32_get (cache, peer_id);
104   if (NULL == entry)
105     return NULL;
106   GNUNET_CONTAINER_DLL_remove (cache_head, cache_tail, entry);
107   GNUNET_CONTAINER_DLL_insert_tail (cache_head, cache_tail, entry);
108   return entry;
109 }
110
111
112 /**
113  * Free the resources occupied by a cache entry
114  *
115  * @param entry the cache entry to free
116  */
117 static void
118 free_entry (struct CacheEntry *entry)
119 {
120   GNUNET_CONTAINER_DLL_remove (cache_head, cache_tail, entry);
121   GNUNET_free_non_null (entry->hello);
122   GNUNET_free (entry);
123 }
124
125
126 /**
127  * Creates a new cache entry and then puts it into the cache's hashtable.
128  *
129  * @param peer_id the index of the peer to tag the newly created entry
130  * @return the newly created entry
131  */
132 static struct CacheEntry *
133 add_entry (unsigned int peer_id)
134 {
135   struct CacheEntry *entry;
136
137   GNUNET_assert (NULL != cache);
138   if (cache_size == GNUNET_CONTAINER_multihashmap32_size (cache))
139   {
140     /* remove the LRU head */
141     entry = cache_head;
142     GNUNET_assert (GNUNET_OK ==
143                    GNUNET_CONTAINER_multihashmap32_remove (cache, (uint32_t)
144                                                            entry->peer_id,
145                                                            entry));
146     free_entry (entry);
147   }
148   entry = GNUNET_new (struct CacheEntry);
149   entry->peer_id = peer_id;
150   GNUNET_assert (GNUNET_OK ==
151                  GNUNET_CONTAINER_multihashmap32_put (cache,
152                                                       (uint32_t) peer_id,
153                                                       entry,
154                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
155   GNUNET_CONTAINER_DLL_insert_tail (cache_head, cache_tail, entry);
156   return entry;
157 }
158
159
160 /**
161  * Iterator over hash map entries.
162  *
163  * @param cls closure
164  * @param key current key
165  * @param value value in the hash map
166  * @return GNUNET_YES if we should continue to
167  *         iterate,
168  *         GNUNET_NO if not.
169  */
170 static int
171 cache_clear_iterator (void *cls, uint32_t key, void *value)
172 {
173   struct CacheEntry *entry = value;
174
175   GNUNET_assert (NULL != entry);
176   GNUNET_assert (GNUNET_YES ==
177                  GNUNET_CONTAINER_multihashmap32_remove (cache, key, value));
178   free_entry (entry);
179   return GNUNET_YES;
180 }
181
182
183 /**
184  * Clear cache
185  */
186 void
187 GST_cache_clear ()
188 {
189   if (NULL != cache)
190   {
191     GNUNET_CONTAINER_multihashmap32_iterate (cache, &cache_clear_iterator, NULL);
192     GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (cache));
193     GNUNET_CONTAINER_multihashmap32_destroy (cache);
194     cache = NULL;
195   }
196   cache_size = 0;
197   cache_head = NULL;
198   cache_tail = NULL;
199 }
200
201
202 /**
203  * Initializes the cache
204  *
205  * @param size the size of the cache
206  */
207 void
208 GST_cache_init (unsigned int size)
209 {
210   if (0 == size)
211     return;
212   cache_size = size;
213   cache = GNUNET_CONTAINER_multihashmap32_create (cache_size);
214 }
215
216
217 /**
218  * Looks up in the hello cache and returns the HELLO of the given peer
219  *
220  * @param peer_id the index of the peer whose HELLO has to be looked up
221  * @return the HELLO message; NULL if not found
222  */
223 const struct GNUNET_MessageHeader *
224 GST_cache_lookup_hello (const unsigned int peer_id)
225 {
226   struct CacheEntry *entry;
227
228   LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id);
229   if (NULL == cache)
230   {
231     LOG_DEBUG ("Caching disabled\n");
232     return NULL;
233   }
234   entry = cache_lookup (peer_id);
235   if (NULL == entry)
236     return NULL;
237   if (NULL != entry->hello)
238     LOG_DEBUG ("HELLO found for peer %u\n", peer_id);
239   return entry->hello;
240 }
241
242
243 /**
244  * Caches the HELLO of the given peer. Updates the HELLO if it was already
245  * cached before
246  *
247  * @param peer_id the peer identity of the peer whose HELLO has to be cached
248  * @param hello the HELLO message
249  */
250 void
251 GST_cache_add_hello (const unsigned int peer_id,
252                      const struct GNUNET_MessageHeader *hello)
253 {
254   struct CacheEntry *entry;
255
256   if (NULL == cache)
257     return;
258   entry = cache_lookup (peer_id);
259   if (NULL == entry)
260     entry = add_entry (peer_id);
261   GNUNET_free_non_null (entry->hello);
262   entry->hello = GNUNET_copy_message (hello);
263 }
264
265 /* end of gnunet-service-testbed_hc.c */