1 /*-------------------------------------------------------------------------
2 * C-Pluff, a plug-in framework for C
3 * Copyright 2007 Johannes Lehtinen
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *-----------------------------------------------------------------------*/
25 * Dynamic plug-in symbols
31 #include "../kazlib/hash.h"
38 /* ------------------------------------------------------------------------
40 * ----------------------------------------------------------------------*/
42 /// Information about symbol providing plug-in
43 typedef struct symbol_provider_info_t {
45 // The providing plug-in
48 // Whether there is also an import dependency for the plug-in
51 // Total symbol usage count
54 } symbol_provider_info_t;
56 /// Information about used symbol
57 typedef struct symbol_info_t {
62 // Information about providing plug-in
63 symbol_provider_info_t *provider_info;
68 /* ------------------------------------------------------------------------
69 * Function definitions
70 * ----------------------------------------------------------------------*/
72 CP_C_API cp_status_t cp_define_symbol(cp_context_t *context, const char *name, void *ptr) {
73 cp_status_t status = CP_OK;
75 CHECK_NOT_NULL(context);
77 // CHECK_NOT_NULL(ptr);
78 if (context->plugin == NULL) {
79 cpi_fatalf(_("Only plug-ins can define context specific symbols."));
82 cpi_lock_context(context);
83 cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
87 // Create a symbol hash if necessary
88 if (context->plugin->defined_symbols == NULL) {
89 if ((context->plugin->defined_symbols = hash_create(HASHCOUNT_T_MAX, (int (*)(const void *, const void *)) strcmp, NULL)) == NULL) {
90 status = CP_ERR_RESOURCE;
95 // Check for a previously defined symbol
96 if (hash_lookup(context->plugin->defined_symbols, name) != NULL) {
97 status = CP_ERR_CONFLICT;
101 // Insert the symbol into the symbol hash
103 if (n == NULL || !hash_alloc_insert(context->plugin->defined_symbols, n, ptr)) {
105 status = CP_ERR_RESOURCE;
112 if (status != CP_OK) {
114 case CP_ERR_RESOURCE:
115 cpi_errorf(context, N_("Plug-in %s could not define symbol %s due to insufficient memory."), context->plugin->plugin->identifier, name);
117 case CP_ERR_CONFLICT:
118 cpi_errorf(context, N_("Plug-in %s tried to redefine symbol %s."), context->plugin->plugin->identifier, name);
124 cpi_unlock_context(context);
129 CP_C_API void * cp_resolve_symbol(cp_context_t *context, const char *id, const char *name, cp_status_t *error) {
130 cp_status_t status = CP_OK;
131 int error_reported = 1;
134 symbol_info_t *symbol_info = NULL;
135 symbol_provider_info_t *provider_info = NULL;
136 cp_plugin_t *pp = NULL;
138 CHECK_NOT_NULL(context);
140 CHECK_NOT_NULL(name);
142 // Resolve the symbol
143 cpi_lock_context(context);
144 cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER | CPI_CF_STOP, __func__);
147 // Allocate space for symbol hashes, if necessary
148 if (context->resolved_symbols == NULL) {
149 context->resolved_symbols = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
151 if (context->symbol_providers == NULL) {
152 context->symbol_providers = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
154 if (context->resolved_symbols == NULL
155 || context->symbol_providers == NULL) {
156 status = CP_ERR_RESOURCE;
160 // Look up the symbol defining plug-in
161 node = hash_lookup(context->env->plugins, id);
163 cpi_warnf(context, N_("Symbol %s in unknown plug-in %s could not be resolved."), name, id);
164 status = CP_ERR_UNKNOWN;
167 pp = hnode_get(node);
169 // Make sure the plug-in has been started
170 if ((status = cpi_start_plugin(context, pp)) != CP_OK) {
171 cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved because the plug-in could not be started."), name, id);
176 // Check for a context specific symbol
177 if (pp->defined_symbols != NULL && (node = hash_lookup(pp->defined_symbols, name)) != NULL) {
178 symbol = hnode_get(node);
181 // Fall back to global symbols, if necessary
182 if (symbol == NULL && pp->runtime_lib != NULL) {
183 symbol = DLSYM(pp->runtime_lib, name);
185 if (symbol == NULL) {
186 const char *error = DLERROR();
188 error = _("Unspecified error.");
190 cpi_warnf(context, N_("Symbol %s in plug-in %s could not be resolved: %s"), name, id, error);
191 status = CP_ERR_UNKNOWN;
195 // Lookup or initialize symbol provider information
196 if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
197 provider_info = hnode_get(node);
199 if ((provider_info = malloc(sizeof(symbol_provider_info_t))) == NULL) {
200 status = CP_ERR_RESOURCE;
203 memset(provider_info, 0, sizeof(symbol_provider_info_t));
204 provider_info->plugin = pp;
205 provider_info->imported = (context->plugin == NULL || cpi_ptrset_contains(context->plugin->imported, pp));
206 if (!hash_alloc_insert(context->symbol_providers, pp, provider_info)) {
207 status = CP_ERR_RESOURCE;
212 // Lookup or initialize symbol information
213 if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
214 symbol_info = hnode_get(node);
216 if ((symbol_info = malloc(sizeof(symbol_info_t))) == NULL) {
217 status = CP_ERR_RESOURCE;
220 memset(symbol_info, 0, sizeof(symbol_info_t));
221 symbol_info->provider_info = provider_info;
222 if (!hash_alloc_insert(context->resolved_symbols, symbol, symbol_info)) {
223 status = CP_ERR_RESOURCE;
228 // Add dependencies (for plug-in)
229 if (provider_info != NULL
230 && !provider_info->imported
231 && provider_info->usage_count == 0) {
232 if (!cpi_ptrset_add(context->plugin->imported, pp)) {
233 status = CP_ERR_RESOURCE;
236 if (!cpi_ptrset_add(pp->importing, context->plugin)) {
237 cpi_ptrset_remove(context->plugin->imported, pp);
238 status = CP_ERR_RESOURCE;
241 cpi_debugf(context, "A dynamic dependency was created from plug-in %s to plug-in %s.", context->plugin->plugin->identifier, pp->plugin->identifier);
244 // Increase usage counts
245 symbol_info->usage_count++;
246 provider_info->usage_count++;
248 if (cpi_is_logged(context, CP_LOG_DEBUG)) {
250 /* TRANSLATORS: First %s is the context owner */
251 cpi_debugf(context, "%s resolved symbol %s defined by plug-in %s.", cpi_context_owner(context, owner, sizeof(owner)), name, id);
256 if (symbol_info != NULL && symbol_info->usage_count == 0) {
257 if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
258 hash_delete_free(context->resolved_symbols, node);
262 if (provider_info != NULL && provider_info->usage_count == 0) {
263 if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
264 hash_delete_free(context->symbol_providers, node);
269 // Report insufficient memory error
270 if (status == CP_ERR_RESOURCE && !error_reported) {
271 cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved due to insufficient memory."), name, id);
273 cpi_unlock_context(context);
284 CP_C_API void cp_release_symbol(cp_context_t *context, const void *ptr) {
286 symbol_info_t *symbol_info;
287 symbol_provider_info_t *provider_info;
289 CHECK_NOT_NULL(context);
292 cpi_lock_context(context);
293 cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
296 // Look up the symbol
297 if ((node = hash_lookup(context->resolved_symbols, ptr)) == NULL) {
298 cpi_errorf(context, N_("Could not release unknown symbol at address %p."), ptr);
301 symbol_info = hnode_get(node);
302 provider_info = symbol_info->provider_info;
304 // Decrease usage count
305 assert(symbol_info->usage_count > 0);
306 symbol_info->usage_count--;
307 assert(provider_info->usage_count > 0);
308 provider_info->usage_count--;
310 // Check if the symbol is not being used anymore
311 if (symbol_info->usage_count == 0) {
312 hash_delete_free(context->resolved_symbols, node);
314 if (cpi_is_logged(context, CP_LOG_DEBUG)) {
316 /* TRANSLATORS: First %s is the context owner */
317 cpi_debugf(context, _("%s released the symbol at address %p defined by plug-in %s."), cpi_context_owner(context, owner, sizeof(owner)), ptr, provider_info->plugin->plugin->identifier);
321 // Check if the symbol providing plug-in is not being used anymore
322 if (provider_info->usage_count == 0) {
323 node = hash_lookup(context->symbol_providers, provider_info->plugin);
324 assert(node != NULL);
325 hash_delete_free(context->symbol_providers, node);
326 if (!provider_info->imported) {
327 cpi_ptrset_remove(context->plugin->imported, provider_info->plugin);
328 cpi_ptrset_remove(provider_info->plugin->importing, context->plugin);
329 cpi_debugf(context, _("A dynamic dependency from plug-in %s to plug-in %s was removed."), context->plugin->plugin->identifier, provider_info->plugin->plugin->identifier);
335 cpi_unlock_context(context);