Changeset 13729 in vbox
- Timestamp:
- Nov 1, 2008 1:43:27 PM (16 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
-
VirtualBoxBase.cpp (modified) (2 diffs)
-
include/VirtualBoxBase.h (modified) (21 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/VirtualBoxBase.cpp
r13580 r13729 1068 1068 1069 1069 /** 1070 * Uninitializes all dependent children registered with #addDependentChild(). 1071 * 1072 * Typically called from the uninit() method. Note that this method will call 1073 * uninit() methods of child objects. If these methods need to call the parent 1074 * object during initialization, uninitDependentChildren() must be called before 1075 * the relevant part of the parent is uninitialized, usually at the begnning of 1076 * the parent uninitialization sequence. 1070 * Uninitializes all dependent children registered on this object with 1071 * #addDependentChild(). 1072 * 1073 * Must be called from within the VirtualBoxBaseProto::AutoUninitSpan (i.e. 1074 * typically from this object's uninit() method) to uninitialize children 1075 * before this object goes out of service and becomes unusable. 1076 * 1077 * Note that this method will call uninit() methods of child objects. If 1078 * these methods need to call the parent object during uninitialization, 1079 * #uninitDependentChildren() must be called before the relevant part of the 1080 * parent is uninitialized: usually at the begnning of the parent 1081 * uninitialization sequence. 1082 * 1083 * @note May lock something through the called children. 1077 1084 */ 1078 1085 void VirtualBoxBaseWithChildrenNEXT::uninitDependentChildren() 1079 1086 { 1080 LogFlowThisFuncEnter(); 1081 1082 AutoWriteLock mapLock (mMapLock); 1083 1084 LogFlowThisFunc (("count=%u...\n", mDependentChildren.size())); 1087 AutoCaller autoCaller (this); 1088 1089 /* We don't want to hold the childrenLock() write lock here (necessary 1090 * to protect mDependentChildren) when uninitializing children because 1091 * we want to avoid a possible deadlock where we could get stuck in 1092 * child->uninit() blocked by AutoUninitSpan waiting for the number of 1093 * child's callers to drop to zero, while some caller is stuck in our 1094 * removeDependentChild() method waiting for the write lock. 1095 * 1096 * The only safe place to not lock and keep accessing our data members 1097 * is the InUninit state (no active call to our object may exist on 1098 * another thread when we are in InUinint, provided that all such calls 1099 * use the AutoCaller class of course). InUinint is also used as a flag 1100 * by removeDependentChild() that prevents touching mDependentChildren 1101 * from outside. Therefore, we assert. 1102 */ 1103 AssertReturnVoid (autoCaller.state() == InUninit); 1085 1104 1086 1105 if (mDependentChildren.size()) 1087 1106 { 1088 /* We keep the lock until we have enumerated all children.1089 * Those ones that will try to call removeDependentChild() from a1090 * different thread will have to wait */1091 1092 Assert (mUninitDoneSem == NIL_RTSEMEVENT);1093 int vrc = RTSemEventCreate (&mUninitDoneSem);1094 AssertRC (vrc);1095 1096 Assert (mChildrenLeft == 0);1097 mChildrenLeft = mDependentChildren.size();1098 1099 1107 for (DependentChildren::iterator it = mDependentChildren.begin(); 1100 it != mDependentChildren.end(); ++ it)1108 it != mDependentChildren.end(); ++ it) 1101 1109 { 1102 1110 VirtualBoxBase *child = (*it).second; 1103 1111 Assert (child); 1112 1113 /* Note that if child->uninit() happens to be called on another 1114 * thread right before us and is not yet finished, the second 1115 * uninit() call will wait until the first one has done so 1116 * (thanks to AutoUninitSpan). */ 1104 1117 if (child) 1105 1118 child->uninit(); 1106 1119 } 1107 1120 1121 /* release all weak references we hold */ 1108 1122 mDependentChildren.clear(); 1109 1123 } 1110 1111 /* Wait until all children that called uninit() on their own on other 1112 * threads but stuck waiting for the map lock in removeDependentChild() have 1113 * finished uninitialization. */ 1114 1115 if (mUninitDoneSem != NIL_RTSEMEVENT) 1116 { 1117 /* let stuck children run */ 1118 mapLock.leave(); 1119 1120 LogFlowThisFunc (("Waiting for uninitialization of all children...\n")); 1121 1122 RTSemEventWait (mUninitDoneSem, RT_INDEFINITE_WAIT); 1123 1124 mapLock.enter(); 1125 1126 RTSemEventDestroy (mUninitDoneSem); 1127 mUninitDoneSem = NIL_RTSEMEVENT; 1128 Assert (mChildrenLeft == 0); 1129 } 1130 1131 LogFlowThisFuncLeave(); 1132 } 1133 1134 /** 1135 * Returns a pointer to the dependent child corresponding to the given 1136 * interface pointer (used as a key in the map of dependent children) or NULL 1137 * if the interface pointer doesn't correspond to any child registered using 1138 * #addDependentChild(). 1124 } 1125 1126 /** 1127 * Returns a pointer to the dependent child (registered using 1128 * #addDependentChild()) corresponding to the given interface pointer or NULL if 1129 * the given pointer is unrelated. 1130 * 1131 * The relation is checked by using the given interface pointer as a key in the 1132 * map of dependent children. 1139 1133 * 1140 1134 * Note that ComPtr <IUnknown> is used as an argument instead of IUnknown * in … … 1143 1137 * 1144 1138 * @param aUnk Pointer to map to the dependent child object. 1145 * @return Pointer to the dependent child object. 1139 * @return Pointer to the dependent VirtualBoxBase child object. 1140 * 1141 * @note Locks #childrenLock() for reading. 1146 1142 */ 1147 1143 VirtualBoxBaseNEXT * 1148 1144 VirtualBoxBaseWithChildrenNEXT::getDependentChild (const ComPtr <IUnknown> &aUnk) 1149 1145 { 1150 AssertReturn (! !aUnk, NULL);1151 1152 Auto WriteLock alock (mMapLock);1146 AssertReturn (!aUnk.isNull(), NULL); 1147 1148 AutoCaller autoCaller (this); 1153 1149 1154 1150 /* return NULL if uninitDependentChildren() is in action */ 1155 if ( mUninitDoneSem != NIL_RTSEMEVENT)1151 if (autoCaller.state() == InUninit) 1156 1152 return NULL; 1153 1154 AutoReadLock alock (childrenLock()); 1157 1155 1158 1156 DependentChildren::const_iterator it = mDependentChildren.find (aUnk); 1159 1157 if (it == mDependentChildren.end()) 1160 1158 return NULL; 1159 1161 1160 return (*it).second; 1162 1161 } 1163 1162 1163 /** Helper for addDependentChild(). */ 1164 1164 void VirtualBoxBaseWithChildrenNEXT::doAddDependentChild ( 1165 1165 IUnknown *aUnk, VirtualBoxBaseNEXT *aChild) 1166 1166 { 1167 AssertReturnVoid (aUnk && aChild); 1168 1169 AutoWriteLock alock (mMapLock); 1170 1171 if (mUninitDoneSem != NIL_RTSEMEVENT) 1172 { 1173 /* uninitDependentChildren() is being run. For this very unlikely case, 1174 * we have to increase the number of children left, for symmetry with 1175 * a later #removeDependentChild() call. */ 1176 ++ mChildrenLeft; 1177 return; 1178 } 1167 AssertReturnVoid (aUnk != NULL); 1168 AssertReturnVoid (aChild != NULL); 1169 1170 AutoCaller autoCaller (this); 1171 1172 /* sanity */ 1173 AssertReturnVoid (autoCaller.state() == InInit || 1174 autoCaller.state() == Ready || 1175 autoCaller.state() == Limited); 1176 1177 AutoWriteLock alock (childrenLock()); 1179 1178 1180 1179 std::pair <DependentChildren::iterator, bool> result = 1181 1180 mDependentChildren.insert (DependentChildren::value_type (aUnk, aChild)); 1182 AssertMsg (result.second, ("Failed to insert a child to the map\n")); 1183 } 1184 1181 AssertMsg (result.second, ("Failed to insert child %p to the map\n", aUnk)); 1182 } 1183 1184 /** Helper for removeDependentChild(). */ 1185 1185 void VirtualBoxBaseWithChildrenNEXT::doRemoveDependentChild (IUnknown *aUnk) 1186 1186 { 1187 1187 AssertReturnVoid (aUnk); 1188 1188 1189 AutoWriteLock alock (mMapLock); 1190 1191 if (mUninitDoneSem != NIL_RTSEMEVENT) 1192 { 1193 /* uninitDependentChildren() is being run. Just decrease the number of 1194 * children left and signal a semaphore if it reaches zero. */ 1195 Assert (mChildrenLeft != 0); 1196 -- mChildrenLeft; 1197 if (mChildrenLeft == 0) 1198 { 1199 int vrc = RTSemEventSignal (mUninitDoneSem); 1200 AssertRC (vrc); 1201 } 1189 AutoCaller autoCaller (this); 1190 1191 /* return shortly; uninitDependentChildren() will do the job */ 1192 if (autoCaller.state() == InUninit) 1202 1193 return; 1203 } 1194 1195 AutoWriteLock alock (childrenLock()); 1204 1196 1205 1197 DependentChildren::size_type result = mDependentChildren.erase (aUnk); 1206 AssertMsg (result == 1, ("Failed to remove the child %p from the map\n", 1207 aUnk)); 1198 AssertMsg (result == 1, ("Failed to remove child %p from the map\n", aUnk)); 1208 1199 NOREF (result); 1209 1200 } -
trunk/src/VBox/Main/include/VirtualBoxBase.h
r13580 r13729 1927 1927 * <ol><li> 1928 1928 * Given an IUnknown instance, it's possible to quickly determine 1929 * whether this instance represents a child object created by the given1930 * component, and if so, get a valid VirtualBoxBase pointer to the child1931 * object. The returned pointer can be then safely casted to the1929 * whether this instance represents a child object that belongs to the 1930 * given component, and if so, get a valid VirtualBoxBase pointer to the 1931 * child object. The returned pointer can be then safely casted to the 1932 1932 * actual class of the child object (to get access to its "internal" 1933 1933 * non-interface methods) provided that no other child components implement 1934 * the same initialinterface IUnknown is queried from.1934 * the same orignial COM interface IUnknown is queried from. 1935 1935 * </li><li> 1936 1936 * When the parent object uninitializes itself, it can easily unintialize … … 1945 1945 * its parent to register itself within the list of dependent children. 1946 1946 * </li><li> 1947 * When a child object it is uninitialized, it calls 1948 * #removeDependentChild() to unregister itself. Since the child's 1949 * uninitialization may originate both from this method and from the child 1950 * itself calling its uninit() on another thread at the same time, please 1951 * make sure that #removeDependentChild() is called: 1952 * <ul><li> 1953 * after the child has successfully entered AutoUninitSpan -- to make 1954 * sure this method is called only once for the given child object 1955 * transitioning from Ready to NotReady. A failure to do so will at 1956 * least likely cause an assertion ("Failed to remove the child from 1957 * the map"). 1958 * </li><li> 1959 * outside the child object's lock -- to avoid guaranteed deadlocks 1960 * caused by different lock order: (child_lock, map_lock) in uninit() 1961 * and (map_lock, child_lock) in this method. 1962 * </li></ul> 1947 * When the child object it is uninitialized, it calls 1948 * #removeDependentChild() to unregister itself. 1963 1949 * </li></ol> 1950 * 1951 * Note that if the parent object does not call #uninitDependentChildren() when 1952 * it gets uninitialized, it must call uninit() methods of individual children 1953 * manually to disconnect them; a failure to do so will cause crashes in these 1954 * methods when chidren get destroyed. 1964 1955 * 1965 1956 * Note that children added by #addDependentChild() are <b>weakly</b> referenced … … 1969 1960 * described here. 1970 1961 * 1971 * @note Once again: because of weak referencing, deadlocks and assertions are 1972 * very likely if #addDependentChild() or #removeDependentChild() are used 1973 * incorrectly (called at inappropriate times). Check the above rules once 1974 * more. 1962 * Access to the child list is serialized using the #childrenLock() lock handle 1963 * (which defaults to the general object lock handle (see 1964 * VirtualBoxBase::lockHandle()). This lock is used by all add/remove methods of 1965 * this class so be aware of the need to preserve the {parent, child} lock order 1966 * when calling these methods. 1975 1967 * 1976 1968 * @todo This is a VirtualBoxBaseWithChildren equivalent that uses the … … 1984 1976 1985 1977 VirtualBoxBaseWithChildrenNEXT() 1986 : mUninitDoneSem (NIL_RTSEMEVENT), mChildrenLeft (0)1987 1978 {} 1988 1979 … … 1991 1982 1992 1983 /** 1993 * Adds the given child to the map of dependent children. 1994 * 1995 * Typically called from the child's init() method, from within the 1996 * AutoInitSpan scope. Otherwise, VirtualBoxBase::AutoCaller must be 1997 * used on @a aChild to make sure it is not uninitialized during this 1998 * method's call. 1984 * Lock handle to use when adding/removing child objects from the list of 1985 * children. It is guaranteed that no any other lock is requested in methods 1986 * of this class while holding this lock. 1987 * 1988 * @warning By default, this simply returns the general object's lock handle 1989 * (see VirtualBoxBase::lockHandle()) which is sufficient for most 1990 * cases. 1991 */ 1992 virtual RWLockHandle *childrenLock() { return lockHandle(); } 1993 1994 /** 1995 * Adds the given child to the list of dependent children. 1996 * 1997 * Usually gets called from the child's init() method. 1998 * 1999 * @note @a aChild (unless it is in InInit state) must be protected by 2000 * VirtualBoxBase::AutoCaller to make sure it is not uninitialized on 2001 * another thread during this method's call. 2002 * 2003 * @note When #childrenLock() is not overloaded (returns the general object 2004 * lock) and this method is called from under the child's read or 2005 * write lock, make sure the {parent, child} locking order is 2006 * preserved by locking the callee (this object) for writing before 2007 * the child's lock. 1999 2008 * 2000 2009 * @param aChild Child object to add (must inherit VirtualBoxBase AND 2001 2010 * implement some interface). 2011 * 2012 * @note Locks #childrenLock() for writing. 2002 2013 */ 2003 2014 template <class C> 2004 2015 void addDependentChild (C *aChild) 2005 2016 { 2006 AssertReturnVoid (aChild );2017 AssertReturnVoid (aChild != NULL); 2007 2018 doAddDependentChild (ComPtr <IUnknown> (aChild), aChild); 2008 2019 } … … 2020 2031 2021 2032 /** 2022 * Removes the given child from the map of dependent children. 2023 * 2024 * Typically called from from the child's uninit() method, from within the 2025 * AutoUninitSpan scope. Make sure te child is not locked for reading or 2026 * writing in this case. 2027 * 2028 * If called not from within the AutoUninitSpan scope, 2029 * VirtualBoxBase::AutoCaller must be used on @a aChild to make sure it is 2030 * not uninitialized during this method's call. 2031 * 2032 * @param aChild Child object to remove (must inherit VirtualBoxBase AND 2033 * implement some interface). 2033 * Removes the given child from the list of dependent children. 2034 * 2035 * Usually gets called from the child's uninit() method. 2036 * 2037 * @note Locks #childrenLock() for writing. 2038 * 2039 * @note @a aChild (unless it is in InUninit state) must be protected by 2040 * VirtualBoxBase::AutoCaller to make sure it is not uninitialized on 2041 * another thread during this method's call. 2042 * 2043 * @note When #childrenLock() is not overloaded (returns the general object 2044 * lock) and this method is called from under the child's read or 2045 * write lock, make sure the {parent, child} locking order is 2046 * preserved by locking the callee (this object) for writing before 2047 * the child's lock. This is irrelevant when the method is called from 2048 * under this object's VirtualBoxBaseProto::AutoUninitSpan (i.e. in 2049 * InUninit state) since in this case no locking is done. 2050 * 2051 * @param aChild Child object to remove. 2052 * 2053 * @note Locks #childrenLock() for writing. 2034 2054 */ 2035 2055 template <class C> 2036 2056 void removeDependentChild (C *aChild) 2037 2057 { 2038 AssertReturnVoid (aChild );2058 AssertReturnVoid (aChild != NULL); 2039 2059 doRemoveDependentChild (ComPtr <IUnknown> (aChild)); 2040 2060 } … … 2072 2092 typedef std::map <IUnknown *, VirtualBoxBaseNEXT *> DependentChildren; 2073 2093 DependentChildren mDependentChildren; 2074 2075 RTSEMEVENT mUninitDoneSem;2076 size_t mChildrenLeft;2077 2078 /* Protects all the fields above */2079 RWLockHandle mMapLock;2080 2094 }; 2081 2095 … … 2092 2106 * #addDependentChild() are <b>strongly</b> referenced, so that they cannot 2093 2107 * be externally destructed until #removeDependentChild() is called. 2094 * For this reason, strict rules of calling #removeDependentChild() don't 2095 * apply to instances of this class -- it can be called anywhere in the 2096 * child's uninit() implementation. 2108 * 2109 * Also, this class doesn't have the 2110 * VirtualBoxBaseWithChildrenNEXT::getDependentChild() method because it would 2111 * be not fast for long lists. 2097 2112 * 2098 2113 * @param C type of child objects (must inherit VirtualBoxBase AND … … 2238 2253 * Base class to track component's chlidren of the particular type. 2239 2254 * 2240 * This class is similar to VirtualBoxBaseWithChildren , with the exception that2241 * all children must be of the same type. For this reason, it's not necessary to2242 * use a map to store children -- a list is used instead.2255 * This class is similar to VirtualBoxBaseWithChildrenNEXT with the exception 2256 * that all children must be of the same type. For this reason, it's not 2257 * necessary to use a map to store children -- a list is used instead. 2243 2258 * 2244 2259 * Also, as opposed to VirtualBoxBaseWithChildren, children added by … … 2248 2263 * See individual method descriptions for more information. 2249 2264 * 2250 * @param C Type of child objects (must inherit VirtualBoxBase AND implement some2251 * interface).2265 * @param C Type of child objects (must inherit VirtualBoxBase AND implement 2266 * some interface). 2252 2267 * 2253 2268 * @todo This is a VirtualBoxBaseWithChildren equivalent that uses the … … 2268 2283 2269 2284 /** 2285 * Lock handle to use when adding/removing child objects from the list of 2286 * children. It is guaranteed that no any other lock is requested in methods 2287 * of this class while holding this lock. 2288 * 2289 * @warning By default, this simply returns the general object's lock handle 2290 * (see VirtualBoxBase::lockHandle()) which is sufficient for most 2291 * cases. 2292 */ 2293 virtual RWLockHandle *childrenLock() { return lockHandle(); } 2294 2295 /** 2270 2296 * Adds the given child to the list of dependent children. 2271 2297 * 2272 2298 * Usually gets called from the child's init() method. 2273 *2274 * @note Locks this object for writing.2275 2299 * 2276 2300 * @note @a aChild (unless it is in InInit state) must be protected by … … 2278 2302 * another thread during this method's call. 2279 2303 * 2280 * @note If this method is called from under the child's read or write lock, 2281 * make sure the {parent, child} locking order is preserved by locking 2282 * the callee (this object) for writing before the child's lock. 2304 * @note When #childrenLock() is not overloaded (returns the general object 2305 * lock) and this method is called from under the child's read or 2306 * write lock, make sure the {parent, child} locking order is 2307 * preserved by locking the callee (this object) for writing before 2308 * the child's lock. 2283 2309 * 2284 2310 * @param aChild Child object to add. 2311 * 2312 * @note Locks #childrenLock() for writing. 2285 2313 */ 2286 2314 void addDependentChild (C *aChild) 2287 2315 { 2288 AssertReturnVoid (aChild );2316 AssertReturnVoid (aChild != NULL); 2289 2317 2290 2318 AutoCaller autoCaller (this); … … 2295 2323 autoCaller.state() == Limited); 2296 2324 2297 AutoWriteLock alock ( this);2325 AutoWriteLock alock (childrenLock()); 2298 2326 mDependentChildren.push_back (aChild); 2299 2327 } … … 2304 2332 * Usually gets called from the child's uninit() method. 2305 2333 * 2306 * Note that once this method returns, the callee (this object) is not 2307 * guaranteed to be valid any more, so the caller must not call its 2308 * other methods. 2309 * 2310 * @note Locks this object for writing. 2334 * Note that once this method returns, the argument (@a aChild) is not 2335 * guaranteed to be valid any more, so the caller of this method must not 2336 * call its other methods. 2311 2337 * 2312 2338 * @note @a aChild (unless it is in InUninit state) must be protected by … … 2314 2340 * another thread during this method's call. 2315 2341 * 2316 * @note If this method is called from under the child's read or write lock, 2317 * make sure the {parent, child} locking order is preserved by locking 2318 * the callee (this object) for writing before the child's lock. 2342 * @note When #childrenLock() is not overloaded (returns the general object 2343 * lock) and this method is called from under the child's read or 2344 * write lock, make sure the {parent, child} locking order is 2345 * preserved by locking the callee (this object) for writing before 2346 * the child's lock. This is irrelevant when the method is called from 2347 * under this object's AutoUninitSpan (i.e. in InUninit state) since 2348 * in this case no locking is done. 2319 2349 * 2320 2350 * @param aChild Child object to remove. 2351 * 2352 * @note Locks #childrenLock() for writing. 2321 2353 */ 2322 2354 void removeDependentChild (C *aChild) … … 2330 2362 return; 2331 2363 2332 AutoWriteLock alock ( this);2364 AutoWriteLock alock (childrenLock()); 2333 2365 mDependentChildren.remove (aChild); 2334 2366 } … … 2339 2371 * Returns the read-only list of all dependent children. 2340 2372 * 2341 * @note Access the returned list (iterate, get size etc.) only after 2342 * locking this objectfor reading or for writing!2373 * @note Access the returned list (iterate, get size etc.) only after making 2374 * sure #childrenLock() is locked for reading or for writing! 2343 2375 */ 2344 2376 const DependentChildren &dependentChildren() const { return mDependentChildren; } … … 2355 2387 * these methods need to call the parent object during uninitialization, 2356 2388 * #uninitDependentChildren() must be called before the relevant part of the 2357 * parent is uninitialized ,usually at the begnning of the parent2389 * parent is uninitialized: usually at the begnning of the parent 2358 2390 * uninitialization sequence. 2359 2391 * 2360 * @note May lock this objectthrough the called children.2392 * @note May lock something through the called children. 2361 2393 */ 2362 2394 void uninitDependentChildren() … … 2364 2396 AutoCaller autoCaller (this); 2365 2397 2366 /* We cannot hold the write lock (necessary here to protect2367 * mDependentChildren) when uninitializing children because we want to2368 * avoid a possible deadlock where we could get stuck in child->uninit()2369 * blocked by AutoUninitSpan waiting for the number of child's callers2370 * to drop to zero, while some caller is stuck in our2398 /* We don't want to hold the childrenLock() write lock here (necessary 2399 * to protect mDependentChildren) when uninitializing children because 2400 * we want to avoid a possible deadlock where we could get stuck in 2401 * child->uninit() blocked by AutoUninitSpan waiting for the number of 2402 * child's callers to drop to zero, while some caller is stuck in our 2371 2403 * removeDependentChild() method waiting for the write lock. 2372 2404 * … … 2389 2421 2390 2422 /* Note that if child->uninit() happens to be called on another 2391 * thread right before us and is not yet finished ;the second2423 * thread right before us and is not yet finished, the second 2392 2424 * uninit() call will wait until the first one has done so 2393 2425 * (thanks to AutoUninitSpan). */ … … 2409 2441 * another thread during this method's call. 2410 2442 * 2411 * @note Locks this objectfor writing.2443 * @note Locks #childrenLock() for writing. 2412 2444 */ 2413 2445 void removeDependentChildren() 2414 2446 { 2415 AutoWriteLock alock ( this);2447 AutoWriteLock alock (childrenLock()); 2416 2448 mDependentChildren.clear(); 2417 2449 }
Note:
See TracChangeset
for help on using the changeset viewer.

