1 /*
2  * Portions of this file Copyright 1999-2005 University of Chicago
3  * Portions of this file Copyright 1999-2005 The University of Southern California.
4  *
5  * This file or a portion of this file is licensed under the
6  * terms of the Globus Toolkit Public License, found at
7  * http://www.globus.org/toolkit/download/license.html.
8  * If you redistribute this file, with or without
9  * modifications, you must include this notice in the file.
10  */
11
12 #include "globus_soap_message.h"
13 #include "globus_i_wsrf_resource.h"
14
15 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
16 /**
17  * @file globus_wsrf_resource.c
18  *
19  * Module implementation and resource creation/destruction functions.
20  */
21 static
22 int
23 globus_l_wsrf_resource_activate(void);
24
25 static
26 int
27 globus_l_wsrf_resource_deactivate(void);
28
29 static
30 void
31 globus_l_wsrf_resource_hashtable_destroy(
32     void *                              datum);
33
34 static
35 void
36 globus_l_resource_destroy(
37     globus_resource_t                   resource);
38
39 static
40 void
41 globus_l_resource_property_destroy(
42     void *                              datum);
43
44 static
45 void
46 globus_l_resource_lifetime_expired(
47     void *                              arg);
48
49 /**
50  * Table mapping resource id strings to resource instances.
51  */
52 static globus_hashtable_t globus_l_wsrf_resources;
53 /**
54  * Lock for resource table.
55  */
56 static globus_mutex_t globus_l_wsrf_resource_lock;
57
58 globus_module_descriptor_t
59 globus_i_wsrf_resource_module = 
60 {
61     "globus_wsrf_resource",
62     globus_l_wsrf_resource_activate,
63     globus_l_wsrf_resource_deactivate
64 };
65
66 /**
67  * Activate the WSRF Resource module.
68  *
69  * Activate modules this one uses, create the resource ID : resource hashtable
70  * and lock.
71  *
72  * @retval GLOBUS_SUCCESS
73  *     Module successfully activated.
74  * @retval 1
75  *     Unable to activate module.
76  */
77 static
78 int
79 globus_l_wsrf_resource_activate(void)
80 54 {
81 54     int rc;
82
83 54     rc = globus_module_activate(GLOBUS_COMMON_MODULE);
84 54     if (rc != GLOBUS_SUCCESS)
85     {
86 0         goto error;
87     }
88 54     rc = globus_module_activate(GLOBUS_SOAP_MESSAGE_MODULE);
89
90 54     if (rc != GLOBUS_SUCCESS)
91     {
92 0         goto deactivate_common_error;
93     }
94
95 54     rc = globus_hashtable_init(&globus_l_wsrf_resources,
96             16,
97             globus_hashtable_string_hash,
98             globus_hashtable_string_keyeq);
99
100 54     if (rc != GLOBUS_SUCCESS)
101     {
102 0         goto deactivate_message_error;
103     }
104
105 54     rc = globus_mutex_init(&globus_l_wsrf_resource_lock, NULL);
106 54     if (rc != GLOBUS_SUCCESS)
107     {
108 0         goto destroy_hashtable_error;
109     }
110     
111 54     return GLOBUS_SUCCESS;
112
113 destroy_hashtable_error:
114 0     globus_hashtable_destroy(&globus_l_wsrf_resources);
115 deactivate_message_error:
116 0     globus_module_deactivate(GLOBUS_SOAP_MESSAGE_MODULE);
117 deactivate_common_error:
118 0     globus_module_deactivate(GLOBUS_COMMON_MODULE);
119 error:
120 0     return 1;
121 }
122 /* globus_l_wsrf_resource_activate() */
123
124 static
125 int
126 globus_l_wsrf_resource_deactivate(void)
127 26 {
128 26     globus_mutex_lock(&globus_l_wsrf_resource_lock);
129
130 26     globus_hashtable_destroy_all(
131         &globus_l_wsrf_resources,
132         globus_l_wsrf_resource_hashtable_destroy);
133
134 26     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
135 26     globus_mutex_destroy(&globus_l_wsrf_resource_lock);
136 26     globus_module_deactivate(GLOBUS_SOAP_MESSAGE_MODULE);
137 26     globus_module_deactivate(GLOBUS_COMMON_MODULE);
138
139 26     return 0;
140 }
141 /* globus_l_wsrf_resource_deactivate() */
142 #endif /* GLOBUS_DONT_DOCUMENT_INTERNAL */
143
144 /**
145  * Create a new WSRF Resource.
146  * @ingroup resource
147  *
148  * Allocate a new reference-counted WSRF resource. Calls to 
149  * globus_resource_find() and globus_resource_finish() manipulate the resource
150  * count. Every call to globus_resource_find() must be paired with a call to
151  * globus_resource_finish(). The service which
152  * calls globus_resource_create() must call globus_resource_destroy() or
153  * globus_resource_set_destroy_time() to destroy the resource.
154  *
155  * @param id
156  *     Identifier to use for the new resource.
157  * @param resource
158  *     Pointer to be set to the newly created resource handle.
159  *
160  * @retval GLOBUS_SUCCESS
161  *     New resource created successfully.
162  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
163  *     One of the parameters to this function was NULL.
164  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_DUPLICATE_RESOURCE
165  *     Another resource with the specified id already exists, so a new one
166  *     could not be created.
167  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_OUT_OF_MEMORY
168  *     Insufficient memory to create the new resource.
169  *
170  * @see globus_resource_find(), globus_resource_finish()
171  */
172 globus_result_t
173 globus_resource_create(
174     const char *                        id,
175     globus_resource_t *                 resource)
176 48 {
177 48     globus_resource_t                   iresource;
178 48     globus_result_t                     result = GLOBUS_SUCCESS;
179 48     int                                 rc;
180 48     GlobusFuncName(globus_resource_create);
181
182 48     if (id == NULL || resource == NULL)
183     {
184 0         result = GLOBUS_RESOURCE_ERROR_NULL_PARAM();
185 0         goto error;
186     }
187
188 48     globus_mutex_lock(&globus_l_wsrf_resource_lock);
189
190 48     iresource = globus_hashtable_lookup(&globus_l_wsrf_resources, (void *) id);
191
192 48     if (iresource != NULL)
193     {
194 0         result = GLOBUS_RESOURCE_ERROR_DUPLICATE_RESOURCE(id);
195
196 0         goto unlock_error;
197     }
198
199 48     iresource = globus_calloc(1, sizeof(struct globus_resource_s));
200 48     if (iresource == NULL)
201     {
202 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
203
204 0         goto unlock_error;
205     }
206
207 48     iresource->id = globus_libc_strdup(id);
208 48     if (iresource->id == NULL)
209     {
210 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
211
212 0         goto free_resource_error;
213     }
214
215 48     rc = globus_hashtable_init(&iresource->resource_properties,
216             31,
217             xsd_QName_hash,
218             xsd_QName_keyeq);
219
220 48     if (rc != GLOBUS_SUCCESS)
221     {
222 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
223
224 0         goto free_id_out;
225     }
226
227 48     globus_cond_init(&iresource->cond, NULL);
228 48     iresource->lifetime_callback = GLOBUS_NULL_HANDLE;
229 48     iresource->destroy_blocking = GLOBUS_FALSE;
230
231 48     rc = globus_hashtable_insert(
232             &globus_l_wsrf_resources,
233             iresource->id,
234             iresource);
235
236 48     if (rc != GLOBUS_SUCCESS)
237     {
238 0         result = GLOBUS_RESOURCE_ERROR_OUT_OF_MEMORY();
239
240 0         goto destroy_resource_error;
241     }
242
243 48     *resource = iresource;
244     
245 48     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
246 48     return result;
247
248 destroy_resource_error:
249 0     globus_hashtable_destroy(&iresource->resource_properties);
250 0     globus_cond_destroy(&iresource->cond);
251 free_id_out:
252 0     globus_libc_free(iresource->id);
253 free_resource_error:
254 0     globus_libc_free(iresource);
255 unlock_error:
256 0     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
257 error:
258 0     return result;
259 }
260 /* globus_resource_create() */
261
262 /**
263  * Find the resource associated with a resource identifier.
264  * @ingroup resource
265  *
266  * Returns a reference to the resource associated with the id.
267  * Destroy this reference by calling #globus_resource_finish().
268  *
269  * @param id
270  *     Resource identifier for the desired resource.
271  * @param resource
272  *     Pointer to a resource which will modified to point to the
273  *     named resource if it is found.
274  *
275  * @retval GLOBUS_SUCCESS
276  *     The resource associated with the specified id was found and a reference
277  *     to it was returned in the @a resource parameter.
278  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
279  *     One of the parameters to this function was NULL.
280  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_UNKNOWN_RESOURCE
281  *     No resource with the specified id exists.
282  * @see #globus_resource_create()
283  */
284 globus_result_t
285 globus_resource_find(
286     const char *                        id,
287     globus_resource_t *                 resource)
288 2099 {
289 2099     globus_resource_t                   iresource;
290 2099     globus_result_t                     result;
291 2099     GlobusFuncName(globus_resource_find);
292
293 2099     if (id == NULL || resource == NULL)
294     {
295 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
296     }
297
298 2099     globus_mutex_lock(&globus_l_wsrf_resource_lock);
299
300 2099     iresource = globus_hashtable_lookup(&globus_l_wsrf_resources, (void *) id);
301
302 2099     if (iresource == NULL || iresource->destroy_blocking)
303     {
304 1         result = GLOBUS_RESOURCE_ERROR_UNKNOWN_RESOURCE(id);
305
306 1         goto unlock_error;
307     }
308 2098     iresource->references++;
309
310 2098     *resource = iresource;
311 2098     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
312
313 2098     return GLOBUS_SUCCESS;
314 unlock_error:
315 1     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
316
317 1     return result;
318 }
319 /* globus_resource_find() */
320
321 /**
322  * Release a reference to a resource.
323  * @ingroup resource
324  *
325  * The caller must no longer access the resource reference once this function
326  * returns. This function destroys the resource when all references to the
327  * resource have been finished and globus_resource_destroy() or the time
328  * passed to globus_resource_set_destroy_time() has elapsed.
329  *
330  * @param resource
331  *     Resource handle which is no longer being used.
332  *
333  * @retval GLOBUS_SUCCESS
334  *     Successfully finished using the resource handle.
335  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
336  *     Null resource handle passed to this function.
337  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NO_REFERENCES
338  *     No references to resource exist.
339  *
340  * @see #globus_resource_find(), #globus_resource_create()
341  */
342 globus_result_t
343 globus_resource_finish(
344     globus_resource_t                   resource)
345 2060 {
346 2060     globus_result_t                     result = GLOBUS_SUCCESS;
347 2060     GlobusFuncName(globus_resource_finish);
348
349 2060     if (resource == NULL)
350     {
351 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
352     }
353
354 2060     globus_mutex_lock(&globus_l_wsrf_resource_lock);
355
356 2060     if (resource->references <= 0)
357     {
358 0         result = GLOBUS_RESOURCE_ERROR_NO_REFERENCES(resource->id);
359
360 0         goto out;
361     }
362 2060     resource->references--;
363
364 2060     globus_assert(resource->references >= 0);
365
366 2060     if (resource->references == 0)
367     {
368 2060         if (resource->destroy_blocking)
369         {
370 0             globus_cond_signal(&resource->cond);
371         }
372     }
373 out:
374 2060     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
375
376 2060     return result;
377 }
378 /* globus_resource_finish() */
379
380 /**
381  * Destroy the resource after the final reference has been finished.
382  * @ingroup resource
383  *
384  * This function blocks until all references to the resource have been
385  * finished and then destroys the contents of the resource. This destruction
386  * will take precedence over a not yet elapsed delayed destruction time set by
387  * #globus_resource_set_destroy_time(). It is an
388  * error to access the resource once this function returns. All subsequent
389  * attempts to find this resource will fail.
390  * 
391  *
392  * @param resource
393  *     Resource handle which is no longer being used.
394  *
395  * @retval GLOBUS_SUCCESS
396  *     Successfully destroyed the resource handle.
397  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
398  *     Null resource handle passed to this function.
399  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_ALREADY_DESTROYED
400  *     The resource has already been destroyed.
401  *
402  * @see globus_resource_create(), globus_resource_finish_and_destroy(),
403  * #globus_resource_set_destroy_time()
404  */
405 globus_result_t
406 globus_resource_destroy(
407     globus_resource_t                   resource)
408 0 {
409 0     globus_result_t                     result;
410 0     globus_bool_t                       active;
411 0     GlobusFuncName(globus_resource_destroy);
412
413 0     if (resource == NULL)
414     {
415 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
416     }
417
418 0     globus_mutex_lock(&globus_l_wsrf_resource_lock);
419 0     if (resource->destroy_blocking)
420     {
421 0         result = GLOBUS_RESOURCE_ERROR_ALREADY_DESTROYED();
422
423 0         goto out;
424     }
425
426 0     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
427     {
428         /* Try to cancel the lifetime callback */
429 0         result = globus_callback_unregister(
430             resource->lifetime_callback,
431             NULL,
432             NULL,
433             &active);
434
435 0         if (result != GLOBUS_SUCCESS || active)
436         {
437             /* Callback can't be stopped---we'll rely on it to destroy the
438              * service
439              */
440 0             goto out;
441         }
442 0         resource->lifetime_callback = GLOBUS_NULL_HANDLE;
443     }
444
445 0     resource->destroy_blocking = GLOBUS_TRUE;
446
447 0     while (resource->references > 0)
448     {
449 0         globus_cond_wait(&resource->cond, &globus_l_wsrf_resource_lock);
450     }
451 0     (void) globus_hashtable_remove(&globus_l_wsrf_resources,
452             resource->id);
453
454 0     globus_l_resource_destroy(resource);
455
456 out:
457 0     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
458 0     return GLOBUS_SUCCESS;
459 }
460 /* globus_resource_destroy() */
461
462 /**
463  * Schedule delayed destruction of a resource.
464  * @ingroup resource
465  *
466  * When the time indicated by the @a timestamp parameter has been reached, the
467  * resource will be destroyed once all remaining references to the resource
468  * have been finished.
469  *
470  * If globus_resource_destroy() is called before this time has been reached,
471  * the timestamp is effectively ignored. 
472  * 
473  * @param resource
474  *     Resource handle which is no longer being used.
475  * @param timestamp
476  *     Time when the resource should be destroyed.
477  *
478  * @retval GLOBUS_SUCCESS
479  *     Successfully set the destroy timeout for the resource handle.
480  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
481  *     Null resource handle passed to this function.
482  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_ALREADY_DESTROYED
483  *     The resource has already been destroyed.
484  *
485  * @see #globus_resource_create(), #globus_resource_destroy(),
486  * #globus_resource_finish_and_destroy()
487  */
488 globus_result_t
489 globus_resource_set_destroy_time(
490     globus_resource_t                   resource,
491     const globus_abstime_t *            timestamp)
492 15 {
493 15     globus_result_t                     result;
494 15     globus_bool_t                       active;
495 15     globus_abstime_t                    now;
496 15     globus_reltime_t                    delay;
497 15     GlobusFuncName(globus_resource_set_destroy_time);
498
499 15     if (resource == NULL || timestamp == NULL)
500     {
501 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
502     }
503
504 15     globus_mutex_lock(&globus_l_wsrf_resource_lock);
505
506 15     if (resource->destroy_blocking)
507     {
508 0         result = GLOBUS_RESOURCE_ERROR_ALREADY_DESTROYED();
509
510 0         goto out;
511     }
512
513 15     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
514     {
515         /* Try to cancel the previous instance of the lifetime callback */
516 0         result = globus_callback_unregister(
517             resource->lifetime_callback,
518             NULL,
519             NULL,
520             &active);
521
522 0         if (result != GLOBUS_SUCCESS || active)
523         {
524             /* Callback can't be stopped---assume this resource is already
525              * going to be destroyed very soon.
526              */
527 0             result = GLOBUS_RESOURCE_ERROR_ALREADY_DESTROYED();
528
529 0             goto out;
530         }
531 0         resource->lifetime_callback = GLOBUS_NULL_HANDLE;
532     }
533
534 15     GlobusTimeAbstimeSet(now, 0, 0);
535
536 15     if (globus_abstime_cmp(&now, timestamp) >= 0)
537     {
538 0         delay.tv_sec = 0;
539 0         delay.tv_usec = 0;
540     }
541     else
542     {
543 15         GlobusTimeAbstimeDiff(delay, *timestamp, now);
544     }
545
546 15     result = globus_callback_register_oneshot(
547         &resource->lifetime_callback,
548         &delay,
549         globus_l_resource_lifetime_expired,
550         resource);
551
552 out:
553 15     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
554
555 15     return result;
556 }
557 /* globus_resource_set_destroy_time() */
558
559 /**
560  * Finish one reference and then destroy the resource.
561  * @ingroup resource
562  *
563  * This function acts identically to globus_resource_destroy() except it
564  * first finishes one reference to the resource. This allows applications to
565  * use code like this:
566  * @code
567  *     globus_resource_find(resource_id, &resource);
568  *     ...
569  *     globus_resource_finish_and_destroy(resource);
570  * @endcode
571  * to avoid a race condition between calling globus_resource_finish() and 
572  * globus_resource_destroy() during which the reference to the resource
573  * would be invalid.
574  * 
575  * If another thread is currently blocked trying to destroy this resource, then
576  * this function acts like #globus_resource_finish().
577  *
578  * @param resource
579  *     Resource handle which is no longer being used.
580  *
581  * @retval GLOBUS_SUCCESS
582  *     Successfully finished using the resource handle.
583  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
584  *     Null resource handle passed to this function.
585  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_ALREADY_DESTROYED
586  *     The resource has already been destroyed.
587  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NO_REFERENCES
588  *     No references to resource exist.
589  *
590  * @see #globus_resource_create(), #globus_resource_destroy(),
591  * #globus_resource_set_destroy_time()
592  */
593 globus_result_t
594 globus_resource_finish_and_destroy(
595     globus_resource_t                   resource)
596 38 {
597 38     globus_result_t                     result = GLOBUS_SUCCESS;
598 38     globus_bool_t                       active;
599 38     GlobusFuncName(globus_resource_finish_and_destroy);
600
601 38     if (resource == NULL)
602     {
603 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
604     }
605
606 38     globus_mutex_lock(&globus_l_wsrf_resource_lock);
607
608 38     if (resource->references <= 0)
609     {
610 0         result = GLOBUS_RESOURCE_ERROR_NO_REFERENCES(resource->id);
611
612 0         goto out;
613     }
614
615 38     resource->references--;
616
617 38     if (resource->destroy_blocking)
618     {
619         /* If another thread is calling destroy, this acts like
620          * globus_resource_finish()
621          */
622 0         goto out;
623     }
624
625 38     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
626     {
627         /* Try to cancel the lifetime callback */
628 12         result = globus_callback_unregister(
629             resource->lifetime_callback,
630             NULL,
631             NULL,
632             &active);
633
634 12         if (result != GLOBUS_SUCCESS || active)
635         {
636             /* Callback can't be stopped---we'll rely on it to destroy the
637              * service
638              */
639 12             goto out;
640         }
641 12         resource->lifetime_callback = GLOBUS_NULL_HANDLE;
642     }
643
644 38     resource->destroy_blocking = GLOBUS_TRUE;
645
646 38     while (resource->references > 0)
647     {
648 0         globus_cond_wait(&resource->cond, &globus_l_wsrf_resource_lock);
649     }
650 38     (void) globus_hashtable_remove(&globus_l_wsrf_resources,
651             resource->id);
652
653 38     globus_l_resource_destroy(resource);
654
655 out:
656 38     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
657
658 38     return result;
659 }
660 /* globus_resource_finish_and_destroy() */
661
662 /**
663  * Retrieve resource-specific data from a resource handle.
664  * @ingroup resource
665  *
666  * @param resource
667  *     Resource handle to query for data.
668  * @param data
669  *     Pointer to be set to the value of resource-specific data.
670  *
671  * @retval GLOBUS_SUCCESS
672  *     Resource specific data successfully retrieved.
673  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
674  *     Null parameter passed to this function. The value of @a data is
675  *     undefined when this value is returned.
676  *
677  * @see #globus_resource_set_resource_specific()
678  */
679 globus_result_t
680 globus_resource_get_resource_specific(
681     const globus_resource_t             resource,
682     void **                             data)
683 0 {
684 0     GlobusFuncName(globus_resource_get_resource_specific);
685
686 0     if (resource == NULL || data == NULL)
687     {
688 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
689     }
690 0     *data = resource->data;
691
692 0     return GLOBUS_SUCCESS;
693 }
694 /* globus_resource_get_resource_specific() */
695
696 /**
697  * Set resource-specific data for a resource handle.
698  * @ingroup resource
699  *
700  * @param resource
701  *     Resource handle to modify.
702  * @param data
703  *     New value of the resource-specific data. If a non-NULL value was
704  *     previously set as resource-specific data, it will be destroyed.
705  * @param destroy_func
706  *     Function pointer of a function to call to destroy the resource-specific
707  *     data. This will be called either when a new instance of
708  *     resource-specific data is set by calling this function again, or when
709  *     the resource is destroyed because of globus_resource_finish() being
710  *     called.
711  *
712  * @retval GLOBUS_SUCCESS
713  *     Resource specific data successfully set.
714  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
715  *     Null parameter passed to this function. 
716  *
717  * @see #globus_resource_get_resource_specific(), #globus_resource_finish()
718  */
719 globus_result_t
720 globus_resource_set_resource_specific(
721     globus_resource_t                   resource,
722     void *                              data,
723     globus_resource_destroy_t           destroy_func)
724 1 {
725 1     GlobusFuncName(globus_resource_set_resource_specific);
726
727 1     if (resource == NULL)
728     {
729 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
730     }
731
732 1     if ((resource->data != NULL) && (resource->data_destroy_func != NULL))
733     {
734 0         (resource->data_destroy_func)(resource->data);
735     }
736
737 1     resource->data = data;
738 1     resource->data_destroy_func = destroy_func;
739
740 1     return GLOBUS_SUCCESS;
741 }
742 /* globus_resource_set_resource_specific() */
743
744 /**
745  * Retrieve resource-specific type registry from a resource handle.
746  * @ingroup resource
747  *
748  * @param resource
749  *     Resource handle to query for data.
750  * @param registry
751  *     Pointer to be set to the value of type registry.
752  *
753  * @retval GLOBUS_SUCCESS
754  *     Type registry successfully retrieved.
755  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
756  *     Null parameter passed to this function. The value of @a registry is
757  *     undefined when this value is returned.
758  *
759  * @see #globus_resource_set_type_registry()
760  */
761 globus_result_t
762 globus_resource_get_type_registry(
763     globus_resource_t                   resource,
764     globus_xsd_type_registry_t *        registry)
765 0 {
766 0     GlobusFuncName(globus_resource_get_type_registry);
767
768 0     if (resource == NULL || registry == NULL)
769     {
770 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
771     }
772 0     *registry = resource->registry;
773
774 0     return GLOBUS_SUCCESS;
775 }
776 /* globus_resource_get_type_registry() */
777
778 /**
779  * Set type registry associated with a resource handle.
780  * @ingroup resource
781  *
782  * @param resource
783  *     Resource handle to modify.
784  * @param registry
785  *     New value for the resource's type registry.
786  *
787  * @retval GLOBUS_SUCCESS
788  *     Resource's type registry successfully set.
789  * @retval GLOBUS_WSRF_RESOURCE_ERROR_TYPE_NULL_PARAM
790  *     Null parameter passed to this function. 
791  *
792  * @see #globus_resource_get_type_registry()
793  */
794 globus_result_t
795 globus_resource_set_type_registry(
796     globus_resource_t                   resource,
797     globus_xsd_type_registry_t          registry)
798 0 {
799 0     GlobusFuncName(globus_resource_set_type_registry);
800
801 0     if (resource == NULL || registry == NULL)
802     {
803 0         return GLOBUS_RESOURCE_ERROR_NULL_PARAM();
804     }
805
806 0     resource->registry = registry;
807
808 0     return GLOBUS_SUCCESS;
809 }
810 /* globus_resource_set_type_registry() */
811
812 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
813 /**
814  * Destroy a resource stored in the resource hashtable at deactivation time.
815  * Resources could have 
816  * - references from finds which were not finished
817  * - other threads blocking in globus_resource_destroy()
818  * - other threads blocking in a callback from
819  *   globus_resource_set_destroy_time()
820  * This function is called with the resource mutex locked.
821  */
822 static
823 void
824 globus_l_wsrf_resource_hashtable_destroy(
825     void *                              datum)
826 1 {
827 1     globus_resource_t                   resource = datum;
828 1     globus_bool_t                       active = GLOBUS_FALSE;
829     
830     /* If a callback is registered, try to remove it; if that fails, then
831      * the callback code will try to destroy the resource, so we'll act
832      * later as if a destroy call was already blocking.
833      */
834 1     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
835     {
836 0         globus_callback_unregister(
837             resource->lifetime_callback,
838             NULL,
839             NULL,
840             &active);
841
842 0         resource->lifetime_callback = GLOBUS_NULL_HANDLE;
843     }
844
845 1     if (active || resource->destroy_blocking)
846     {
847         /* If destroy_blocking is true or a callback is waiting for the lock,
848          * then another thread is going to try to destroy this, or is waiting
849          * for the referece count to reach 0 already.  We'll help that out.
850          */
851 0         resource->references = 0;
852 0         globus_cond_signal(&resource->cond);
853
854 0         return;
855     }
856     else
857     {
858         /* Otherwise, no destroy is in progress for this resource, so we must
859          * free it ourselves.
860          */
861 1         globus_l_resource_destroy(resource);
862     }
863 }
864 /* globus_l_wsrf_resource_hashtable_destroy() */
865
866 /**
867  * Free memory associated with a resource handle.
868  * When this function is called for a resource, all callbacks and threads
869  * blocking on the condition of this resource must be finished, otherwise
870  * weird things may happen in those threads.
871  *
872  * This function is called with the mutex locked.
873  *
874  * @param datum
875  *     Pointer to the resource.
876  */
877 static
878 void
879 globus_l_resource_destroy(
880     globus_resource_t                   resource)
881 39 {
882
883 39     if (resource->resource_properties != NULL)
884     {
885 39         globus_hashtable_destroy_all(
886                 &resource->resource_properties,
887                 globus_l_resource_property_destroy);
888     }
889 39     globus_libc_free(resource->id);
890
891 39     if (resource->data && resource->data_destroy_func)
892     {
893 1         resource->data_destroy_func(resource->data);
894     }
895 39     globus_cond_destroy(&resource->cond);
896
897 39     if (resource->lifetime_callback != GLOBUS_NULL_HANDLE)
898     {
899 0         globus_callback_unregister(
900             resource->lifetime_callback,
901             NULL,
902             NULL,
903             NULL);
904     }
905
906 39     globus_libc_free(resource);
907 }
908 /* globus_l_resource_destroy() */
909
910 /**
911  * Free memory associated with a resource property.
912  *
913  * Function signature uses a void * to match the globus_hashtable_destroy_all()
914  * prototype.
915  * 
916  * @param datum
917  *     Pointer to a globus_resource_property_t to destroy.
918  */
919 static
920 void
921 globus_l_resource_property_destroy(
922     void *                              datum)
923 78 {
924 78     globus_resource_property_t *        prop = datum;
925
926 78     if (prop->property)
927     {
928 58         prop->type_info->destroy(prop->property);
929     }
930 78     if (prop->name.Namespace)
931     {
932 78         globus_libc_free(prop->name.Namespace);
933     }
934 78     if (prop->name.local)
935     {
936 78         globus_libc_free(prop->name.local);
937     }
938 78     globus_libc_free(prop);
939 }
940 /* globus_l_resource_property_destroy() */
941
942 static
943 void
944 globus_l_resource_lifetime_expired(
945     void *                              arg)
946 0 {
947 0     globus_resource_t                   resource = arg;
948
949 0     globus_mutex_lock(&globus_l_wsrf_resource_lock);
950
951 0     resource->destroy_blocking = GLOBUS_TRUE;
952
953 0     while (resource->references > 0)
954     {
955 0         globus_cond_wait(&resource->cond, &globus_l_wsrf_resource_lock);
956     }
957 0     (void) globus_hashtable_remove(&globus_l_wsrf_resources,
958             resource->id);
959
960 0     globus_l_resource_destroy(resource);
961 0     globus_mutex_unlock(&globus_l_wsrf_resource_lock);
962 }
963 /* globus_l_resource_lifetime_expired() */