c84f61bbae3c0d612a43e9ce67d3a6b3c299a883
[mirror/winof/.git] / core / complib / cl_obj.c
1 /*\r
2  * Copyright (c) 2005 SilverStorm Technologies.  All rights reserved.\r
3  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. \r
4  *\r
5  * This software is available to you under the OpenIB.org BSD license\r
6  * below:\r
7  *\r
8  *     Redistribution and use in source and binary forms, with or\r
9  *     without modification, are permitted provided that the following\r
10  *     conditions are met:\r
11  *\r
12  *      - Redistributions of source code must retain the above\r
13  *        copyright notice, this list of conditions and the following\r
14  *        disclaimer.\r
15  *\r
16  *      - Redistributions in binary form must reproduce the above\r
17  *        copyright notice, this list of conditions and the following\r
18  *        disclaimer in the documentation and/or other materials\r
19  *        provided with the distribution.\r
20  *\r
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
24  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
25  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
26  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
27  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
28  * SOFTWARE.\r
29  *\r
30  * $Id$\r
31  */\r
32 \r
33 \r
34 #include <complib/cl_obj.h>\r
35 #include <complib/cl_memory.h>\r
36 #include <complib/cl_debug.h>\r
37 \r
38 \r
39 /* Number of relation objects to add to the global pool when growing. */\r
40 #define CL_REL_POOL_SIZE        ( 4096 / sizeof( cl_obj_rel_t ) )\r
41 \r
42 \r
43 \r
44 /* The global object manager. */\r
45 cl_obj_mgr_t                            *gp_obj_mgr = NULL;\r
46 \r
47 \r
48 \r
49 /********************************************************************\r
50  * Global Object Manager\r
51  *******************************************************************/\r
52 \r
53 cl_status_t\r
54 cl_obj_mgr_create()\r
55 {\r
56         cl_status_t                     status;\r
57 \r
58         /* See if the object manager has already been created. */\r
59         if( gp_obj_mgr )\r
60                 return CL_SUCCESS;\r
61 \r
62         /* Allocate the object manager. */\r
63         gp_obj_mgr = cl_zalloc( sizeof( cl_obj_mgr_t ) );\r
64         if( !gp_obj_mgr )\r
65                 return CL_INSUFFICIENT_MEMORY;\r
66 \r
67         /* Construct the object manager. */\r
68         cl_qlist_init( &gp_obj_mgr->obj_list );\r
69         cl_spinlock_construct( &gp_obj_mgr->lock );\r
70         cl_async_proc_construct( &gp_obj_mgr->async_proc_mgr );\r
71         cl_qpool_construct( &gp_obj_mgr->rel_pool );\r
72 \r
73         /* Initialize the spinlock. */\r
74         status = cl_spinlock_init( &gp_obj_mgr->lock );\r
75         if( status != CL_SUCCESS )\r
76         {\r
77                 cl_obj_mgr_destroy();\r
78                 return status;\r
79         }\r
80 \r
81         /* Initialize the asynchronous processing manager. */\r
82         status = cl_async_proc_init( &gp_obj_mgr->async_proc_mgr, 0, "obj_mgr" );\r
83         if( status != CL_SUCCESS )\r
84         {\r
85                 cl_obj_mgr_destroy();\r
86                 return status;\r
87         }\r
88 \r
89         /* Initialize the relationship pool. */\r
90         status = cl_qpool_init( &gp_obj_mgr->rel_pool, 0, 0, CL_REL_POOL_SIZE,\r
91                 sizeof( cl_obj_rel_t ), NULL, NULL, gp_obj_mgr );\r
92         if( status != CL_SUCCESS )\r
93         {\r
94                 cl_obj_mgr_destroy();\r
95                 return status;\r
96         }\r
97 \r
98         return CL_SUCCESS;\r
99 }\r
100 \r
101 \r
102 \r
103 void\r
104 cl_obj_mgr_destroy()\r
105 {\r
106         cl_list_item_t                  *p_list_item;\r
107         cl_obj_t                                *p_obj;\r
108 \r
109         /* See if the object manager had been created. */\r
110         if( !gp_obj_mgr )\r
111                 return;\r
112 \r
113         /* Verify that all object's have been destroyed. */\r
114         for( p_list_item = cl_qlist_head( &gp_obj_mgr->obj_list );\r
115                  p_list_item != cl_qlist_end( &gp_obj_mgr->obj_list );\r
116                  p_list_item = cl_qlist_next( p_list_item ) )\r
117         {\r
118                 p_obj = PARENT_STRUCT( p_list_item, cl_obj_t, pool_item );\r
119 #if defined( _DEBUG_ )\r
120                         cl_dbg_out( "object not destroyed %p(%i), ref_cnt: %d\n",\r
121                                 p_obj, p_obj->type, p_obj->ref_cnt );\r
122 #endif\r
123         }\r
124 \r
125         /* Destroy all object manager resources. */\r
126         cl_spinlock_destroy( &gp_obj_mgr->lock );\r
127         cl_async_proc_destroy( &gp_obj_mgr->async_proc_mgr );\r
128         cl_qpool_destroy( &gp_obj_mgr->rel_pool );\r
129 \r
130         /* Free the object manager and clear the global pointer. */\r
131         cl_free( gp_obj_mgr );\r
132         gp_obj_mgr = NULL;\r
133 }\r
134 \r
135 \r
136 \r
137 /*\r
138  * Get an item to track object relationships.\r
139  */\r
140 cl_obj_rel_t*\r
141 cl_rel_alloc()\r
142 {\r
143         cl_obj_rel_t    *p_rel;\r
144 \r
145         CL_ASSERT( gp_obj_mgr );\r
146 \r
147         cl_spinlock_acquire( &gp_obj_mgr->lock );\r
148         p_rel = (cl_obj_rel_t*)cl_qpool_get( &gp_obj_mgr->rel_pool );\r
149         cl_spinlock_release( &gp_obj_mgr->lock );\r
150 \r
151         return p_rel;\r
152 }\r
153 \r
154 \r
155 \r
156 /*\r
157  * Return an item used to track relationships back to the pool.\r
158  */\r
159 void\r
160 cl_rel_free(\r
161         IN                              cl_obj_rel_t * const            p_rel )\r
162 {\r
163         CL_ASSERT( gp_obj_mgr && p_rel );\r
164 \r
165         cl_spinlock_acquire( &gp_obj_mgr->lock );\r
166         cl_qpool_put( &gp_obj_mgr->rel_pool, &p_rel->pool_item );\r
167         cl_spinlock_release( &gp_obj_mgr->lock );\r
168 }\r
169 \r
170 \r
171 \r
172 /*\r
173  * Insert an object into the global object manager's list.\r
174  */\r
175 static void\r
176 __track_obj(\r
177         IN                              cl_obj_t                                        *p_obj )\r
178 {\r
179         CL_ASSERT( gp_obj_mgr && p_obj );\r
180 \r
181         cl_spinlock_acquire( &gp_obj_mgr->lock );\r
182         cl_qlist_insert_tail( &gp_obj_mgr->obj_list,\r
183                 (cl_list_item_t*)&p_obj->pool_item );\r
184         cl_spinlock_release( &gp_obj_mgr->lock );\r
185 }\r
186 \r
187 \r
188 \r
189 /*\r
190  * Remove an object from the global object manager's list.\r
191  */\r
192 static void\r
193 __remove_obj(\r
194         IN                              cl_obj_t                                        *p_obj )\r
195 {\r
196         CL_ASSERT( gp_obj_mgr && p_obj );\r
197 \r
198         cl_spinlock_acquire( &gp_obj_mgr->lock );\r
199         cl_qlist_remove_item( &gp_obj_mgr->obj_list,\r
200                 (cl_list_item_t*)&p_obj->pool_item );\r
201         cl_spinlock_release( &gp_obj_mgr->lock );\r
202 }\r
203 \r
204 \r
205 \r
206 /********************************************************************\r
207  * Generic Object Class\r
208  *******************************************************************/\r
209 \r
210 /* Function prototypes. */\r
211 static void\r
212 __destroy_obj(\r
213         IN                              cl_obj_t                                        *p_obj );\r
214 \r
215 static void\r
216 __destroy_cb(\r
217         IN                              cl_async_proc_item_t            *p_item );\r
218 \r
219 /* Sets the state of an object and returns the old state. */\r
220 static cl_state_t\r
221 __obj_set_state(\r
222         IN                              cl_obj_t * const                        p_obj,\r
223         IN              const   cl_state_t                                      new_state );\r
224 \r
225 \r
226 \r
227 \r
228 void\r
229 cl_obj_construct(\r
230         IN                              cl_obj_t * const                        p_obj,\r
231         IN              const   uint32_t                                        obj_type )\r
232 {\r
233         CL_ASSERT( p_obj );\r
234         cl_memclr( p_obj, sizeof( cl_obj_t ) );\r
235 \r
236         cl_spinlock_construct( &p_obj->lock );\r
237         p_obj->state = CL_UNINITIALIZED;\r
238         p_obj->type = obj_type;\r
239         cl_event_construct( &p_obj->event );\r
240 \r
241         cl_qlist_init( &p_obj->parent_list );\r
242         cl_qlist_init( &p_obj->child_list );\r
243 \r
244         /* Insert the object into the global tracking list. */\r
245         __track_obj( p_obj );\r
246 }\r
247 \r
248 \r
249 \r
250 cl_status_t\r
251 cl_obj_init(\r
252         IN                              cl_obj_t * const                        p_obj,\r
253         IN                              cl_destroy_type_t                       destroy_type,\r
254         IN              const   cl_pfn_obj_call_t                       pfn_destroying OPTIONAL,\r
255         IN              const   cl_pfn_obj_call_t                       pfn_cleanup OPTIONAL,\r
256         IN              const   cl_pfn_obj_call_t                       pfn_free )\r
257 {\r
258         cl_status_t                             status;\r
259 \r
260         CL_ASSERT( p_obj && pfn_free );\r
261         CL_ASSERT( p_obj->state == CL_UNINITIALIZED );\r
262 \r
263         /* The object references itself until it is destroyed. */\r
264         p_obj->ref_cnt = 1;\r
265 \r
266         /* Record destruction callbacks. */\r
267         p_obj->pfn_destroying = pfn_destroying;\r
268         p_obj->pfn_cleanup = pfn_cleanup;\r
269         p_obj->pfn_free = pfn_free;\r
270 \r
271         /* Set the destroy function pointer based on the destruction type. */\r
272         p_obj->destroy_type = destroy_type;\r
273         p_obj->async_item.pfn_callback = __destroy_cb;\r
274 \r
275         /* Initialize the spinlock. */\r
276         status = cl_spinlock_init( &p_obj->lock );\r
277         if( status != CL_SUCCESS )\r
278                 return status;\r
279 \r
280         /* Initialize the synchronous cleanup event. */\r
281         status = cl_event_init( &p_obj->event, FALSE );\r
282         if( status != CL_SUCCESS )\r
283                 return status;\r
284 \r
285         p_obj->state = CL_INITIALIZED;\r
286 \r
287         return CL_SUCCESS;\r
288 }\r
289 \r
290 \r
291 \r
292 void\r
293 cl_obj_destroy(\r
294         IN                              cl_obj_t *                                      p_obj )\r
295 {\r
296         cl_state_t              old_state;\r
297 \r
298         CL_ASSERT( p_obj );\r
299 \r
300         /* Mark that we're destroying the object. */\r
301         old_state = __obj_set_state( p_obj, CL_DESTROYING );\r
302 \r
303         /*\r
304          * Only a single thread can actually destroy the object.  Multiple\r
305          * threads can initiate destruction as long as the callers can ensure\r
306          * their object reference is valid.\r
307          */\r
308         if( old_state == CL_DESTROYING )\r
309                 return;\r
310 \r
311         /* Destroy the object. */\r
312         __destroy_obj( p_obj );\r
313 }\r
314 \r
315 \r
316 \r
317 void\r
318 cl_obj_reset(\r
319         IN                              cl_obj_t * const                        p_obj )\r
320 {\r
321         CL_ASSERT( p_obj );\r
322         CL_ASSERT( p_obj->ref_cnt == 0 );\r
323         CL_ASSERT( p_obj->state == CL_DESTROYING );\r
324 \r
325         p_obj->ref_cnt = 1;\r
326         p_obj->state = CL_INITIALIZED;\r
327 \r
328         cl_qlist_remove_all( &p_obj->parent_list );\r
329         cl_qlist_remove_all( &p_obj->child_list );\r
330 }\r
331 \r
332 \r
333 \r
334 static cl_state_t\r
335 __obj_set_state(\r
336         IN                              cl_obj_t * const                        p_obj,\r
337         IN              const   cl_state_t                                      new_state )\r
338 {\r
339         cl_state_t              old_state;\r
340 \r
341         cl_spinlock_acquire( &p_obj->lock );\r
342         old_state = p_obj->state;\r
343         p_obj->state = new_state;\r
344         cl_spinlock_release( &p_obj->lock );\r
345 \r
346         return old_state;\r
347 }\r
348 \r
349 \r
350 \r
351 /*\r
352  * Add a dependent relationship between two objects.\r
353  */\r
354 void\r
355 cl_obj_insert_rel(\r
356         IN                              cl_obj_rel_t * const            p_rel,\r
357         IN                              cl_obj_t * const                        p_parent_obj,\r
358         IN                              cl_obj_t * const                        p_child_obj )\r
359 {\r
360         CL_ASSERT( p_rel && p_parent_obj && p_child_obj );\r
361 \r
362         cl_spinlock_acquire( &p_parent_obj->lock );\r
363         cl_obj_insert_rel_parent_locked( p_rel, p_parent_obj, p_child_obj );\r
364         cl_spinlock_release( &p_parent_obj->lock );\r
365 }\r
366 \r
367 \r
368 \r
369 /*\r
370  * Add a dependent relationship between two objects.\r
371  */\r
372 void\r
373 cl_obj_insert_rel_parent_locked(\r
374         IN                              cl_obj_rel_t * const            p_rel,\r
375         IN                              cl_obj_t * const                        p_parent_obj,\r
376         IN                              cl_obj_t * const                        p_child_obj )\r
377 {\r
378         CL_ASSERT( p_rel && p_parent_obj && p_child_obj );\r
379 \r
380         /* The child object needs to maintain a reference on the parent. */\r
381         cl_obj_ref( p_parent_obj );\r
382         cl_obj_ref( p_child_obj );\r
383 \r
384         /* Save the relationship details. */\r
385         p_rel->p_child_obj = p_child_obj;\r
386         p_rel->p_parent_obj = p_parent_obj;\r
387 \r
388         /*\r
389          * Track the object - hold both locks to ensure that the relationship is\r
390          * viewable in the child and parent lists at the same time.\r
391          */\r
392         cl_spinlock_acquire( &p_child_obj->lock );\r
393 \r
394         cl_qlist_insert_tail( &p_child_obj->parent_list, &p_rel->list_item );\r
395         cl_qlist_insert_tail( &p_parent_obj->child_list,\r
396                 (cl_list_item_t*)&p_rel->pool_item );\r
397 \r
398         cl_spinlock_release( &p_child_obj->lock );\r
399 }\r
400 \r
401 \r
402 \r
403 /*\r
404  * Remove an existing relationship.\r
405  */\r
406 void\r
407 cl_obj_remove_rel(\r
408         IN                              cl_obj_rel_t * const            p_rel )\r
409 {\r
410         cl_obj_t                *p_child_obj;\r
411         cl_obj_t                *p_parent_obj;\r
412 \r
413         CL_ASSERT( p_rel );\r
414         CL_ASSERT( p_rel->p_child_obj && p_rel->p_parent_obj );\r
415 \r
416         p_child_obj = p_rel->p_child_obj;\r
417         p_parent_obj = p_rel->p_parent_obj;\r
418 \r
419         /*\r
420          * Release the objects - hold both locks to ensure that the relationship is\r
421          * removed from the child and parent lists at the same time.\r
422          */\r
423         cl_spinlock_acquire( &p_parent_obj->lock );\r
424         cl_spinlock_acquire( &p_child_obj->lock );\r
425 \r
426         cl_qlist_remove_item( &p_child_obj->parent_list, &p_rel->list_item );\r
427         cl_qlist_remove_item( &p_parent_obj->child_list,\r
428                 (cl_list_item_t*)&p_rel->pool_item );\r
429 \r
430         cl_spinlock_release( &p_child_obj->lock );\r
431         cl_spinlock_release( &p_parent_obj->lock );\r
432 \r
433         /* Dereference the objects. */\r
434         cl_obj_deref( p_parent_obj );\r
435         cl_obj_deref( p_child_obj );\r
436 \r
437         p_rel->p_child_obj = NULL;\r
438         p_rel->p_parent_obj = NULL;\r
439 }\r
440 \r
441 \r
442 \r
443 /*\r
444  * Increment a reference count on an object.\r
445  */\r
446 int32_t\r
447 cl_obj_ref(\r
448         IN                              cl_obj_t * const                        p_obj )\r
449 {\r
450         CL_ASSERT( p_obj );\r
451 \r
452         /*\r
453          * We need to allow referencing the object during destruction in order\r
454          * to properly synchronize destruction between parent and child objects.\r
455          */\r
456         CL_ASSERT( p_obj->state == CL_INITIALIZED ||\r
457                 p_obj->state == CL_DESTROYING );\r
458 \r
459         return cl_atomic_inc( &p_obj->ref_cnt );\r
460 }\r
461 \r
462 \r
463 \r
464 /*\r
465  * Decrement the reference count on an AL object.  Destroy the object if\r
466  * it is no longer referenced.  This object should not be an object's parent.\r
467  */\r
468 int32_t\r
469 cl_obj_deref(\r
470         IN                              cl_obj_t * const                        p_obj )\r
471 {\r
472         int32_t                 ref_cnt;\r
473 \r
474         CL_ASSERT( p_obj );\r
475         CL_ASSERT( p_obj->state == CL_INITIALIZED ||\r
476                 p_obj->state == CL_DESTROYING );\r
477 \r
478         ref_cnt = cl_atomic_dec( &p_obj->ref_cnt );\r
479 \r
480         /* If the reference count went to 0, the object should be destroyed. */\r
481         if( ref_cnt == 0 )\r
482         {\r
483                 if( p_obj->destroy_type == CL_DESTROY_ASYNC )\r
484                 {\r
485                         /* Queue the object for asynchronous destruction. */\r
486                         CL_ASSERT( gp_obj_mgr );\r
487                         cl_async_proc_queue( &gp_obj_mgr->async_proc_mgr,\r
488                                 &p_obj->async_item );\r
489                 }\r
490                 else\r
491                 {\r
492                         /* Signal an event for synchronous destruction. */\r
493                         cl_event_signal( &p_obj->event );\r
494                 }\r
495         }\r
496 \r
497         return ref_cnt;\r
498 }\r
499 \r
500 \r
501 \r
502 /*\r
503  * Called to cleanup all resources allocated by an object.\r
504  */\r
505 void\r
506 cl_obj_deinit(\r
507         IN                              cl_obj_t * const                        p_obj )\r
508 {\r
509         CL_ASSERT( p_obj );\r
510         CL_ASSERT( p_obj->state == CL_UNINITIALIZED ||\r
511                 p_obj->state == CL_DESTROYING );\r
512 #if defined( _DEBUG_ )\r
513         {\r
514                 cl_list_item_t  *p_list_item;\r
515                 cl_obj_rel_t    *p_rel;\r
516 \r
517                 /*\r
518                  * Check that we didn't leave any list items in the parent list\r
519                  * that came from the global pool.  Ignore list items allocated by\r
520                  * the user to simplify their usage model.\r
521                  */\r
522                 for( p_list_item = cl_qlist_head( &p_obj->parent_list );\r
523                          p_list_item != cl_qlist_end( &p_obj->parent_list );\r
524                          p_list_item = cl_qlist_next( p_list_item ) )\r
525                 {\r
526                         p_rel = (cl_obj_rel_t*)PARENT_STRUCT( p_list_item,\r
527                                 cl_obj_rel_t, list_item );\r
528                         CL_ASSERT( p_rel->pool_item.p_pool !=\r
529                                 &gp_obj_mgr->rel_pool.qcpool );\r
530                 }\r
531         }\r
532 #endif\r
533         CL_ASSERT( cl_is_qlist_empty( &p_obj->child_list ) );\r
534 \r
535         /* Remove the object from the global tracking list. */\r
536         __remove_obj( p_obj );\r
537 \r
538         cl_event_destroy( &p_obj->event );\r
539         cl_spinlock_destroy( &p_obj->lock );\r
540 \r
541         /* Mark the object as destroyed for debugging purposes. */\r
542         p_obj->state = CL_DESTROYED;\r
543 }\r
544 \r
545 \r
546 \r
547 /*\r
548  * Remove the given object from its relationships with all its parents.\r
549  * This call requires synchronization to the given object.\r
550  */\r
551 static void\r
552 __remove_parent_rel(\r
553         IN                              cl_obj_t * const                        p_obj )\r
554 {\r
555         cl_list_item_t          *p_list_item;\r
556         cl_obj_rel_t            *p_rel;\r
557 \r
558         /* Remove this child object from all its parents. */\r
559         for( p_list_item = cl_qlist_tail( &p_obj->parent_list );\r
560                  p_list_item != cl_qlist_end( &p_obj->parent_list );\r
561                  p_list_item = cl_qlist_prev( p_list_item ) )\r
562         {\r
563                 p_rel = (cl_obj_rel_t*)PARENT_STRUCT( p_list_item,\r
564                         cl_obj_rel_t, list_item );\r
565 \r
566                 /*\r
567                  * Remove the child from the parent's list, but do not dereference\r
568                  * the parent.  This lets the user access the parent in the callback\r
569                  * routines, but allows destruction to proceed.\r
570                  */\r
571                 cl_spinlock_acquire( &p_rel->p_parent_obj->lock );\r
572                 cl_qlist_remove_item( &p_rel->p_parent_obj->child_list,\r
573                         (cl_list_item_t*)&p_rel->pool_item );\r
574 \r
575                 /*\r
576                  * Remove the relationship's reference to the child.  Use an atomic\r
577                  * decrement rather than cl_obj_deref, since we're already holding the\r
578                  * child object's lock.\r
579                  */\r
580                 cl_atomic_dec( &p_obj->ref_cnt );\r
581                 CL_ASSERT( p_obj->ref_cnt > 0 );\r
582 \r
583                 cl_spinlock_release( &p_rel->p_parent_obj->lock );\r
584 \r
585                 /*\r
586                  * Mark that the child is no longer related to the parent.  We still\r
587                  * hold a reference on the parent object, so we don't clear the parent\r
588                  * pointer until that reference is released.\r
589                  */\r
590                 p_rel->p_child_obj = NULL;\r
591         }\r
592 }\r
593 \r
594 \r
595 \r
596 static void\r
597 __destroy_child_obj(\r
598         IN                              cl_obj_t *                                      p_obj )\r
599 {\r
600         cl_list_item_t                  *p_list_item;\r
601         cl_obj_rel_t                    *p_rel;\r
602         cl_obj_t                                *p_child_obj;\r
603         cl_state_t                              old_state;\r
604 \r
605         /*      Destroy all child objects. */\r
606         cl_spinlock_acquire( &p_obj->lock );\r
607         for( p_list_item = cl_qlist_tail( &p_obj->child_list );\r
608                  p_list_item != cl_qlist_end( &p_obj->child_list );\r
609                  p_list_item = cl_qlist_tail( &p_obj->child_list ) )\r
610         {\r
611                 p_rel = (cl_obj_rel_t*)PARENT_STRUCT( p_list_item,\r
612                         cl_obj_rel_t, pool_item );\r
613 \r
614                 /*\r
615                  * Take a reference on the child to protect against another parent\r
616                  * of the object destroying it while we are trying to access it.\r
617                  * If the child object is being destroyed, it will try to remove\r
618                  * this relationship from this parent.\r
619                  */\r
620                 p_child_obj = p_rel->p_child_obj;\r
621                 cl_obj_ref( p_child_obj );\r
622 \r
623                 /*\r
624                  * We cannot hold the parent lock when acquiring the child's lock, or\r
625                  * a deadlock can occur if the child is in the process of destroying\r
626                  * itself and its parent relationships.\r
627                  */\r
628                 cl_spinlock_release( &p_obj->lock );\r
629 \r
630                 /*\r
631                  * Mark that we wish to destroy the object.  If the old state indicates\r
632                  * that we should destroy the object, continue with the destruction.\r
633                  * Note that there is a reference held on the child object from its\r
634                  * creation.  We no longer need the prior reference taken above.\r
635                  */\r
636                 old_state = __obj_set_state( p_child_obj, CL_DESTROYING );\r
637                 cl_obj_deref( p_child_obj );\r
638 \r
639                 if( old_state != CL_DESTROYING )\r
640                         __destroy_obj( p_child_obj );\r
641 \r
642                 /* Continue processing the relationship list. */\r
643                 cl_spinlock_acquire( &p_obj->lock );\r
644         }\r
645         cl_spinlock_release( &p_obj->lock );\r
646 }\r
647 \r
648 \r
649 \r
650 /*\r
651  * Destroys an object.  This call returns TRUE if the destruction process\r
652  * should proceed, or FALSE if destruction is already in progress.\r
653  */\r
654 static void\r
655 __destroy_obj(\r
656         IN                              cl_obj_t                                        *p_obj )\r
657 {\r
658         uint32_t                        ref_cnt;\r
659         cl_destroy_type_t       destroy_type;\r
660 \r
661         CL_ASSERT( p_obj );\r
662         CL_ASSERT( p_obj->state == CL_DESTROYING );\r
663 \r
664          /* Remove this child object from all its parents. */\r
665         __remove_parent_rel( p_obj );\r
666 \r
667         /* Notify the user that the object is being destroyed. */\r
668         if( p_obj->pfn_destroying )\r
669                 p_obj->pfn_destroying( p_obj );\r
670 \r
671         /*      Destroy all child objects. */\r
672         __destroy_child_obj( p_obj );\r
673 \r
674         /*\r
675          * Cache the destroy_type because the object could be freed by the time\r
676          * cl_obj_deref below returns.\r
677          */\r
678         destroy_type = p_obj->destroy_type;\r
679 \r
680         /* Dereference this object as it is being destroyed. */\r
681         ref_cnt = cl_obj_deref( p_obj );\r
682 \r
683         if( destroy_type == CL_DESTROY_SYNC )\r
684         {\r
685                 if( ref_cnt )\r
686                 {\r
687                         /* Wait for all other references to go away. */\r
688 #if DBG\r
689                         /*\r
690                          * In debug builds, we assert every 10 seconds - a synchronous\r
691                          * destruction should not take that long.\r
692                          */\r
693                         while( cl_event_wait_on( &p_obj->event, 10000000, FALSE ) ==\r
694                                 CL_TIMEOUT )\r
695                         {\r
696                                 CL_ASSERT( !p_obj->ref_cnt );\r
697                         }\r
698 #else   /* DBG */\r
699                         cl_event_wait_on( &p_obj->event, EVENT_NO_TIMEOUT, FALSE );\r
700 #endif  /* DBG */\r
701                 }\r
702                 __destroy_cb( &p_obj->async_item );\r
703         }\r
704 }\r
705 \r
706 \r
707 \r
708 /*\r
709  * Dereference all parents the object was related to.\r
710  */\r
711 static cl_obj_t*\r
712 __deref_parents(\r
713         IN                              cl_obj_t * const                        p_obj )\r
714 {\r
715         cl_list_item_t          *p_list_item;\r
716         cl_obj_rel_t            *p_rel;\r
717         cl_obj_t                        *p_parent_obj;\r
718 \r
719         /* Destruction of the object is already serialized - no need to lock. */\r
720 \r
721         /*\r
722          * Dereference all parents.  Keep the relationship items in the child's\r
723          * list, so that they can be returned to the user through the free callback.\r
724          */\r
725         for( p_list_item = cl_qlist_head( &p_obj->parent_list );\r
726                  p_list_item != cl_qlist_end( &p_obj->parent_list );\r
727                  p_list_item = cl_qlist_next( p_list_item ) )\r
728         {\r
729                 p_rel = (cl_obj_rel_t*)PARENT_STRUCT( p_list_item,\r
730                         cl_obj_rel_t, list_item );\r
731 \r
732                 p_parent_obj = p_rel->p_parent_obj;\r
733                 p_rel->p_parent_obj = NULL;\r
734                 CL_ASSERT( !p_rel->p_child_obj );\r
735                 if( cl_qlist_next( p_list_item ) ==\r
736                         cl_qlist_end( &p_obj->parent_list ) )\r
737                 {\r
738                         /* Last parent - don't dereference it until after the "free" cb. */\r
739                         return p_parent_obj;\r
740                 }\r
741                 else\r
742                 {\r
743                         cl_obj_deref( p_parent_obj );\r
744                 }\r
745         }\r
746         return NULL;\r
747 }\r
748 \r
749 \r
750 \r
751 static void\r
752 __destroy_cb(\r
753         IN                              cl_async_proc_item_t            *p_item )\r
754 {\r
755         cl_obj_t                                *p_obj, *p_last_parent;\r
756 \r
757         CL_ASSERT( p_item );\r
758 \r
759         p_obj = PARENT_STRUCT( p_item, cl_obj_t, async_item );\r
760         CL_ASSERT( !p_obj->ref_cnt );\r
761         CL_ASSERT( p_obj->state == CL_DESTROYING );\r
762 \r
763         /* Cleanup any hardware related resources. */\r
764         if( p_obj->pfn_cleanup )\r
765                 p_obj->pfn_cleanup( p_obj );\r
766 \r
767         /* We can now safely dereference all but the last parent. */\r
768         p_last_parent = __deref_parents( p_obj );\r
769 \r
770         /* Free the resources associated with the object. */\r
771         CL_ASSERT( p_obj->pfn_free );\r
772         p_obj->pfn_free( p_obj );\r
773 \r
774         if( p_last_parent )\r
775                 cl_obj_deref( p_last_parent );\r
776 }\r