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