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