cache.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. * =====================================================================================
  3. *
  4. * Filename: cache.c
  5. *
  6. * Description: A simple cache
  7. *
  8. * Version: 1.0
  9. * Created: 04/11/2013 02:31:02 PM
  10. * Revision: none
  11. * Compiler: gcc
  12. *
  13. * Author: Oliver Lorenz (ol), [email protected]
  14. * Company: https://olorenz.org
  15. * License: This is licensed under the same terms as uthash itself
  16. *
  17. * =====================================================================================
  18. */
  19. #include <errno.h>
  20. #include <pthread.h>
  21. #include <stdlib.h>
  22. #include "cache.h"
  23. #include "uthash.h"
  24. /**
  25. * A cache entry
  26. */
  27. struct foo_cache_entry {
  28. char *key; /**<The key */
  29. void *data; /**<Payload */
  30. UT_hash_handle hh; /**<Hash Handle for uthash */
  31. };
  32. #define KEY_MAX_LENGTH 32
  33. /**
  34. * A cache object
  35. */
  36. struct foo_cache {
  37. size_t max_entries; /**<Amount of entries this cache object can hold */
  38. pthread_rwlock_t cache_lock; /**<A lock for concurrent access */
  39. struct foo_cache_entry *entries; /**<Head pointer for uthash */
  40. void (*free_cb) (void *element);/**<Callback function to free cache entries */
  41. };
  42. /** Creates a new cache object
  43. @param dst
  44. Where the newly allocated cache object will be stored in
  45. @param capacity
  46. The maximum number of elements this cache object can hold
  47. @return EINVAL if dst is NULL, ENOMEM if malloc fails, 0 otherwise
  48. */
  49. int foo_cache_create(struct foo_cache **dst, const size_t capacity,
  50. void (*free_cb) (void *element))
  51. {
  52. struct foo_cache *new = NULL;
  53. int rv;
  54. if (!dst)
  55. return EINVAL;
  56. if ((new = malloc(sizeof(*new))) == NULL)
  57. return ENOMEM;
  58. if ((rv = pthread_rwlock_init(&(new->cache_lock), NULL)) != 0)
  59. goto err_out;
  60. new->max_entries = capacity;
  61. new->entries = NULL;
  62. new->free_cb = free_cb;
  63. *dst = new;
  64. return 0;
  65. err_out:
  66. if (new)
  67. free(new);
  68. return rv;
  69. }
  70. /** Frees an allocated cache object
  71. @param cache
  72. The cache object to free
  73. @param keep_data
  74. Whether to free contained data or just delete references to it
  75. @return EINVAL if cache is NULL, 0 otherwise
  76. */
  77. int foo_cache_delete(struct foo_cache *cache, int keep_data)
  78. {
  79. struct foo_cache_entry *entry, *tmp;
  80. int rv;
  81. if (!cache)
  82. return EINVAL;
  83. rv = pthread_rwlock_wrlock(&(cache->cache_lock));
  84. if (rv)
  85. return rv;
  86. if (keep_data) {
  87. HASH_CLEAR(hh, cache->entries);
  88. } else {
  89. HASH_ITER(hh, cache->entries, entry, tmp) {
  90. HASH_DEL(cache->entries, entry);
  91. if (cache->free_cb)
  92. cache->free_cb(entry->data);
  93. free(entry);
  94. }
  95. }
  96. (void)pthread_rwlock_unlock(&(cache->cache_lock));
  97. (void)pthread_rwlock_destroy(&(cache->cache_lock));
  98. free(cache);
  99. cache = NULL;
  100. return 0;
  101. }
  102. /** Checks if a given key is in the cache
  103. @param cache
  104. The cache object
  105. @param key
  106. The key to look-up
  107. @param result
  108. Where to store the result if key is found.
  109. A warning: Even though result is just a pointer,
  110. you have to call this function with a **ptr,
  111. otherwise this will blow up in your face.
  112. @return EINVAL if cache is NULL, 0 otherwise
  113. */
  114. int foo_cache_lookup(struct foo_cache *cache, char *key, void *result)
  115. {
  116. int rv;
  117. struct foo_cache_entry *tmp = NULL;
  118. char **dirty_hack = result;
  119. if (!cache || !key || !result)
  120. return EINVAL;
  121. rv = pthread_rwlock_wrlock(&(cache->cache_lock));
  122. if (rv)
  123. return rv;
  124. HASH_FIND_STR(cache->entries, key, tmp);
  125. if (tmp) {
  126. size_t key_len = strnlen(tmp->key, KEY_MAX_LENGTH);
  127. HASH_DELETE(hh, cache->entries, tmp);
  128. HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp);
  129. *dirty_hack = tmp->data;
  130. } else {
  131. *dirty_hack = result = NULL;
  132. }
  133. rv = pthread_rwlock_unlock(&(cache->cache_lock));
  134. return rv;
  135. }
  136. /** Inserts a given <key, value> pair into the cache
  137. @param cache
  138. The cache object
  139. @param key
  140. The key that identifies <value>
  141. @param data
  142. Data associated with <key>
  143. @return EINVAL if cache is NULL, ENOMEM if malloc fails, 0 otherwise
  144. */
  145. int foo_cache_insert(struct foo_cache *cache, char *key, void *data)
  146. {
  147. struct foo_cache_entry *entry = NULL;
  148. struct foo_cache_entry *tmp_entry = NULL;
  149. size_t key_len = 0;
  150. int rv;
  151. if (!cache || !data)
  152. return EINVAL;
  153. if ((entry = malloc(sizeof(*entry))) == NULL)
  154. return ENOMEM;
  155. if ((rv = pthread_rwlock_wrlock(&(cache->cache_lock))) != 0)
  156. goto err_out;
  157. entry->key = key;
  158. entry->data = data;
  159. key_len = strnlen(entry->key, KEY_MAX_LENGTH);
  160. HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry);
  161. if (HASH_COUNT(cache->entries) >= cache->max_entries) {
  162. HASH_ITER(hh, cache->entries, entry, tmp_entry) {
  163. HASH_DELETE(hh, cache->entries, entry);
  164. if (cache->free_cb)
  165. cache->free_cb(entry->data);
  166. else
  167. free(entry->data);
  168. /* free(key->key) if data has been copied */
  169. free(entry);
  170. break;
  171. }
  172. }
  173. rv = pthread_rwlock_unlock(&(cache->cache_lock));
  174. return rv;
  175. err_out:
  176. if (entry)
  177. free(entry);
  178. (void)pthread_rwlock_unlock(&(cache->cache_lock));
  179. return rv;
  180. }