xref: /freebsd/contrib/pkgconf/libpkgconf/cache.c (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
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