winverbs: process connect and accept asynchronously
[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 cl_status_t\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_status_t     status;\r
361         CL_ASSERT( p_rel && p_parent_obj && p_child_obj );\r
362 \r
363         cl_spinlock_acquire( &p_parent_obj->lock );\r
364         status = cl_obj_insert_rel_parent_locked( p_rel, p_parent_obj, p_child_obj );\r
365         cl_spinlock_release( &p_parent_obj->lock );\r
366         return status;\r
367 }\r
368 \r
369 \r
370 \r
371 /*\r
372  * Add a dependent relationship between two objects.\r
373  */\r
374 cl_status_t\r
375 cl_obj_insert_rel_parent_locked(\r
376         IN                              cl_obj_rel_t * const            p_rel,\r
377         IN                              cl_obj_t * const                        p_parent_obj,\r
378         IN                              cl_obj_t * const                        p_child_obj )\r
379 {\r
380         CL_ASSERT( p_rel && p_parent_obj && p_child_obj );\r
381 \r
382         if(p_parent_obj->state != CL_INITIALIZED)\r
383                 return CL_INVALID_STATE;\r
384         /* The child object needs to maintain a reference on the parent. */\r
385         cl_obj_ref( p_parent_obj );\r
386         cl_obj_ref( p_child_obj );\r
387 \r
388         /* Save the relationship details. */\r
389         p_rel->p_child_obj = p_child_obj;\r
390         p_rel->p_parent_obj = p_parent_obj;\r
391 \r
392         /*\r
393          * Track the object - hold both locks to ensure that the relationship is\r
394          * viewable in the child and parent lists at the same time.\r
395          */\r
396         cl_spinlock_acquire( &p_child_obj->lock );\r
397 \r
398         cl_qlist_insert_tail( &p_child_obj->parent_list, &p_rel->list_item );\r
399         cl_qlist_insert_tail( &p_parent_obj->child_list,\r
400                 (cl_list_item_t*)&p_rel->pool_item );\r
401 \r
402         cl_spinlock_release( &p_child_obj->lock );\r
403         return CL_SUCCESS;\r
404 }\r
405 \r
406 \r
407 \r
408 /*\r
409  * Remove an existing relationship.\r
410  */\r
411 void\r
412 cl_obj_remove_rel(\r
413         IN                              cl_obj_rel_t * const            p_rel )\r
414 {\r
415         cl_obj_t                *p_child_obj;\r
416         cl_obj_t                *p_parent_obj;\r
417 \r
418         CL_ASSERT( p_rel );\r
419         CL_ASSERT( p_rel->p_child_obj && p_rel->p_parent_obj );\r
420 \r
421         p_child_obj = p_rel->p_child_obj;\r
422         p_parent_obj = p_rel->p_parent_obj;\r
423 \r
424         /*\r
425          * Release the objects - hold both locks to ensure that the relationship is\r
426          * removed from the child and parent lists at the same time.\r
427          */\r
428         cl_spinlock_acquire( &p_parent_obj->lock );\r
429         cl_spinlock_acquire( &p_child_obj->lock );\r
430 \r
431         cl_qlist_remove_item( &p_child_obj->parent_list, &p_rel->list_item );\r
432         cl_qlist_remove_item( &p_parent_obj->child_list,\r
433                 (cl_list_item_t*)&p_rel->pool_item );\r
434 \r
435         cl_spinlock_release( &p_child_obj->lock );\r
436         cl_spinlock_release( &p_parent_obj->lock );\r
437 \r
438         /* Dereference the objects. */\r
439         cl_obj_deref( p_parent_obj );\r
440         cl_obj_deref( p_child_obj );\r
441 \r
442         p_rel->p_child_obj = NULL;\r
443         p_rel->p_parent_obj = NULL;\r
444 }\r
445 \r
446 \r
447 \r
448 /*\r
449  * Increment a reference count on an object.\r
450  */\r
451 int32_t\r
452 cl_obj_ref(\r
453         IN                              cl_obj_t * const                        p_obj )\r
454 {\r
455         CL_ASSERT( p_obj );\r
456 \r
457         /*\r
458          * We need to allow referencing the object during destruction in order\r
459          * to properly synchronize destruction between parent and child objects.\r
460          */\r
461         CL_ASSERT( p_obj->state == CL_INITIALIZED ||\r
462                 p_obj->state == CL_DESTROYING );\r
463 \r
464         return cl_atomic_inc( &p_obj->ref_cnt );\r
465 }\r
466 \r
467 \r
468 \r
469 /*\r
470  * Decrement the reference count on an AL object.  Destroy the object if\r
471  * it is no longer referenced.  This object should not be an object's parent.\r
472  */\r
473 int32_t\r
474 cl_obj_deref(\r
475         IN                              cl_obj_t * const                        p_obj )\r
476 {\r
477         int32_t                 ref_cnt;\r
478 \r
479         CL_ASSERT( p_obj );\r
480         CL_ASSERT( p_obj->state == CL_INITIALIZED ||\r
481                 p_obj->state == CL_DESTROYING );\r
482 \r
483         ref_cnt = cl_atomic_dec( &p_obj->ref_cnt );\r
484 \r
485         /* If the reference count went to 0, the object should be destroyed. */\r
486         if( ref_cnt == 0 )\r
487         {\r
488                 if( p_obj->destroy_type == CL_DESTROY_ASYNC )\r
489                 {\r
490                         /* Queue the object for asynchronous destruction. */\r
491                         CL_ASSERT( gp_obj_mgr );\r
492                         cl_async_proc_queue( &gp_obj_mgr->async_proc_mgr,\r
493                                 &p_obj->async_item );\r
494                 }\r
495                 else\r
496                 {\r
497                         /* Signal an event for synchronous destruction. */\r
498                         cl_event_signal( &p_obj->event );\r
499                 }\r
500         }\r
501 \r
502         return ref_cnt;\r
503 }\r
504 \r
505 \r
506 \r
507 /*\r
508  * Called to cleanup all resources allocated by an object.\r
509  */\r
510 void\r
511 cl_obj_deinit(\r
512         IN                              cl_obj_t * const                        p_obj )\r
513 {\r
514         CL_ASSERT( p_obj );\r
515         CL_ASSERT( p_obj->state == CL_UNINITIALIZED ||\r
516                 p_obj->state == CL_DESTROYING );\r
517 #if defined( _DEBUG_ )\r
518         {\r
519                 cl_list_item_t  *p_list_item;\r
520                 cl_obj_rel_t    *p_rel;\r
521 \r
522                 /*\r
523                  * Check that we didn't leave any list items in the parent list\r
524                  * that came from the global pool.  Ignore list items allocated by\r
525                  * the user to simplify their usage model.\r
526                  */\r
527                 for( p_list_item = cl_qlist_head( &p_obj->parent_list );\r
528                          p_list_item != cl_qlist_end( &p_obj->parent_list );\r
529                          p_list_item = cl_qlist_next( p_list_item ) )\r
530                 {\r
531                         p_rel = (cl_obj_rel_t*)PARENT_STRUCT( p_list_item,\r
532                                 cl_obj_rel_t, list_item );\r
533                         CL_ASSERT( p_rel->pool_item.p_pool !=\r
534                                 &gp_obj_mgr->rel_pool.qcpool );\r
535                 }\r
536         }\r
537 #endif\r
538         CL_ASSERT( cl_is_qlist_empty( &p_obj->child_list ) );\r
539 \r
540         /* Remove the object from the global tracking list. */\r
541         __remove_obj( p_obj );\r
542 \r
543         cl_event_destroy( &p_obj->event );\r
544         cl_spinlock_destroy( &p_obj->lock );\r
545 \r
546         /* Mark the object as destroyed for debugging purposes. */\r
547         p_obj->state = CL_DESTROYED;\r
548 }\r
549 \r
550 \r
551 \r
552 /*\r
553  * Remove the given object from its relationships with all its parents.\r
554  * This call requires synchronization to the given object.\r
555  */\r
556 static void\r
557 __remove_parent_rel(\r
558         IN                              cl_obj_t * const                        p_obj )\r
559 {\r
560         cl_list_item_t          *p_list_item;\r
561         cl_obj_rel_t            *p_rel;\r
562 \r
563         /* Remove this child object from all its parents. */\r
564         for( p_list_item = cl_qlist_tail( &p_obj->parent_list );\r
565                  p_list_item != cl_qlist_end( &p_obj->parent_list );\r
566                  p_list_item = cl_qlist_prev( p_list_item ) )\r
567         {\r
568                 p_rel = (cl_obj_rel_t*)PARENT_STRUCT( p_list_item,\r
569                         cl_obj_rel_t, list_item );\r
570 \r
571                 /*\r
572                  * Remove the child from the parent's list, but do not dereference\r
573                  * the parent.  This lets the user access the parent in the callback\r
574                  * routines, but allows destruction to proceed.\r
575                  */\r
576                 cl_spinlock_acquire( &p_rel->p_parent_obj->lock );\r
577                 cl_qlist_remove_item( &p_rel->p_parent_obj->child_list,\r
578                         (cl_list_item_t*)&p_rel->pool_item );\r
579 \r
580                 /*\r
581                  * Remove the relationship's reference to the child.  Use an atomic\r
582                  * decrement rather than cl_obj_deref, since we're already holding the\r
583                  * child object's lock.\r
584                  */\r
585                 cl_atomic_dec( &p_obj->ref_cnt );\r
586                 CL_ASSERT( p_obj->ref_cnt > 0 );\r
587 \r
588                 cl_spinlock_release( &p_rel->p_parent_obj->lock );\r
589 \r
590                 /*\r
591                  * Mark that the child is no longer related to the parent.  We still\r
592                  * hold a reference on the parent object, so we don't clear the parent\r
593                  * pointer until that reference is released.\r
594                  */\r
595                 p_rel->p_child_obj = NULL;\r
596         }\r
597 }\r
598 \r
599 \r
600 \r
601 static void\r
602 __destroy_child_obj(\r
603         IN                              cl_obj_t *                                      p_obj )\r
604 {\r
605         cl_list_item_t                  *p_list_item;\r
606         cl_obj_rel_t                    *p_rel;\r
607         cl_obj_t                                *p_child_obj;\r
608         cl_state_t                              old_state;\r
609 \r
610         /*      Destroy all child objects. */\r
611         cl_spinlock_acquire( &p_obj->lock );\r
612         for( p_list_item = cl_qlist_tail( &p_obj->child_list );\r
613                  p_list_item != cl_qlist_end( &p_obj->child_list );\r
614                  p_list_item = cl_qlist_tail( &p_obj->child_list ) )\r
615         {\r
616                 p_rel = (cl_obj_rel_t*)PARENT_STRUCT( p_list_item,\r
617                         cl_obj_rel_t, pool_item );\r
618 \r
619                 /*\r
620                  * Take a reference on the child to protect against another parent\r
621                  * of the object destroying it while we are trying to access it.\r
622                  * If the child object is being destroyed, it will try to remove\r
623                  * this relationship from this parent.\r
624                  */\r
625                 p_child_obj = p_rel->p_child_obj;\r
626                 cl_obj_ref( p_child_obj );\r
627 \r
628                 /*\r
629                  * We cannot hold the parent lock when acquiring the child's lock, or\r
630                  * a deadlock can occur if the child is in the process of destroying\r
631                  * itself and its parent relationships.\r
632                  */\r
633                 cl_spinlock_release( &p_obj->lock );\r
634 \r
635                 /*\r
636                  * Mark that we wish to destroy the object.  If the old state indicates\r
637                  * that we should destroy the object, continue with the destruction.\r
638                  * Note that there is a reference held on the child object from its\r
639                  * creation.  We no longer need the prior reference taken above.\r
640                  */\r
641                 old_state = __obj_set_state( p_child_obj, CL_DESTROYING );\r
642                 cl_obj_deref( p_child_obj );\r
643 \r
644                 if( old_state != CL_DESTROYING )\r
645                         __destroy_obj( p_child_obj );\r
646 \r
647                 /* Continue processing the relationship list. */\r
648                 cl_spinlock_acquire( &p_obj->lock );\r
649         }\r
650         cl_spinlock_release( &p_obj->lock );\r
651 }\r
652 \r
653 \r
654 \r
655 /*\r
656  * Destroys an object.  This call returns TRUE if the destruction process\r
657  * should proceed, or FALSE if destruction is already in progress.\r
658  */\r
659 static void\r
660 __destroy_obj(\r
661         IN                              cl_obj_t                                        *p_obj )\r
662 {\r
663         uint32_t                        ref_cnt;\r
664         cl_destroy_type_t       destroy_type;\r
665 \r
666         CL_ASSERT( p_obj );\r
667         CL_ASSERT( p_obj->state == CL_DESTROYING );\r
668 \r
669          /* Remove this child object from all its parents. */\r
670         __remove_parent_rel( p_obj );\r
671 \r
672         /* Notify the user that the object is being destroyed. */\r
673         if( p_obj->pfn_destroying )\r
674                 p_obj->pfn_destroying( p_obj );\r
675 \r
676         /*      Destroy all child objects. */\r
677         __destroy_child_obj( p_obj );\r
678 \r
679         /*\r
680          * Cache the destroy_type because the object could be freed by the time\r
681          * cl_obj_deref below returns.\r
682          */\r
683         destroy_type = p_obj->destroy_type;\r
684 \r
685         /* Dereference this object as it is being destroyed. */\r
686         ref_cnt = cl_obj_deref( p_obj );\r
687 \r
688         if( destroy_type == CL_DESTROY_SYNC )\r
689         {\r
690                 if( ref_cnt )\r
691                 {\r
692                         /* Wait for all other references to go away. */\r
693 #if DBG\r
694                         /*\r
695                          * In debug builds, we assert every 10 seconds - a synchronous\r
696                          * destruction should not take that long.\r
697                          */\r
698                         while( cl_event_wait_on( &p_obj->event, 10000000, FALSE ) ==\r
699                                 CL_TIMEOUT )\r
700                         {\r
701                                 CL_ASSERT( !p_obj->ref_cnt );\r
702                         }\r
703 #else   /* DBG */\r
704                         cl_event_wait_on( &p_obj->event, EVENT_NO_TIMEOUT, FALSE );\r
705 #endif  /* DBG */\r
706                 }\r
707                 __destroy_cb( &p_obj->async_item );\r
708         }\r
709 }\r
710 \r
711 \r
712 \r
713 /*\r
714  * Dereference all parents the object was related to.\r
715  */\r
716 static cl_obj_t*\r
717 __deref_parents(\r
718         IN                              cl_obj_t * const                        p_obj )\r
719 {\r
720         cl_list_item_t          *p_list_item;\r
721         cl_obj_rel_t            *p_rel;\r
722         cl_obj_t                        *p_parent_obj;\r
723 \r
724         /* Destruction of the object is already serialized - no need to lock. */\r
725 \r
726         /*\r
727          * Dereference all parents.  Keep the relationship items in the child's\r
728          * list, so that they can be returned to the user through the free callback.\r
729          */\r
730         for( p_list_item = cl_qlist_head( &p_obj->parent_list );\r
731                  p_list_item != cl_qlist_end( &p_obj->parent_list );\r
732                  p_list_item = cl_qlist_next( p_list_item ) )\r
733         {\r
734                 p_rel = (cl_obj_rel_t*)PARENT_STRUCT( p_list_item,\r
735                         cl_obj_rel_t, list_item );\r
736 \r
737                 p_parent_obj = p_rel->p_parent_obj;\r
738                 p_rel->p_parent_obj = NULL;\r
739                 CL_ASSERT( !p_rel->p_child_obj );\r
740                 if( cl_qlist_next( p_list_item ) ==\r
741                         cl_qlist_end( &p_obj->parent_list ) )\r
742                 {\r
743                         /* Last parent - don't dereference it until after the "free" cb. */\r
744                         return p_parent_obj;\r
745                 }\r
746                 else\r
747                 {\r
748                         cl_obj_deref( p_parent_obj );\r
749                 }\r
750         }\r
751         return NULL;\r
752 }\r
753 \r
754 \r
755 \r
756 static void\r
757 __destroy_cb(\r
758         IN                              cl_async_proc_item_t            *p_item )\r
759 {\r
760         cl_obj_t                                *p_obj, *p_last_parent;\r
761 \r
762         CL_ASSERT( p_item );\r
763 \r
764         p_obj = PARENT_STRUCT( p_item, cl_obj_t, async_item );\r
765         CL_ASSERT( !p_obj->ref_cnt );\r
766         CL_ASSERT( p_obj->state == CL_DESTROYING );\r
767 \r
768         /* Cleanup any hardware related resources. */\r
769         if( p_obj->pfn_cleanup )\r
770                 p_obj->pfn_cleanup( p_obj );\r
771 \r
772         /* We can now safely dereference all but the last parent. */\r
773         p_last_parent = __deref_parents( p_obj );\r
774 \r
775         /* Free the resources associated with the object. */\r
776         CL_ASSERT( p_obj->pfn_free );\r
777         p_obj->pfn_free( p_obj );\r
778 \r
779         if( p_last_parent )\r
780                 cl_obj_deref( p_last_parent );\r
781 }\r