jcs's openbsd hax
openbsd
at jcs 231 lines 5.7 kB view raw
1/* 2 * cache.c 3 * package object cache 4 * 5 * Copyright (c) 2013 pkgconf authors (see AUTHORS). 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * This software is provided 'as is' and without any warranty, express or 12 * implied. In no event shall the authors be liable for any damages arising 13 * from the use of this software. 14 */ 15 16#include <libpkgconf/stdinc.h> 17#include <libpkgconf/libpkgconf.h> 18 19#include <assert.h> 20 21/* 22 * !doc 23 * 24 * libpkgconf `cache` module 25 * ========================= 26 * 27 * The libpkgconf `cache` module manages a package/module object cache, allowing it to 28 * avoid loading duplicate copies of a package/module. 29 * 30 * A cache is tied to a specific pkgconf client object, so package objects should not 31 * be shared across threads. 32 */ 33 34static int 35cache_member_cmp(const void *a, const void *b) 36{ 37 const char *key = a; 38 const pkgconf_pkg_t *pkg = *(void **) b; 39 40 return strcmp(key, pkg->id); 41} 42 43static int 44cache_member_sort_cmp(const void *a, const void *b) 45{ 46 const pkgconf_pkg_t *pkgA = *(void **) a; 47 const pkgconf_pkg_t *pkgB = *(void **) b; 48 49 if (pkgA == NULL) 50 return 1; 51 52 if (pkgB == NULL) 53 return -1; 54 55 return strcmp(pkgA->id, pkgB->id); 56} 57 58static void 59cache_dump(const pkgconf_client_t *client) 60{ 61 size_t i; 62 63 PKGCONF_TRACE(client, "dumping package cache contents"); 64 65 for (i = 0; i < client->cache_count; i++) 66 { 67 const pkgconf_pkg_t *pkg = client->cache_table[i]; 68 69 PKGCONF_TRACE(client, SIZE_FMT_SPECIFIER": %p(%s)", 70 i, pkg, pkg == NULL ? "NULL" : pkg->id); 71 } 72} 73 74/* 75 * !doc 76 * 77 * .. c:function:: pkgconf_pkg_t *pkgconf_cache_lookup(const pkgconf_client_t *client, const char *id) 78 * 79 * Looks up a package in the cache given an `id` atom, 80 * such as ``gtk+-3.0`` and returns the already loaded version 81 * if present. 82 * 83 * :param pkgconf_client_t* client: The client object to access. 84 * :param char* id: The package atom to look up in the client object's cache. 85 * :return: A package object if present, else ``NULL``. 86 * :rtype: pkgconf_pkg_t * 87 */ 88pkgconf_pkg_t * 89pkgconf_cache_lookup(pkgconf_client_t *client, const char *id) 90{ 91 if (client->cache_table == NULL) 92 return NULL; 93 94 pkgconf_pkg_t **pkg; 95 96 pkg = bsearch(id, client->cache_table, 97 client->cache_count, sizeof (void *), 98 cache_member_cmp); 99 100 if (pkg != NULL) 101 { 102 PKGCONF_TRACE(client, "found: %s @%p", id, *pkg); 103 return pkgconf_pkg_ref(client, *pkg); 104 } 105 106 PKGCONF_TRACE(client, "miss: %s", id); 107 return NULL; 108} 109 110/* 111 * !doc 112 * 113 * .. c:function:: void pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg) 114 * 115 * Adds an entry for the package to the package cache. 116 * The cache entry must be removed if the package is freed. 117 * 118 * :param pkgconf_client_t* client: The client object to modify. 119 * :param pkgconf_pkg_t* pkg: The package object to add to the client object's cache. 120 * :return: nothing 121 */ 122void 123pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg) 124{ 125 if (pkg == NULL) 126 return; 127 128 pkgconf_pkg_ref(client, pkg); 129 130 PKGCONF_TRACE(client, "added @%p to cache", pkg); 131 132 /* mark package as cached */ 133 pkg->flags |= PKGCONF_PKG_PROPF_CACHED; 134 135 ++client->cache_count; 136 client->cache_table = pkgconf_reallocarray(client->cache_table, 137 client->cache_count, sizeof (void *)); 138 client->cache_table[client->cache_count - 1] = pkg; 139 140 qsort(client->cache_table, client->cache_count, 141 sizeof(void *), cache_member_sort_cmp); 142} 143 144/* 145 * !doc 146 * 147 * .. c:function:: void pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg) 148 * 149 * Deletes a package from the client object's package cache. 150 * 151 * :param pkgconf_client_t* client: The client object to modify. 152 * :param pkgconf_pkg_t* pkg: The package object to remove from the client object's cache. 153 * :return: nothing 154 */ 155void 156pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg) 157{ 158 if (client->cache_table == NULL) 159 return; 160 161 if (pkg == NULL) 162 return; 163 164 if (!(pkg->flags & PKGCONF_PKG_PROPF_CACHED)) 165 return; 166 167 PKGCONF_TRACE(client, "removed @%p from cache", pkg); 168 169 pkgconf_pkg_t **slot; 170 171 slot = bsearch(pkg->id, client->cache_table, 172 client->cache_count, sizeof (void *), 173 cache_member_cmp); 174 175 if (slot == NULL) 176 return; 177 178 (*slot)->flags &= ~PKGCONF_PKG_PROPF_CACHED; 179 pkgconf_pkg_unref(client, *slot); 180 *slot = NULL; 181 182 qsort(client->cache_table, client->cache_count, 183 sizeof(void *), cache_member_sort_cmp); 184 185 if (client->cache_table[client->cache_count - 1] != NULL) 186 { 187 PKGCONF_TRACE(client, "end of cache table refers to %p, not NULL", 188 client->cache_table[client->cache_count - 1]); 189 cache_dump(client); 190 abort(); 191 } 192 193 client->cache_count--; 194 if (client->cache_count > 0) 195 { 196 client->cache_table = pkgconf_reallocarray(client->cache_table, 197 client->cache_count, sizeof(void *)); 198 } 199 else 200 { 201 free(client->cache_table); 202 client->cache_table = NULL; 203 } 204} 205 206/* 207 * !doc 208 * 209 * .. c:function:: void pkgconf_cache_free(pkgconf_client_t *client) 210 * 211 * Releases all resources related to a client object's package cache. 212 * This function should only be called to clear a client object's package cache, 213 * as it may release any package in the cache. 214 * 215 * :param pkgconf_client_t* client: The client object to modify. 216 */ 217void 218pkgconf_cache_free(pkgconf_client_t *client) 219{ 220 if (client->cache_table == NULL) 221 return; 222 223 while (client->cache_count > 0) 224 pkgconf_cache_remove(client, client->cache_table[0]); 225 226 free(client->cache_table); 227 client->cache_table = NULL; 228 client->cache_count = 0; 229 230 PKGCONF_TRACE(client, "cleared package cache"); 231}