iotivity 0.9.0
[platform/upstream/iotivity.git] / service / protocol-plugin / lib / cpluff / libcpluff / psymbol.c
1 /*-------------------------------------------------------------------------
2  * C-Pluff, a plug-in framework for C
3  * Copyright 2007 Johannes Lehtinen
4  * 
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:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
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  *-----------------------------------------------------------------------*/
23
24 /** @file
25  * Dynamic plug-in symbols
26  */
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 #include "../kazlib/hash.h"
32 #include "cpluff.h"
33 #include "defines.h"
34 #include "internal.h"
35 #include "util.h"
36
37
38 /* ------------------------------------------------------------------------
39  * Data structures
40  * ----------------------------------------------------------------------*/
41
42 /// Information about symbol providing plug-in
43 typedef struct symbol_provider_info_t {
44         
45         // The providing plug-in
46         cp_plugin_t *plugin;
47         
48         // Whether there is also an import dependency for the plug-in
49         int imported;
50         
51         // Total symbol usage count
52         int usage_count;
53         
54 } symbol_provider_info_t;
55
56 /// Information about used symbol
57 typedef struct symbol_info_t {
58
59         // Symbol usage count
60         int usage_count;
61         
62         // Information about providing plug-in
63         symbol_provider_info_t *provider_info;
64         
65 } symbol_info_t;
66
67
68 /* ------------------------------------------------------------------------
69  * Function definitions
70  * ----------------------------------------------------------------------*/
71
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;
74         
75         CHECK_NOT_NULL(context);
76         CHECK_NOT_NULL(name);
77 //      CHECK_NOT_NULL(ptr);
78         if (context->plugin == NULL) {
79                 cpi_fatalf(_("Only plug-ins can define context specific symbols."));
80         }
81         
82         cpi_lock_context(context);
83         cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
84         do {
85                 char *n;
86                 
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;
91                                 break;
92                         }
93                 }
94                 
95                 // Check for a previously defined symbol
96                 if (hash_lookup(context->plugin->defined_symbols, name) != NULL) {
97                         status = CP_ERR_CONFLICT;
98                         break;
99                 }
100
101                 // Insert the symbol into the symbol hash
102                 n = strdup(name);
103                 if (n == NULL || !hash_alloc_insert(context->plugin->defined_symbols, n, ptr)) {
104                         free(n);
105                         status = CP_ERR_RESOURCE;
106                         break;
107                 } 
108
109         } while (0);
110         
111         // Report error
112         if (status != CP_OK) {
113                 switch (status) {
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);
116                                 break;
117                         case CP_ERR_CONFLICT:
118                                 cpi_errorf(context, N_("Plug-in %s tried to redefine symbol %s."), context->plugin->plugin->identifier, name);
119                                 break;
120                         default:
121                                 break;
122                 }
123         }
124         cpi_unlock_context(context);
125         
126         return status;
127 }
128
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;
132         hnode_t *node;
133         void *symbol = NULL;
134         symbol_info_t *symbol_info = NULL;
135         symbol_provider_info_t *provider_info = NULL;
136         cp_plugin_t *pp = NULL;
137
138         CHECK_NOT_NULL(context);
139         CHECK_NOT_NULL(id);
140         CHECK_NOT_NULL(name);
141         
142         // Resolve the symbol
143         cpi_lock_context(context);
144         cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER | CPI_CF_STOP, __func__);
145         do {
146
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);
150                 }
151                 if (context->symbol_providers == NULL) {
152                         context->symbol_providers = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
153                 }
154                 if (context->resolved_symbols == NULL
155                         || context->symbol_providers == NULL) {
156                         status = CP_ERR_RESOURCE;
157                         break;
158                 }
159
160                 // Look up the symbol defining plug-in
161                 node = hash_lookup(context->env->plugins, id);
162                 if (node == NULL) {
163                         cpi_warnf(context, N_("Symbol %s in unknown plug-in %s could not be resolved."), name, id);
164                         status = CP_ERR_UNKNOWN;
165                         break;
166                 }
167                 pp = hnode_get(node);
168
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);
172                         error_reported = 1;
173                         break;
174                 }
175
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);
179                 }
180
181                 // Fall back to global symbols, if necessary
182                 if (symbol == NULL && pp->runtime_lib != NULL) {
183                         symbol = DLSYM(pp->runtime_lib, name);
184                 }
185                 if (symbol == NULL) {
186                         const char *error = DLERROR();
187                         if (error == NULL) {
188                                 error = _("Unspecified error.");
189                         }
190                         cpi_warnf(context, N_("Symbol %s in plug-in %s could not be resolved: %s"), name, id, error);
191                         status = CP_ERR_UNKNOWN;
192                         break;
193                 }
194
195                 // Lookup or initialize symbol provider information
196                 if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
197                         provider_info = hnode_get(node);
198                 } else {
199                         if ((provider_info = malloc(sizeof(symbol_provider_info_t))) == NULL) {
200                                 status = CP_ERR_RESOURCE;
201                                 break;
202                         }
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;
208                                 break;
209                         }
210                 }
211                 
212                 // Lookup or initialize symbol information
213                 if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
214                         symbol_info = hnode_get(node);
215                 } else {
216                         if ((symbol_info = malloc(sizeof(symbol_info_t))) == NULL) {
217                                 status = CP_ERR_RESOURCE;
218                                 break;
219                         }
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;
224                                 break;
225                         }
226                 }
227                 
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;
234                                 break;
235                         }
236                         if (!cpi_ptrset_add(pp->importing, context->plugin)) {
237                                 cpi_ptrset_remove(context->plugin->imported, pp);
238                                 status = CP_ERR_RESOURCE;
239                                 break;
240                         }
241                         cpi_debugf(context, "A dynamic dependency was created from plug-in %s to plug-in %s.", context->plugin->plugin->identifier, pp->plugin->identifier);
242                 }
243                 
244                 // Increase usage counts
245                 symbol_info->usage_count++;
246                 provider_info->usage_count++;
247
248                 if (cpi_is_logged(context, CP_LOG_DEBUG)) {
249                         char owner[64];
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);
252                 }
253         } while (0);
254
255         // Clean up
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);
259                 }
260                 free(symbol_info);
261         }
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);
265                 }
266                 free(provider_info);
267         }
268
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);
272         }
273         cpi_unlock_context(context);
274
275         // Return error code
276         if (error != NULL) {
277                 *error = status;
278         }
279         
280         // Return symbol
281         return symbol;
282 }
283
284 CP_C_API void cp_release_symbol(cp_context_t *context, const void *ptr) {
285         hnode_t *node;
286         symbol_info_t *symbol_info;
287         symbol_provider_info_t *provider_info;
288         
289         CHECK_NOT_NULL(context);
290         CHECK_NOT_NULL(ptr);
291
292         cpi_lock_context(context);
293         cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
294         do {
295
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);
299                         break;
300                 }
301                 symbol_info = hnode_get(node);
302                 provider_info = symbol_info->provider_info;
303         
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--;
309         
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);
313                         free(symbol_info);
314                         if (cpi_is_logged(context, CP_LOG_DEBUG)) {
315                                 char owner[64];
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);
318                         }
319                 }
320         
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);
330                         }
331                         free(provider_info);
332                 }
333                 
334         } while (0);
335         cpi_unlock_context(context);
336 }