VirtualBox

Changeset 13729 in vbox


Ignore:
Timestamp:
Nov 1, 2008 1:43:27 PM (16 years ago)
Author:
vboxsync
Message:

Main: VirtualBoxBaseWithChildrenNEXT: Don't use a separate lock for a map since in the new locking scheme it is safe to use the general object lock. Add a possiblity to override the lock used to serialize access to the map (list for VirtualBoxBaseWithTypedChildrenNEXT) of children by reimplementing childrenLock().

Location:
trunk/src/VBox/Main
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/VirtualBoxBase.cpp

    r13580 r13729  
    10681068
    10691069/**
    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.
    10771084 */
    10781085void VirtualBoxBaseWithChildrenNEXT::uninitDependentChildren()
    10791086{
    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);
    10851104
    10861105    if (mDependentChildren.size())
    10871106    {
    1088         /* We keep the lock until we have enumerated all children.
    1089          * Those ones that will try to call removeDependentChild() from a
    1090          * 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 
    10991107        for (DependentChildren::iterator it = mDependentChildren.begin();
    1100             it != mDependentChildren.end(); ++ it)
     1108             it != mDependentChildren.end(); ++ it)
    11011109        {
    11021110            VirtualBoxBase *child = (*it).second;
    11031111            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). */
    11041117            if (child)
    11051118                child->uninit();
    11061119        }
    11071120
     1121        /* release all weak references we hold */
    11081122        mDependentChildren.clear();
    11091123    }
    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.
    11391133 *
    11401134 * Note that ComPtr <IUnknown> is used as an argument instead of IUnknown * in
     
    11431137 *
    11441138 * @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.
    11461142 */
    11471143VirtualBoxBaseNEXT *
    11481144VirtualBoxBaseWithChildrenNEXT::getDependentChild (const ComPtr <IUnknown> &aUnk)
    11491145{
    1150     AssertReturn (!!aUnk, NULL);
    1151 
    1152     AutoWriteLock alock (mMapLock);
     1146    AssertReturn (!aUnk.isNull(), NULL);
     1147
     1148    AutoCaller autoCaller (this);
    11531149
    11541150    /* return NULL if uninitDependentChildren() is in action */
    1155     if (mUninitDoneSem != NIL_RTSEMEVENT)
     1151    if (autoCaller.state() == InUninit)
    11561152        return NULL;
     1153
     1154    AutoReadLock alock (childrenLock());
    11571155
    11581156    DependentChildren::const_iterator it = mDependentChildren.find (aUnk);
    11591157    if (it == mDependentChildren.end())
    11601158        return NULL;
     1159
    11611160    return (*it).second;
    11621161}
    11631162
     1163/** Helper for addDependentChild(). */
    11641164void VirtualBoxBaseWithChildrenNEXT::doAddDependentChild (
    11651165    IUnknown *aUnk, VirtualBoxBaseNEXT *aChild)
    11661166{
    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());
    11791178
    11801179    std::pair <DependentChildren::iterator, bool> result =
    11811180        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(). */
    11851185void VirtualBoxBaseWithChildrenNEXT::doRemoveDependentChild (IUnknown *aUnk)
    11861186{
    11871187    AssertReturnVoid (aUnk);
    11881188
    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)
    12021193        return;
    1203     }
     1194
     1195    AutoWriteLock alock (childrenLock());
    12041196
    12051197    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));
    12081199    NOREF (result);
    12091200}
  • trunk/src/VBox/Main/include/VirtualBoxBase.h

    r13580 r13729  
    19271927 * <ol><li>
    19281928 *      Given an IUnknown instance, it's possible to quickly determine
    1929  *      whether this instance represents a child object created by the given
    1930  *      component, and if so, get a valid VirtualBoxBase pointer to the child
    1931  *      object. The returned pointer can be then safely casted to the
     1929 *      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
    19321932 *      actual class of the child object (to get access to its "internal"
    19331933 *      non-interface methods) provided that no other child components implement
    1934  *      the same initial interface IUnknown is queried from.
     1934 *      the same orignial COM interface IUnknown is queried from.
    19351935 * </li><li>
    19361936 *      When the parent object uninitializes itself, it can easily unintialize
     
    19451945 *      its parent to register itself within the list of dependent children.
    19461946 * </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.
    19631949 * </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.
    19641955 *
    19651956 * Note that children added by #addDependentChild() are <b>weakly</b> referenced
     
    19691960 * described here.
    19701961 *
    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.
    19751967 *
    19761968 * @todo This is a VirtualBoxBaseWithChildren equivalent that uses the
     
    19841976
    19851977    VirtualBoxBaseWithChildrenNEXT()
    1986         : mUninitDoneSem (NIL_RTSEMEVENT), mChildrenLeft (0)
    19871978    {}
    19881979
     
    19911982
    19921983    /**
    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.
    19992008     *
    20002009     * @param aChild    Child object to add (must inherit VirtualBoxBase AND
    20012010     *                  implement some interface).
     2011     *
     2012     * @note Locks #childrenLock() for writing.
    20022013     */
    20032014    template <class C>
    20042015    void addDependentChild (C *aChild)
    20052016    {
    2006         AssertReturnVoid (aChild);
     2017        AssertReturnVoid (aChild != NULL);
    20072018        doAddDependentChild (ComPtr <IUnknown> (aChild), aChild);
    20082019    }
     
    20202031
    20212032    /**
    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.
    20342054     */
    20352055    template <class C>
    20362056    void removeDependentChild (C *aChild)
    20372057    {
    2038         AssertReturnVoid (aChild);
     2058        AssertReturnVoid (aChild != NULL);
    20392059        doRemoveDependentChild (ComPtr <IUnknown> (aChild));
    20402060    }
     
    20722092    typedef std::map <IUnknown *, VirtualBoxBaseNEXT *> DependentChildren;
    20732093    DependentChildren mDependentChildren;
    2074 
    2075     RTSEMEVENT mUninitDoneSem;
    2076     size_t mChildrenLeft;
    2077 
    2078     /* Protects all the fields above */
    2079     RWLockHandle mMapLock;
    20802094};
    20812095
     
    20922106 *  #addDependentChild() are <b>strongly</b> referenced, so that they cannot
    20932107 *  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.
    20972112 *
    20982113 *  @param C    type of child objects (must inherit VirtualBoxBase AND
     
    22382253 * Base class to track component's chlidren of the particular type.
    22392254 *
    2240  * This class is similar to VirtualBoxBaseWithChildren, with the exception that
    2241  * all children must be of the same type. For this reason, it's not necessary to
    2242  * 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.
    22432258 *
    22442259 * Also, as opposed to VirtualBoxBaseWithChildren, children added by
     
    22482263 * See individual method descriptions for more information.
    22492264 *
    2250  * @param C Type of child objects (must inherit VirtualBoxBase AND implementsome
    2251  *          interface).
     2265 * @param C Type of child objects (must inherit VirtualBoxBase AND implement
     2266 *          some interface).
    22522267 *
    22532268 * @todo This is a VirtualBoxBaseWithChildren equivalent that uses the
     
    22682283
    22692284    /**
     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    /**
    22702296     * Adds the given child to the list of dependent children.
    22712297     *
    22722298     * Usually gets called from the child's init() method.
    2273      *
    2274      * @note Locks this object for writing.
    22752299     *
    22762300     * @note @a aChild (unless it is in InInit state) must be protected by
     
    22782302     *       another thread during this method's call.
    22792303     *
    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.
    22832309     *
    22842310     * @param aChild    Child object to add.
     2311     *
     2312     * @note Locks #childrenLock() for writing.
    22852313     */
    22862314    void addDependentChild (C *aChild)
    22872315    {
    2288         AssertReturnVoid (aChild);
     2316        AssertReturnVoid (aChild != NULL);
    22892317
    22902318        AutoCaller autoCaller (this);
     
    22952323                          autoCaller.state() == Limited);
    22962324
    2297         AutoWriteLock alock (this);
     2325        AutoWriteLock alock (childrenLock());
    22982326        mDependentChildren.push_back (aChild);
    22992327    }
     
    23042332     * Usually gets called from the child's uninit() method.
    23052333     *
    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.
    23112337     *
    23122338     * @note @a aChild (unless it is in InUninit state) must be protected by
     
    23142340     *       another thread during this method's call.
    23152341     *
    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.
    23192349     *
    23202350     * @param aChild    Child object to remove.
     2351     *
     2352     * @note Locks #childrenLock() for writing.
    23212353     */
    23222354    void removeDependentChild (C *aChild)
     
    23302362            return;
    23312363
    2332         AutoWriteLock alock (this);
     2364        AutoWriteLock alock (childrenLock());
    23332365        mDependentChildren.remove (aChild);
    23342366    }
     
    23392371     * Returns the read-only list of all dependent children.
    23402372     *
    2341      * @note Access the returned list (iterate, get size etc.) only after
    2342      *       locking this object for 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!
    23432375     */
    23442376    const DependentChildren &dependentChildren() const { return mDependentChildren; }
     
    23552387     * these methods need to call the parent object during uninitialization,
    23562388     * #uninitDependentChildren() must be called before the relevant part of the
    2357      * parent is uninitialized, usually at the begnning of the parent
     2389     * parent is uninitialized: usually at the begnning of the parent
    23582390     * uninitialization sequence.
    23592391     *
    2360      * @note May lock this object through the called children.
     2392     * @note May lock something through the called children.
    23612393     */
    23622394    void uninitDependentChildren()
     
    23642396        AutoCaller autoCaller (this);
    23652397
    2366         /* We cannot hold the write lock (necessary here to protect
    2367          * mDependentChildren) when uninitializing children because we want to
    2368          * avoid a possible deadlock where we could get stuck in child->uninit()
    2369          * blocked by AutoUninitSpan waiting for the number of child's callers
    2370          * to drop to zero, while some caller is stuck in our
     2398        /* 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
    23712403         * removeDependentChild() method waiting for the write lock.
    23722404         *
     
    23892421
    23902422                /* Note that if child->uninit() happens to be called on another
    2391                  * thread right before us and is not yet finished; the second
     2423                 * thread right before us and is not yet finished, the second
    23922424                 * uninit() call will wait until the first one has done so
    23932425                 * (thanks to AutoUninitSpan). */
     
    24092441     *       another thread during this method's call.
    24102442     *
    2411      * @note Locks this object for writing.
     2443     * @note Locks #childrenLock() for writing.
    24122444     */
    24132445    void removeDependentChildren()
    24142446    {
    2415         AutoWriteLock alock (this);
     2447        AutoWriteLock alock (childrenLock());
    24162448        mDependentChildren.clear();
    24172449    }
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette