9f2b15579ed692f7b82b6c5407349f761917632d
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_cache.c
1 /*
2   This file is part of GNUnet.
3   (C) 2008--2013 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 testbed/gnunet-service-testbed_cache.c
23  * @brief testbed cache implementation
24  * @author Sree Harsha Totakura
25  */
26 #include "gnunet-service-testbed.h"
27
28 /**
29  * Redefine LOG with a changed log component string
30  */
31 #ifdef LOG
32 #undef LOG
33 #endif
34 #define LOG(kind,...)                                   \
35   GNUNET_log_from (kind, "testbed-cache", __VA_ARGS__)
36
37
38 /**
39  * Time to expire a cache entry
40  */
41 #define CACHE_EXPIRY                            \
42   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
43
44
45 /**
46  * Cache entry
47  */
48 struct CacheEntry
49 {
50   /**
51    * DLL next ptr for least recently used cache entries
52    */
53   struct CacheEntry *next;
54
55   /**
56    * DLL prev ptr for least recently used cache entries
57    */
58   struct CacheEntry *prev;
59
60   /**
61    * The peer identity of this peer. Will be set upon opening a connection to
62    * the peers CORE service. Will be NULL until then and after the CORE
63    * connection is closed
64    */
65   struct GNUNET_PeerIdentity *peer_identity;
66
67   /**
68    * The key for this entry
69    */
70   struct GNUNET_HashCode key;
71
72   /**
73    * The HELLO message
74    */
75   struct GNUNET_MessageHeader *hello;
76
77   /**
78    * The id of the peer this entry corresponds to
79    */
80   unsigned int peer_id;
81
82   /**
83    * Is this entry in LRU cache queue?
84    */
85   unsigned int in_lru;
86 };
87
88
89 /**
90  * Hashmap to maintain cache
91  */
92 static struct GNUNET_CONTAINER_MultiHashMap *cache;
93
94 /**
95  * DLL head for least recently used cache entries; least recently used
96  * cache items are at the head. The cache enties are added to this queue when
97  * their demand becomes zero. They are removed from the queue when they are
98  * needed by any operation.
99  */
100 static struct CacheEntry *lru_cache_head;
101
102 /**
103  * DLL tail for least recently used cache entries; recently used cache
104  * items are at the tail.The cache enties are added to this queue when
105  * their demand becomes zero. They are removed from the queue when they are
106  * needed by any operation.
107  */
108 static struct CacheEntry *lru_cache_tail;
109
110 /**
111  * the size of the LRU queue
112  */
113 static unsigned int lru_cache_size;
114
115 /**
116  * the threshold size for the LRU queue
117  */
118 static unsigned int lru_cache_threshold_size;
119
120 /**
121  * The total number of elements in cache
122  */
123 static unsigned int cache_size;
124
125
126 /**
127  * Looks up in the cache and returns the entry
128  *
129  * @param key the peer identity of the peer whose corresponding entry has to be
130  *          looked up
131  * @return the HELLO message; NULL if not found
132  */
133 static struct CacheEntry *
134 cache_lookup (const struct GNUNET_HashCode *key)
135 {
136   struct CacheEntry *entry;
137
138   if (NULL == cache)
139     return NULL;
140   entry = GNUNET_CONTAINER_multihashmap_get (cache, key);
141   return entry;
142 }
143
144
145 /**
146  * Creates a new cache entry and then puts it into the cache's hashtable.
147  *
148  * @param key the hash code to use for inserting the newly created entry
149  * @param peer_id the index of the peer to tag the newly created entry
150  * @return the newly created entry
151  */
152 static struct CacheEntry *
153 add_entry (const struct GNUNET_HashCode *key, unsigned int peer_id)
154 {
155   struct CacheEntry *entry;
156
157   entry = GNUNET_malloc (sizeof (struct CacheEntry));
158   entry->peer_id = peer_id;
159   memcpy (&entry->key, key, sizeof (struct GNUNET_HashCode));
160   GNUNET_assert (GNUNET_OK ==
161                  GNUNET_CONTAINER_multihashmap_put (cache, &entry->key, entry,
162                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
163   cache_size++;
164   return entry;
165 }
166
167
168 /**
169  * Iterator over hash map entries.
170  *
171  * @param cls closure
172  * @param key current key code
173  * @param value value in the hash map
174  * @return GNUNET_YES if we should continue to
175  *         iterate,
176  *         GNUNET_NO if not.
177  */
178 static int
179 cache_clear_iterator (void *cls, const struct GNUNET_HashCode *key, void *value)
180 {
181   struct CacheEntry *entry = value;
182   static unsigned int ncleared;
183
184   GNUNET_assert (NULL != entry);
185   LOG_DEBUG ("Clearing entry %u of %u\n", ++ncleared, cache_size);
186   GNUNET_assert (GNUNET_YES ==
187                  GNUNET_CONTAINER_multihashmap_remove (cache, key, value));
188   GNUNET_free_non_null (entry->hello);
189   GNUNET_free (entry);
190   return GNUNET_YES;
191 }
192
193
194 /**
195  * Clear cache
196  */
197 void
198 GST_cache_clear ()
199 {
200   GNUNET_CONTAINER_multihashmap_iterate (cache, &cache_clear_iterator, NULL);
201   GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (cache));
202   GNUNET_CONTAINER_multihashmap_destroy (cache);
203   cache = NULL;
204   lru_cache_size = 0;
205   lru_cache_threshold_size = 0;
206   cache_size = 0;
207   lru_cache_head = NULL;
208   lru_cache_tail = NULL;
209 }
210
211
212 /**
213  * Initializes the cache
214  *
215  * @param size the size of the cache
216  */
217 void
218 GST_cache_init (unsigned int size)
219 {
220   if (0 == size)
221     return;
222   lru_cache_threshold_size = size;
223   if (size > 1)
224     size = size / 2;
225   cache = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_YES);
226 }
227
228
229 /**
230  * Looks up in the hello cache and returns the HELLO of the given peer
231  *
232  * @param peer_id the index of the peer whose HELLO has to be looked up
233  * @return the HELLO message; NULL if not found
234  */
235 const struct GNUNET_MessageHeader *
236 GST_cache_lookup_hello (const unsigned int peer_id)
237 {
238   struct CacheEntry *entry;
239   struct GNUNET_HashCode key;
240
241   LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id);
242   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
243   entry = cache_lookup (&key);
244   if (NULL == entry)
245     return NULL;
246   if (NULL != entry->hello)
247     LOG_DEBUG ("HELLO found for peer %u\n", peer_id);
248   return entry->hello;
249 }
250
251
252 /**
253  * Caches the HELLO of the given peer. Updates the HELLO if it was already
254  * cached before
255  *
256  * @param peer_id the peer identity of the peer whose HELLO has to be cached
257  * @param hello the HELLO message
258  */
259 void
260 GST_cache_add_hello (const unsigned int peer_id,
261                      const struct GNUNET_MessageHeader *hello)
262 {
263   struct CacheEntry *entry;
264   struct GNUNET_HashCode key;
265
266   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
267   entry = GNUNET_CONTAINER_multihashmap_get (cache, &key);
268   if (NULL == entry)
269     entry = add_entry (&key, peer_id);
270   GNUNET_free_non_null (entry->hello);
271   entry->hello = GNUNET_copy_message (hello);
272 }
273
274 /* end of gnunet-service-testbed_hc.c */