1 /*
2  * Copyright 1999-2006 University of Chicago
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  * http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "globus_soap_message.h"
18 #include "globus_i_wsrf_resource.h"
19
20 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
21 /**
22  * @file globus_wsrf_resource.c
23  *
24  * Module implementation and resource creation/destruction functions.
25  */
26 static
27 int
28 globus_l_wsrf_resource_activate(void);
29
30 static
31 int
32 globus_l_wsrf_resource_deactivate(void);
33
34 static
35 void
36 globus_l_wsrf_resource_hashtable_destroy(
37     void *                              datum);
38
39 static
40 void
41 globus_l_resource_destroy(
42     globus_resource_t                   resource);
43
44 static
45 void
46 globus_l_resource_property_destroy(
47     void *                              datum);
48
49 static
50 void
51 globus_l_resource_lifetime_expired(
52     void *                              arg);
53
54 static
55 int
56 globus_l_resource_try_lock(
57     globus_resource_t                   resource);
58
59 static
60 void
61 globus_l_resource_lock(
62     globus_resource_t                   resource);
63 static
64 void
65 globus_l_resource_unlock(
66     globus_resource_t                   resource);
67
68 static
69 void
70 globus_l_resource_destroy_keyed_data(
71     void *                              p);
72
73 0 GlobusDebugDefine(GLOBUS_RESOURCE);
74
75 /**
76  * Table mapping resource id strings to resource instances.
77  */
78 static globus_hashtable_t globus_l_wsrf_resources;
79 /**
80  * Lock for resource table.
81  */
82 static globus_mutex_t globus_l_wsrf_resource_lock;
83
84 globus_module_descriptor_t
85 globus_i_wsrf_resource_module = 
86 {
87     "globus_wsrf_resource",
88     globus_l_wsrf_resource_activate,
89     globus_l_wsrf_resource_deactivate
90 };
91
92 /**
93  * Activate the WSRF Resource module.
94  *
95  * Activate modules this one uses, create the resource ID : resource hashtable
96  * and lock.
97  *
98  * @retval GLOBUS_SUCCESS
99  *     Module successfully activated.
100  * @retval 1
101  *     Unable to activate module.
102  */
103 static
104 int
105 globus_l_wsrf_resource_activate(void)
106 {
107 173     int rc;
108
109 173     rc = globus_module_activate(GLOBUS_COMMON_MODULE);
110 173     if (rc != GLOBUS_SUCCESS)
111     {
112 0         goto error;
113     }
114 173     GlobusDebugInit(GLOBUS_RESOURCE, DEBUG INFO TRACE WARN ERROR);
115
116 173     rc = globus_module_activate(GLOBUS_SOAP_MESSAGE_MODULE);
117
118 173     if (rc != GLOBUS_SUCCESS)
119     {
120 0         goto deactivate_common_error;
121     }
122
123 173     rc = globus_hashtable_init(&globus_l_wsrf_resources,
124             16,
125             globus_hashtable_string_hash,
126             globus_hashtable_string_keyeq);
127
128 173     if (rc != GLOBUS_SUCCESS)
129     {
130 0         goto deactivate_message_error;
131     }
132
133 173     rc = globus_mutex_init(&globus_l_wsrf_resource_lock, NULL);
134 173     if (rc != GLOBUS_SUCCESS)
135     {
136 0         goto destroy_hashtable_error;
137     }
138     
139 173     return GLOBUS_SUCCESS;
140
141 destroy_hashtable_error:
142 0     globus_hashtable_destroy(&globus_l_wsrf_resources);
143 deactivate_message_error:
144 0     globus_module_deactivate(GLOBUS_SOAP_MESSAGE_MODULE);
145 deactivate_common_error:
146 0     GlobusDebugDestroy(GLOBUS_RESOURCE);
147 0     globus_module_deactivate(GLOBUS_COMMON_MODULE);
148 error:
149 0     return 1;
150 }
151 /* globus_l_wsrf_resource_activate() */
152
153 static
154 int
155 globus_l_wsrf_resource_deactivate(void)
156 100 {
157 100     globus_mutex_lock(&globus_l_wsrf_resource_lock);
158
159 100     globus_hashtable_destroy_all(
160         &globus_l_wsrf_resources,
161         globus_l_wsrf_resource_hashtable_destroy);
162
163 100     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
164 100     globus_mutex_destroy(&globus_l_wsrf_resource_lock);
165 100     globus_module_deactivate(GLOBUS_SOAP_MESSAGE_MODULE);
166 100     GlobusDebugDestroy(GLOBUS_RESOURCE);
167 100     globus_module_deactivate(GLOBUS_COMMON_MODULE);
168
169 100     return 0;
170 }
171 /* globus_l_wsrf_resource_deactivate() */
172 #endif /* GLOBUS_DONT_DOCUMENT_INTERNAL */
173
174 /**
175  * Create a new WSRF Resource.
176  * @ingroup resource
177  *
178  * Allocate a new WSRF resource and lock the reference to
179  * it.  Every call to globus_resource_find() must be paired with
180  * a call to globus_resource_finish(). The service which
181  * calls globus_resource_create() must call globus_resource_destroy() or
182  * globus_resource_set_destroy_time() to cause the resource to be destroyed.
183  *
184  * @param id
185  *     Identifier to use for the new resource.
186  * @param resource
187  *     Pointer to be set to the newly created resource handle.
188  *
189  * @retval GLOBUS_SUCCESS
190  *     New resource created successfully.
191  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
192  *     One of the parameters to this function was NULL.
193  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_DUPLICATE_RESOURCE
194  *     Another resource with the specified id already exists, so a new one
195  *     could not be created.
196  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_OUT_OF_MEMORY
197  *     Insufficient memory to create the new resource.
198  *
199  * @see globus_resource_find(), globus_resource_finish()
200  */
201 globus_result_t
202 globus_resource_create(
203     const char *                        id,
204     globus_resource_t *                 resource)
205 444 {
206 444     globus_resource_t                   iresource;
207 444     globus_result_t                     result = GLOBUS_SUCCESS;
208 444     int                                 rc;
209 444     GlobusFuncName(globus_resource_create);
210
211 444     GlobusResourceDebugEnter();
212
213 444     if (id == NULL || resource == NULL)
214     {
215 3         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
216 3         goto error;
217     }
218
219 441     globus_mutex_lock(&globus_l_wsrf_resource_lock);
220
221 441     iresource = globus_hashtable_lookup(&globus_l_wsrf_resources, (void *) id);
222
223 441     if (iresource != NULL)
224     {
225 1         result = GLOBUS_RESOURCE_ERROR_DUPLICATE_RESOURCE(id);
226
227 1         goto unlock_error;
228     }
229
230 440     iresource = globus_calloc(1, sizeof(struct globus_resource_s));
231 440     if (iresource == NULL)
232     {
233 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
234
235 0         goto unlock_error;
236     }
237
238 440     iresource->id = globus_libc_strdup(id);
239 440     if (iresource->id == NULL)
240     {
241 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
242
243 0         goto free_resource_error;
244     }
245
246 440     rc = globus_hashtable_init(&iresource->resource_properties,
247             31,
248             xsd_QName_hash,
249             xsd_QName_keyeq);
250
251 440     if (rc != GLOBUS_SUCCESS)
252     {
253 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
254
255 0         goto free_id_out;
256     }
257
258 440     iresource->thread_id = globus_thread_self(); 
259 440     iresource->lock_count = 1;
260
261 440     iresource->lifetime_callback = GLOBUS_NULL_HANDLE;
262
263 440     rc = globus_hashtable_insert(
264             &globus_l_wsrf_resources,
265             iresource->id,
266             iresource);
267
268 440     if (rc != GLOBUS_SUCCESS)
269     {
270 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
271
272 0         goto destroy_hashtable_out;
273     }
274
275 440     iresource->property_changed_callbacks = NULL;
276
277 440     *resource = iresource;
278     
279 440     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
280 440     GlobusResourceDebugExit();
281 440     return result;
282
283 destroy_hashtable_out:
284 0     globus_hashtable_destroy(&iresource->resource_properties);
285 free_id_out:
286 0     globus_libc_free(iresource->id);
287 free_resource_error:
288 0     globus_libc_free(iresource);
289 unlock_error:
290 1     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
291 error:
292 4     GlobusResourceDebugExit();
293 4     return result;
294 }
295 /* globus_resource_create() */
296
297 /**
298  * Find and lock the resource associated with a resource identifier.
299  * @ingroup resource
300  *
301  * Returns a reference to the resource associated with the id.
302  * Release this reference by calling #globus_resource_finish(), or destroy
303  * the resource by calling #globus_resource_destroy().
304  *
305  * @param id
306  *     Resource identifier for the desired resource.
307  * @param resource
308  *     Pointer to a resource which will modified to point to the
309  *     named resource if it is found.
310  *
311  * @retval GLOBUS_SUCCESS
312  *     The resource associated with the specified id was found and a reference
313  *     to it was returned in the @a resource parameter.
314  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
315  *     One of the parameters to this function was NULL.
316  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_UNKNOWN_RESOURCE
317  *     No resource with the specified id exists.
318  * @see #globus_resource_create()
319  */
320 globus_result_t
321 globus_resource_find(
322     const char *                        id,
323     globus_resource_t *                 resource)
324 3297 {
325 3297     globus_resource_t                   iresource;
326 3297     globus_result_t                     result;
327 3297     GlobusFuncName(globus_resource_find);
328
329 3297     GlobusResourceDebugEnter();
330 3297     if (id == NULL || resource == NULL)
331     {
332 0         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
333
334 0         goto out;
335     }
336
337     /* look up in hashtable */
338 3297     globus_mutex_lock(&globus_l_wsrf_resource_lock);
339 3297     iresource = globus_hashtable_lookup(&globus_l_wsrf_resources, (void *) id);
340 3297     if (iresource == NULL)
341     {
342 21         result = GLOBUS_RESOURCE_ERROR_UNKNOWN_RESOURCE(id);
343
344 21         goto unlock_error;
345     }
346
347 3276     globus_l_resource_lock(iresource);
348
349 3276     *resource = iresource;
350 3276     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
351
352 3276     GlobusResourceDebugExit();
353 3276     return GLOBUS_SUCCESS;
354
355 unlock_error:
356 21     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
357
358 out:
359 21     GlobusResourceDebugExit();
360 21     return result;
361 }
362 /* globus_resource_find() */
363
364 /**
365  * Release a reference to a resource.
366  * @ingroup resource
367  *
368  * The caller must no longer access the resource reference once this function
369  * returns. This function destroys the resource when all references to the
370  * resource have been finished and globus_resource_destroy() or the time
371  * passed to globus_resource_set_destroy_time() has elapsed.
372  *
373  * @param resource
374  *     Resource handle which is no longer being used.
375  *
376  * @retval GLOBUS_SUCCESS
377  *     Successfully finished using the resource handle.
378  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
379  *     Null resource handle passed to this function.
380  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NO_REFERENCES
381  *     No references to resource exist.
382  *
383  * @see #globus_resource_find(), #globus_resource_create()
384  */
385 globus_result_t
386 globus_resource_finish(
387     globus_resource_t                   resource)
388 3319 {
389 3319     globus_result_t                     result = GLOBUS_SUCCESS;
390 3319     GlobusFuncName(globus_resource_finish);
391
392 3319     GlobusResourceDebugEnter();
393 3319     if (resource == NULL)
394     {
395 1         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
396
397 1         goto out;
398     }
399
400 3318     globus_mutex_lock(&globus_l_wsrf_resource_lock);
401 3318     globus_l_resource_unlock(resource);
402 3318     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
403
404 out:
405 3319     GlobusResourceDebugExit();
406 3319     return result;
407 }
408 /* globus_resource_finish() */
409
410 /**
411  * Schedule delayed destruction of a resource.
412  * @ingroup resource
413  *
414  * When the time indicated by the @a timestamp parameter has been reached, the
415  * resource will be destroyed once all remaining references to the resource
416  * have been finished.
417  *
418  * If globus_resource_destroy() is called before this time has been reached,
419  * the timestamp is effectively ignored. 
420  * 
421  * @param resource
422  *     Resource handle which is no longer being used.
423  * @param timestamp
424  *     Time when the resource should be destroyed.
425  *
426  * @retval GLOBUS_SUCCESS
427  *     Successfully set the destroy timeout for the resource handle.
428  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
429  *     Null resource handle passed to this function.
430  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_ALREADY_DESTROYED
431  *     The resource has already been destroyed.
432  *
433  * @see #globus_resource_create(), #globus_resource_destroy()
434  */
435 globus_result_t
436 globus_resource_set_destroy_time(
437     globus_resource_t                   resource,
438     const globus_abstime_t *            timestamp)
439 26 {
440 26     globus_result_t                     result;
441 26     globus_bool_t                       active;
442 26     globus_abstime_t                    now;
443 26     globus_reltime_t                    delay;
444 26     char *                              tmp_id;
445 26     GlobusFuncName(globus_resource_set_destroy_time);
446
447 26     GlobusResourceDebugEnter();
448 26     if (resource == NULL || timestamp == NULL)
449     {
450 2         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
451
452 2         goto out;
453     }
454
455 24     tmp_id = globus_libc_strdup(resource->id);
456
457 24     if (tmp_id == NULL)
458     {
459 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
460
461 0         goto out;
462     }
463
464 24     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
465     {
466         /* Try to cancel the previous instance of the lifetime callback */
467 0         result = globus_callback_unregister(
468             resource->lifetime_callback,
469             NULL,
470             NULL,
471             &active);
472
473 0         if (result != GLOBUS_SUCCESS || active)
474         {
475             /* Callback can't be stopped---assume this resource is already
476              * going to be destroyed very soon.
477              */
478 0             result = GLOBUS_RESOURCE_ERROR_ALREADY_DESTROYED();
479
480 0             goto free_tmp_id_out;
481         }
482 0         resource->lifetime_callback = GLOBUS_NULL_HANDLE;
483
484 0         resource->destroy_time.tv_sec = 0L;
485 0         resource->destroy_time.tv_nsec = 0L;
486     }
487
488 24     GlobusTimeAbstimeSet(now, 0, 0);
489
490 24     if (globus_abstime_cmp(&now, timestamp) >= 0)
491     {
492 0         delay.tv_sec = 0;
493 0         delay.tv_usec = 0;
494     }
495     else
496     {
497 24         GlobusTimeAbstimeDiff(delay, *timestamp, now);
498     }
499
500 24     result = globus_callback_register_oneshot(
501         &resource->lifetime_callback,
502         &delay,
503         globus_l_resource_lifetime_expired,
504         tmp_id);
505
506 24     if (result == GLOBUS_SUCCESS)
507     {
508 24         tmp_id = NULL;
509     }
510     else
511     {
512 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
513     }
514 24     resource->destroy_time.tv_sec = timestamp->tv_sec;
515 24     resource->destroy_time.tv_nsec = timestamp->tv_nsec;
516
517 free_tmp_id_out:
518 24     if (tmp_id)
519     {
520 0         globus_libc_free(tmp_id);
521     }
522 out:
523 26     GlobusResourceDebugExit();
524
525 26     return result;
526 }
527 /* globus_resource_set_destroy_time() */
528
529 /**
530  * Get scheduled destruction time of a resource.
531  * @ingroup resource
532  *
533  * @param resource
534  *     Resource handle to check.
535  * @param timestamp
536  *     Pointer to value to be set to the time when the resource should be
537  *     destroyed.
538  *
539  * @retval GLOBUS_SUCCESS
540  *     Successfully set the destroy timeout for the resource handle.
541  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
542  *     Null resource handle passed to this function.
543  *
544  * @see #globus_resource_create(), #globus_resource_destroy()
545  */
546 globus_result_t
547 globus_resource_get_destroy_time(
548     const globus_resource_t             resource,
549     globus_abstime_t *                  timestamp)
550 95 {
551 95     globus_result_t                     result = GLOBUS_SUCCESS;
552 95     GlobusFuncName(globus_resource_get_destroy_time);
553
554 95     GlobusResourceDebugEnter();
555 95     if (resource == NULL || timestamp == NULL)
556     {
557 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
558     }
559
560 95     timestamp->tv_sec = resource->destroy_time.tv_sec;
561 95     timestamp->tv_nsec = resource->destroy_time.tv_nsec;
562
563 95     GlobusResourceDebugExit();
564 95     return result;
565 }
566 /* globus_resource_get_destroy_time() */
567
568 /**
569  * Finish a reference to a resource and then destroy the resource.
570  * @ingroup resource
571  *
572  * This function blocks until all references to the resource have been
573  * finished and then destroys the contents of the resource. This destruction
574  * will take precedence over a not yet elapsed delayed destruction time set by
575  * #globus_resource_set_destroy_time(). It is an
576  * error to access the resource once this function returns. All subsequent
577  * attempts to find this resource will fail.
578  * 
579  * @param resource
580  *     Resource handle which is no longer being used.
581  *
582  * @retval GLOBUS_SUCCESS
583  *     Successfully finished using the resource handle.
584  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
585  *     Null resource handle passed to this function.
586  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_ALREADY_DESTROYED
587  *     The resource has already been destroyed.
588  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NO_REFERENCES
589  *     No references to resource exist.
590  *
591  * @see #globus_resource_create(), #globus_resource_destroy(),
592  * #globus_resource_set_destroy_time()
593  */
594 globus_result_t
595 globus_resource_destroy(
596     globus_resource_t                   resource)
597 141 {
598 141     globus_result_t                     result = GLOBUS_SUCCESS;
599 141     globus_bool_t                       active;
600 141     GlobusFuncName(globus_resource_destroy);
601
602 141     GlobusResourceDebugEnter();
603 141     if (resource == NULL)
604     {
605 1         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
606
607 1         goto out;
608     }
609
610 140     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
611     {
612         /* Try to cancel the lifetime callback */
613 18         result = globus_callback_unregister(
614             resource->lifetime_callback,
615             NULL,
616             NULL,
617             &active);
618
619 18         if (result != GLOBUS_SUCCESS || active)
620         {
621             /* Callback can't be stopped---we'll rely on it to destroy the
622              * service
623              */
624 18             goto out;
625         }
626 18         resource->lifetime_callback = GLOBUS_NULL_HANDLE;
627 18         resource->destroy_time.tv_sec = 0L;
628 18         resource->destroy_time.tv_nsec = 0L;
629     }
630
631 140     globus_mutex_lock(&globus_l_wsrf_resource_lock);
632     /*
633      * Release lock acquired during find or create. This allows other
634      * interested threads to get their last grabs at this resource.
635      */
636 140     globus_l_resource_unlock(resource);
637
638     /* Can't be found by other threads once this is removed from the hashtable
639      */
640 140     (void) globus_hashtable_remove(&globus_l_wsrf_resources, resource->id);
641
642     /* Wait for other threads which have already partially found it to finish.
643      */
644 140     while (resource->lock_count > 0 || resource->waiters > 0)
645     {
646 0         globus_cond_wait(&resource->cond, &globus_l_wsrf_resource_lock);
647     }
648 140     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
649
650     /* this call releases the lock on the resource's recursive mutex */
651 140     globus_l_resource_destroy(resource);
652
653 out:
654 141     GlobusResourceDebugExit();
655 141     return result;
656 }
657 /* globus_resource_destroy() */
658
659 /**
660  * Retrieve resource-specific data from a resource handle.
661  * @ingroup resource
662  *
663  * @param resource
664  *     Resource handle to query for data.
665  * @param data
666  *     Pointer to be set to the value of resource-specific data.
667  *
668  * @retval GLOBUS_SUCCESS
669  *     Resource specific data successfully retrieved.
670  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
671  *     Null parameter passed to this function. The value of @a data is
672  *     undefined when this value is returned.
673  *
674  * @see #globus_resource_set_resource_specific()
675  */
676 globus_result_t
677 globus_resource_get_resource_specific(
678     const globus_resource_t             resource,
679     void **                             data)
680 56 {
681 56     globus_result_t                     result = GLOBUS_SUCCESS;
682 56     GlobusFuncName(globus_resource_get_resource_specific);
683
684 56     GlobusResourceDebugEnter();
685
686 56     if (resource == NULL || data == NULL)
687     {
688 2         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
689 2         goto out;
690     }
691 54     *data = resource->data;
692
693 out:
694 56     GlobusResourceDebugExit();
695 56     return result;
696 }
697 /* globus_resource_get_resource_specific() */
698
699 /**
700  * Set resource-specific data for a resource handle.
701  * @ingroup resource
702  *
703  * @param resource
704  *     Resource handle to modify.
705  * @param data
706  *     New value of the resource-specific data. If a non-NULL value was
707  *     previously set as resource-specific data, it will be destroyed.
708  * @param destroy_func
709  *     Function pointer of a function to call to destroy the resource-specific
710  *     data. This will be called either when a new instance of
711  *     resource-specific data is set by calling this function again, or when
712  *     the resource is destroyed because of globus_resource_finish() being
713  *     called.
714  *
715  * @retval GLOBUS_SUCCESS
716  *     Resource specific data successfully set.
717  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
718  *     Null parameter passed to this function. 
719  *
720  * @see #globus_resource_get_resource_specific(), #globus_resource_finish()
721  */
722 globus_result_t
723 globus_resource_set_resource_specific(
724     globus_resource_t                   resource,
725     void *                              data,
726     globus_resource_destroy_t           destroy_func)
727 25 {
728 25     globus_result_t                     result = GLOBUS_SUCCESS;
729 25     GlobusFuncName(globus_resource_set_resource_specific);
730
731 25     GlobusResourceDebugEnter();
732 25     if (resource == NULL)
733     {
734 1         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
735
736 1         goto out;
737     }
738
739 24     if ((resource->data != NULL) && (resource->data_destroy_func != NULL))
740     {
741 1         (resource->data_destroy_func)(resource->data);
742     }
743
744 24     resource->data = data;
745 24     resource->data_destroy_func = destroy_func;
746
747 out:
748 25     GlobusResourceDebugExit();
749 25     return result;
750 }
751 /* globus_resource_set_resource_specific() */
752
753 /**
754  * Retrieve resource-specific data from a resource handle based on a key.
755  * @ingroup resource
756  *
757  * @param resource
758  *     Resource handle to query for data.
759  * @param key
760  *     Key describing the data.
761  * @param data
762  *     Pointer to be set to the value of resource-specific data.
763  *
764  * @retval GLOBUS_SUCCESS
765  *     Resource specific data successfully retrieved.
766  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
767  *     Null parameter passed to this function. The value of @a data is
768  *     undefined when this value is returned.
769  *
770  * @see #globus_resource_set_resource_specific()
771  */
772 globus_result_t
773 globus_resource_get_resource_specific_by_key(
774     const globus_resource_t             resource,
775     const char *                        key,
776     void **                             data)
777 0 {
778 0     globus_i_resource_specific_data_t * data_info;
779 0     globus_result_t                     result = GLOBUS_SUCCESS;
780 0     GlobusFuncName(globus_resource_get_resource_specific);
781
782 0     GlobusResourceDebugEnter();
783 0     if (resource == NULL || data == NULL)
784     {
785 0         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
786
787 0         goto out;
788     }
789
790 0     if (resource->keyed_data == NULL)
791     {
792 0         *data = NULL;
793     }
794     else
795     {
796 0         data_info = globus_hashtable_lookup(
797                 &resource->keyed_data,
798                 (void *) key);
799
800 0         if (data_info)
801         {
802 0             *data = data_info->data;
803         }
804         else
805         {
806 0             *data = NULL;
807         }
808     }
809
810 out:
811 0     GlobusResourceDebugExit();
812
813 0     return result;
814 }
815 /* globus_resource_get_resource_specific_by_key() */
816
817 /**
818  * Set resource-specific data for a resource handle based on a key.
819  * @ingroup resource
820  *
821  * @param resource
822  *     Resource handle to modify.
823  * @param data
824  *     New value of the resource-specific data. If a non-NULL value was
825  *     previously set as resource-specific data, it will be destroyed.
826  * @param destroy_func
827  *     Function pointer of a function to call to destroy the resource-specific
828  *     data. This will be called either when a new instance of
829  *     resource-specific data is set by calling this function again, or when
830  *     the resource is destroyed because of globus_resource_finish() being
831  *     called.
832  *
833  * @retval GLOBUS_SUCCESS
834  *     Resource specific data successfully set.
835  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
836  *     Null parameter passed to this function. 
837  *
838  * @see #globus_resource_get_resource_specific(), #globus_resource_finish()
839  */
840 globus_result_t
841 globus_resource_set_resource_specific_by_key(
842     globus_resource_t                   resource,
843     const char *                        key,
844     void *                              data,
845     globus_resource_destroy_t           destroy_func)
846 134 {
847 134     globus_result_t                     result = GLOBUS_SUCCESS;
848 134     int                                 rc;
849 134     globus_i_resource_specific_data_t * data_info;
850 134     GlobusFuncName(globus_resource_set_resource_specific);
851
852 134     GlobusResourceDebugEnter();
853 134     if (resource == NULL)
854     {
855 0         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
856
857 0         goto out;
858     }
859
860 134     if (data != NULL && resource->keyed_data == NULL)
861     {
862 93         rc = globus_hashtable_init(
863                 &resource->keyed_data,
864                 3,
865                 globus_hashtable_string_hash,
866                 globus_hashtable_string_keyeq);
867
868 93         if (rc != 0)
869         {
870 0             result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
871
872 0             goto out;
873         }
874     }
875
876 134     if (resource->keyed_data != NULL)
877     {
878 134         data_info = globus_hashtable_lookup(
879                 &resource->keyed_data,
880                 (void *) key);
881
882 134         if (data_info == NULL)
883         {
884 134             data_info = calloc(1, sizeof(globus_i_resource_specific_data_t));
885 134             if (data_info == NULL)
886             {
887 0                 result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
888
889 0                 goto out;
890             }
891 134             data_info->key = globus_libc_strdup(key);
892
893 134             if (data_info->key == NULL)
894             {
895 0                 result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
896
897 0                 goto free_data_info_out;
898             }
899
900 134             rc = globus_hashtable_insert(
901                     &resource->keyed_data,
902                     data_info->key,
903                     data_info);
904 134             if (rc != GLOBUS_SUCCESS)
905             {
906 0                 result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
907
908 0                 goto free_data_info_key_out;
909             }
910         }
911 0         else if (data_info != NULL && data_info->data_destroy_func != NULL)
912         {
913 0             (data_info->data_destroy_func)(data_info->data);
914         }
915
916 134         data_info->data = data;
917 134         data_info->data_destroy_func = destroy_func;
918     }
919
920 134     if (result != GLOBUS_SUCCESS)
921     {
922 free_data_info_key_out:
923 0         free(data_info->key);
924 free_data_info_out:
925 0         free(data_info);
926     }
927 out:
928 134     GlobusResourceDebugExit();
929
930 134     return result;
931 }
932 /* globus_resource_set_resource_specific_by_key() */
933
934 /**
935  * Retrieve resource-specific type registry from a resource handle.
936  * @ingroup resource
937  *
938  * @param resource
939  *     Resource handle to query for data.
940  * @param registry
941  *     Pointer to be set to the value of type registry.
942  *
943  * @retval GLOBUS_SUCCESS
944  *     Type registry successfully retrieved.
945  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
946  *     Null parameter passed to this function. The value of @a registry is
947  *     undefined when this value is returned.
948  *
949  * @see #globus_resource_set_type_registry()
950  */
951 globus_result_t
952 globus_resource_get_type_registry(
953     globus_resource_t                   resource,
954     globus_xsd_type_registry_t *        registry)
955 4 {
956 4     globus_result_t                     result = GLOBUS_SUCCESS;
957 4     GlobusFuncName(globus_resource_get_type_registry);
958
959 4     GlobusResourceDebugEnter();
960
961 4     if (resource == NULL || registry == NULL)
962     {
963 2         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
964
965 2         goto out;
966     }
967 2     *registry = resource->registry;
968
969 out:
970 4     GlobusResourceDebugExit();
971 4     return result;
972 }
973 /* globus_resource_get_type_registry() */
974
975 /**
976  * Set type registry associated with a resource handle.
977  * @ingroup resource
978  *
979  * @param resource
980  *     Resource handle to modify.
981  * @param registry
982  *     New value for the resource's type registry.
983  *
984  * @retval GLOBUS_SUCCESS
985  *     Resource's type registry successfully set.
986  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
987  *     Null parameter passed to this function. 
988  *
989  * @see #globus_resource_get_type_registry()
990  */
991 globus_result_t
992 globus_resource_set_type_registry(
993     globus_resource_t                   resource,
994     globus_xsd_type_registry_t          registry)
995 1 {
996 1     globus_result_t                     result = GLOBUS_SUCCESS;
997
998 1     GlobusFuncName(globus_resource_set_type_registry);
999
1000 1     GlobusResourceDebugEnter();
1001 1     if (resource == NULL || registry == NULL)
1002     {
1003 0         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
1004
1005 0         goto out;
1006     }
1007
1008 1     resource->registry = registry;
1009
1010 out:
1011 1     GlobusResourceDebugExit();
1012 1     return result;
1013 }
1014 /* globus_resource_set_type_registry() */
1015
1016 globus_result_t
1017 globus_resource_get_id(
1018     const globus_resource_t             resource,
1019     const char **                       resource_id)
1020 414 {
1021 414     globus_result_t                     result = GLOBUS_SUCCESS;
1022
1023 414     GlobusFuncName(globus_resource_get_id);
1024
1025 414     GlobusResourceDebugEnter();
1026 414     if (resource == NULL || resource_id == NULL)
1027     {
1028 0         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
1029
1030 0         goto out;
1031     }
1032
1033 414     *resource_id = resource->id;
1034
1035 out:
1036 414     GlobusResourceDebugExit();
1037
1038 414     return result;
1039 }
1040
1041 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
1042 /**
1043  * Destroy a resource stored in the resource hashtable at deactivation time.
1044  * Resources could have 
1045  * - references from finds which were not finished
1046  * - other threads blocking in globus_resource_destroy()
1047  * - other threads blocking in a callback from
1048  *   globus_resource_set_destroy_time()
1049  * This function is called with the resource mutex locked.
1050  */
1051 static
1052 void
1053 globus_l_wsrf_resource_hashtable_destroy(
1054     void *                              datum)
1055 259 {
1056 259     globus_resource_t                   resource = datum;
1057 259     globus_bool_t                       active = GLOBUS_FALSE;
1058 259     int                                 rc;
1059 259     GlobusFuncName(globus_l_wsrf_resource_hashtable_destroy);
1060
1061 259     GlobusResourceDebugEnter();
1062
1063 259     rc = globus_l_resource_try_lock(resource);
1064
1065 259     if (rc == EBUSY)
1066     {
1067         /* Why is this locked? Missing finish? */
1068
1069 0         goto out;
1070     }
1071
1072     /* If a callback is registered, try to remove it; if that fails, then
1073      * the callback code will try to destroy the resource, so we'll act
1074      * later as if a destroy call was already blocking.
1075      */
1076 259     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
1077     {
1078 1         globus_callback_unregister(
1079             resource->lifetime_callback,
1080             NULL,
1081             NULL,
1082             &active);
1083
1084 1         resource->lifetime_callback = GLOBUS_NULL_HANDLE;
1085     }
1086
1087 259     if (! active)
1088     {
1089         /* No destroy is in progress for this resource, so we must
1090          * free it ourselves.
1091          */
1092
1093 259         globus_mutex_unlock(&globus_l_wsrf_resource_lock);
1094 259         globus_l_resource_destroy(resource);
1095 259         globus_mutex_lock(&globus_l_wsrf_resource_lock);
1096     }
1097 out:
1098 259     GlobusResourceDebugExit();
1099 }
1100 /* globus_l_wsrf_resource_hashtable_destroy() */
1101
1102 /**
1103  * Free memory associated with a resource handle.
1104  * When this function is called for a resource, all callbacks and threads
1105  * blocking on the condition of this resource must be finished, otherwise
1106  * weird things may happen in those threads.
1107  *
1108  * This function is called with the mutex locked.
1109  *
1110  * @param datum
1111  *     Pointer to the resource.
1112  */
1113 static
1114 void
1115 globus_l_resource_destroy(
1116     globus_resource_t                   resource)
1117 399 {
1118 399     GlobusFuncName(globus_l_resource_destroy);
1119
1120 399     GlobusResourceDebugEnter();
1121
1122 399     if (resource->data && resource->data_destroy_func)
1123     {
1124 7         resource->data_destroy_func(resource->data);
1125     }
1126
1127 399     if (resource->keyed_data != NULL)
1128     {
1129 59         globus_hashtable_destroy_all(
1130                 &resource->keyed_data,
1131                 globus_l_resource_destroy_keyed_data);
1132     }
1133
1134 399     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
1135     {
1136 0         globus_callback_unregister(
1137             resource->lifetime_callback,
1138             NULL,
1139             NULL,
1140             NULL);
1141     }
1142 399     if (resource->resource_properties != NULL)
1143     {
1144 399         globus_hashtable_destroy_all(
1145                 &resource->resource_properties,
1146                 globus_l_resource_property_destroy);
1147     }
1148 399     globus_list_destroy_all(resource->property_changed_callbacks, free);
1149 399     globus_libc_free(resource->id);
1150
1151 399     globus_cond_destroy(&resource->cond);
1152
1153 399     globus_libc_free(resource);
1154
1155 399     GlobusResourceDebugExit();
1156 }
1157 /* globus_l_resource_destroy() */
1158
1159 /**
1160  * Free memory associated with a resource property.
1161  *
1162  * Function signature uses a void * to match the globus_hashtable_destroy_all()
1163  * prototype.
1164  * 
1165  * @param datum
1166  *     Pointer to a globus_resource_property_t to destroy.
1167  */
1168 static
1169 void
1170 globus_l_resource_property_destroy(
1171     void *                              datum)
1172 744 {
1173 744     globus_resource_property_t *        prop = datum;
1174 744     GlobusFuncName(globus_l_resource_property_destroy);
1175
1176 744     GlobusResourceDebugEnter();
1177
1178 744     if (prop->property)
1179     {
1180 342         prop->type_info->destroy(prop->property);
1181     }
1182 744     if (prop->name.Namespace)
1183     {
1184 744         globus_libc_free(prop->name.Namespace);
1185     }
1186 744     if (prop->name.local)
1187     {
1188 744         globus_libc_free(prop->name.local);
1189     }
1190 744     globus_libc_free(prop);
1191
1192 744     GlobusResourceDebugExit();
1193 }
1194 /* globus_l_resource_property_destroy() */
1195
1196 static
1197 void
1198 globus_l_resource_lifetime_expired(
1199     void *                              arg)
1200 2 {
1201 2     char *                              id = arg;
1202 2     globus_resource_t                   resource;
1203 2     globus_result_t                     result;
1204 2     GlobusFuncName(globus_l_resource_lifetime_expired);
1205
1206 2     GlobusResourceDebugEnter();
1207
1208 2     result = globus_resource_find(id, &resource);
1209
1210 2     if (result == GLOBUS_SUCCESS)
1211     {
1212 2         globus_callback_unregister(
1213             resource->lifetime_callback,
1214             NULL,
1215             NULL,
1216             NULL);
1217 2         resource->lifetime_callback = GLOBUS_NULL_HANDLE;
1218 2         globus_resource_destroy(resource);
1219     }
1220 2     GlobusResourceDebugExit();
1221 }
1222 /* globus_l_resource_lifetime_expired() */
1223
1224 /* called with globus_l_wsrf_resource_lock already locked */
1225 static
1226 int
1227 globus_l_resource_try_lock(
1228     globus_resource_t                   resource)
1229 259 {
1230 259     globus_thread_t                     thread_id = globus_thread_self();
1231 259     int                                 rc = 0;
1232 259     GlobusFuncName(globus_l_resource_try_lock);
1233
1234 259     GlobusResourceDebugEnter();
1235
1236 259     if (resource->lock_count > 0 && 
1237             !globus_thread_equal(resource->thread_id, thread_id))
1238     {
1239 0         rc = EBUSY;
1240
1241 0         goto out;
1242     }
1243 259     resource->lock_count++;
1244 259     resource->thread_id = thread_id;
1245
1246 out:
1247 259     GlobusResourceDebugExit();
1248 259     return rc;
1249 }
1250 /* globus_l_resource_try_lock() */
1251
1252 /* called with globus_l_wsrf_resource_lock already locked */
1253 static
1254 void
1255 globus_l_resource_lock(
1256     globus_resource_t                   resource)
1257 3276 {
1258 3276     globus_thread_t                     thread_id = globus_thread_self();
1259 3276     GlobusFuncName(globus_l_resource_lock);
1260
1261 3276     GlobusResourceDebugEnter();
1262
1263 3276     GlobusResourceDebugPrintf(GLOBUS_L_RESOURCE_DEBUG_DEBUG,
1264             ("Trying to lock resource %p\n", resource));
1265
1266 3276     if (resource->lock_count > 0 && 
1267             !globus_thread_equal(resource->thread_id, thread_id))
1268     {
1269 0         GlobusResourceDebugPrintf(GLOBUS_L_RESOURCE_DEBUG_DEBUG,
1270                 ("added to waiters list for %p [%d others]\n",
1271                  resource,
1272                  resource->waiters));
1273 0         resource->waiters++;
1274
1275 0         do
1276         {
1277 0             GlobusResourceDebugPrintf(GLOBUS_L_RESOURCE_DEBUG_DEBUG,
1278                     ("waiting for condition for resource %p\n", resource));
1279 0             globus_cond_wait(&resource->cond, &globus_l_wsrf_resource_lock);
1280 0         } while (resource->lock_count > 0);
1281
1282 0         resource->waiters--;
1283     }
1284 3276     resource->lock_count++;
1285 3276     resource->thread_id = thread_id;
1286 3276     GlobusResourceDebugPrintf(GLOBUS_L_RESOURCE_DEBUG_DEBUG,
1287             ("locked resource %p [lock count %d]\n",
1288              resource, resource->lock_count));
1289 3276     GlobusResourceDebugExit();
1290 }
1291
1292 /* called with globus_l_wsrf_resource_lock already locked */
1293 static
1294 void
1295 globus_l_resource_unlock(
1296     globus_resource_t                   resource)
1297 3458 {
1298 3458     globus_thread_t                     thread_id = globus_thread_self();
1299 3458     GlobusFuncName(globus_l_resource_lock);
1300
1301 3458     GlobusResourceDebugEnter();
1302
1303 3458     GlobusResourceDebugPrintf(GLOBUS_L_RESOURCE_DEBUG_DEBUG,
1304             ("trying to unlock resource %p [lock count %d]\n",
1305              resource, resource->lock_count));
1306
1307 3458     globus_assert(resource->lock_count > 0);
1308 3458     globus_assert(globus_thread_equal(resource->thread_id, thread_id));
1309
1310 3458     if (--resource->lock_count == 0)
1311     {
1312 3263         GlobusResourceDebugPrintf(GLOBUS_L_RESOURCE_DEBUG_DEBUG,
1313                 ("Unlock resource %p [%d waiters] \n",
1314                  resource, resource->waiters));
1315 3263         memset(&resource->thread_id, '\0', sizeof(globus_thread_t));
1316
1317 3263         globus_cond_broadcast(&resource->cond);
1318     }
1319 3458     GlobusResourceDebugExit();
1320 }
1321 /* globus_l_resource_unlock() */
1322
1323 static
1324 void
1325 globus_l_resource_destroy_keyed_data(
1326     void *                              p)
1327 97 {
1328 97     globus_i_resource_specific_data_t * data_info = p;
1329 97     GlobusFuncName(globus_l_resource_destroy_keyed_data);
1330
1331 97     GlobusResourceDebugEnter();
1332
1333 97     if (data_info->data != NULL && data_info->data_destroy_func != NULL)
1334     {
1335 97         data_info->data_destroy_func(data_info->data);
1336     }
1337 97     free(data_info->key);
1338 97     free(data_info);
1339
1340 97     GlobusResourceDebugExit();
1341 }
1342 /* globus_l_resource_destroy_keyed_data() */