Index: /trunk/doc/manual/en_US/SDKRef.xml
===================================================================
--- /trunk/doc/manual/en_US/SDKRef.xml	(revision 48296)
+++ /trunk/doc/manual/en_US/SDKRef.xml	(revision 48297)
@@ -3531,4 +3531,17 @@
       <itemizedlist>
         <listitem>
+          <para>The explicit medium locking methods 
+            <xref linkend="IMedium__lockRead" xreflabel="IMedium::lockRead()" />
+            and <xref linkend="IMedium__lockWrite" xreflabel="IMedium::lockWrite()" />
+            have been redesigned. They return a lock token object reference
+            now, and calling the <xref linkend="IToken__abandon"
+              xreflabel="IToken::abandon()" /> method (or letting the reference
+            count to this object drop to 0) will unlock it. This eliminates
+            the rather common problem that an API client crash left behind
+            locks, and also improves the safety (API clients can't release
+            locks they didn't obtain).</para>
+        </listitem>
+
+        <listitem>
           <para><computeroutput>IMachine::delete</computeroutput>
           has been renamed to <xref linkend="IMachine__deleteConfig"
Index: /trunk/include/VBox/log.h
===================================================================
--- /trunk/include/VBox/log.h	(revision 48296)
+++ /trunk/include/VBox/log.h	(revision 48297)
@@ -486,4 +486,6 @@
     /** Main group, ISystemProperties. */
     LOG_GROUP_MAIN_SYSTEMPROPERTIES,
+    /** Main group, IToken. */
+    LOG_GROUP_MAIN_TOKEN,
     /** Main group, IUSBController. */
     LOG_GROUP_MAIN_USBCONTROLLER,
@@ -894,4 +896,5 @@
     "MAIN_STORAGEDEVICECHANGEDEVENT", \
     "MAIN_SYSTEMPROPERTIES", \
+    "MAIN_TOKEN", \
     "MAIN_USBCONTROLLER", \
     "MAIN_USBCONTROLLERCHANGEDEVENT", \
Index: /trunk/src/VBox/Main/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Main/Makefile.kmk	(revision 48296)
+++ /trunk/src/VBox/Main/Makefile.kmk	(revision 48297)
@@ -363,4 +363,5 @@
 	src-server/StorageControllerImpl.cpp \
 	src-server/SystemPropertiesImpl.cpp \
+	src-server/TokenImpl.cpp \
 	src-server/USBControllerImpl.cpp \
 	src-server/USBDeviceFiltersImpl.cpp \
Index: /trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 48296)
+++ /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 48297)
@@ -3700,24 +3700,6 @@
       <desc>
         Gets called by <link to="IInternalSessionControl::onlineMergeMedium"/>.
-      </desc>
-      <param name="mediumAttachment" type="IMediumAttachment" dir="in">
-        <desc>The medium attachment which needs to be cleaned up.</desc>
-      </param>
-      <param name="source" type="IMedium" dir="in">
-        <desc>Merge source medium.</desc>
-      </param>
-      <param name="target" type="IMedium" dir="in">
-        <desc>Merge target medium.</desc>
-      </param>
-      <param name="mergeForward" type="boolean" dir="in">
-        <desc>Merge direction.</desc>
-      </param>
-      <param name="parentForTarget" type="IMedium" dir="in">
-        <desc>For forward merges: new parent for target medium.</desc>
-      </param>
-      <param name="childrenToReparent" type="IMedium" safearray="yes" dir="in">
-        <desc>For backward merges: list of media which need their parent UUID
-        updated.</desc>
-      </param>
+        All necessary state information is available at the called object.
+      </desc>
     </method>
 
@@ -13612,8 +13594,8 @@
         write operation such as <link to="#cloneTo"/> or <link to="#mergeTo"/>.
 
-        The medium locked for reading must be unlocked using the <link
-        to="#unlockRead"/> method. Calls to <link to="#lockRead"/>
-        can be nested and must be followed by the same number of paired
-        <link to="#unlockRead"/> calls.
+        The medium locked for reading must be unlocked by abandoning the
+        returned token object, see <link to="IToken"/>. Calls to
+        <link to="#lockRead"/> can be nested and the lock is actually released
+        when all callers have abandoned the token.
 
         This method sets the medium state (see <link to="#state"/>) to
@@ -13635,28 +13617,9 @@
 
       </desc>
-      <param name="state" type="MediumState" dir="return">
-        <desc>
-          State of the medium after the operation.
-        </desc>
-      </param>
-    </method>
-
-    <method name="unlockRead">
-      <desc>
-        Cancels the read lock previously set by <link to="#lockRead"/>.
-
-        For both success and failure, this method returns the current state
-        of the medium <i>after</i> the operation.
-
-        See <link to="#lockRead"/> for more details.
-
-        <result name="VBOX_E_INVALID_OBJECT_STATE">
-          Medium not locked for reading.
-        </result>
-
-      </desc>
-      <param name="state" type="MediumState" dir="return">
-        <desc>
-          State of the medium after the operation.
+      <param name="token" type="IToken" dir="return">
+        <desc>
+          Token object, when this is released (reference count reaches 0) then
+          the lock count is decreased. The lock is released when the lock count
+          reaches 0.
         </desc>
       </param>
@@ -13689,6 +13652,7 @@
         write operation such as <link to="#cloneTo"/> or <link to="#mergeTo"/>.
 
-        The medium locked for writing must be unlocked using the <link
-        to="#unlockWrite"/> method. Write locks <i>cannot</i> be nested.
+        The medium locked for writing must be unlocked by abandoning the
+        returned token object, see <link to="IToken"/>. Write locks
+        <i>cannot</i> be nested.
 
         This method sets the medium state (see <link to="#state"/>) to
@@ -13701,7 +13665,4 @@
         storage unit.
 
-        For both, success and failure, this method returns the current
-        state of the medium <i>before</i> the operation.
-
         <result name="VBOX_E_INVALID_OBJECT_STATE">
           Invalid medium state (e.g. not created, locked, inaccessible,
@@ -13710,28 +13671,8 @@
 
       </desc>
-      <param name="state" type="MediumState" dir="return">
-        <desc>
-          State of the medium after the operation.
-        </desc>
-      </param>
-    </method>
-
-    <method name="unlockWrite">
-      <desc>
-        Cancels the write lock previously set by <link to="#lockWrite"/>.
-
-        For both success and failure, this method returns the current
-        state of the medium <i>after</i> the operation.
-
-        See <link to="#lockWrite"/> for more details.
-
-        <result name="VBOX_E_INVALID_OBJECT_STATE">
-          Medium not locked for writing.
-        </result>
-
-      </desc>
-      <param name="state" type="MediumState" dir="return">
-        <desc>
-          State of the medium after the operation.
+      <param name="token" type="IToken" dir="return">
+        <desc>
+          Token object, when this is released (reference count reaches 0) then
+          the lock is released.
         </desc>
       </param>
@@ -14494,4 +14435,44 @@
 
   <!--
+  // IToken
+  /////////////////////////////////////////////////////////////////////////
+  -->
+
+  <interface
+    name="IToken" extends="$unknown"
+    uuid="3b1c4797-e289-4d4c-b74c-50c9b86a36f8"
+    wsmap="managed"
+    >
+    <desc>
+      The IToken interface represents a token passed to an API client, which
+      triggers cleanup actions when it is explicitly released by calling the
+      <link to="#abandon"/> method (preferred, as it is accurately defined
+      when the release happens), or when the object reference count drops
+      to 0. The latter way is implicitly used when an API client crashes,
+      however the discovery that there was a crash can take rather long,
+      depending on the platform (COM needs 6 minutes). So better don't rely
+      on the crash behavior too much.
+    </desc>
+
+    <method name="abandon" wrap-hint-server="passcaller">
+      <desc>Releases this token. Cannot be undone in any way, and makes the
+        token object unusable (even the <link to="#dummy"/> method will return
+        an error), ready for releasing. It is a more defined way than just
+        letting the reference count drop to 0, because the latter (depending
+        on the platform) can trigger asynchronous cleanup activity.
+      </desc>
+    </method>
+
+    <method name="dummy">
+      <desc>Purely a NOOP. Useful when using proxy type API bindings (e.g. the
+        webservice) which manage objects on behalf of the actual client, using
+        an object reference expiration time based garbage collector.
+      </desc>
+    </method>
+
+  </interface>
+
+
+  <!--
   // IKeyboard
   /////////////////////////////////////////////////////////////////////////
@@ -14509,6 +14490,6 @@
       Use this interface to send keystrokes or the Ctrl-Alt-Del sequence
       to the virtual machine.
-
     </desc>
+
     <method name="putScancode">
       <desc>Sends a scancode to the keyboard.
@@ -17939,20 +17920,4 @@
         <desc>The index of the target image in the chain.
         Redundant, but drastically reduces IPC.</desc>
-      </param>
-      <param name="source" type="IMedium" dir="in">
-        <desc>Merge source medium.</desc>
-      </param>
-      <param name="target" type="IMedium" dir="in">
-        <desc>Merge target medium.</desc>
-      </param>
-      <param name="mergeForward" type="boolean" dir="in">
-        <desc>Merge direction.</desc>
-      </param>
-      <param name="parentForTarget" type="IMedium" dir="in">
-        <desc>For forward merges: new parent for target medium.</desc>
-      </param>
-      <param name="childrenToReparent" type="IMedium" safearray="yes" dir="in">
-        <desc>For backward merges: list of media which need their parent UUID
-        updated.</desc>
       </param>
       <param name="progress" type="IProgress" dir="in">
Index: /trunk/src/VBox/Main/include/ConsoleImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/ConsoleImpl.h	(revision 48296)
+++ /trunk/src/VBox/Main/include/ConsoleImpl.h	(revision 48297)
@@ -230,7 +230,4 @@
     HRESULT onlineMergeMedium(IMediumAttachment *aMediumAttachment,
                               ULONG aSourceIdx, ULONG aTargetIdx,
-                              IMedium *aSource, IMedium *aTarget,
-                              BOOL aMergeForward, IMedium *aParentForTarget,
-                              ComSafeArrayIn(IMedium *, aChildrenToReparent),
                               IProgress *aProgress);
     VMMDev *getVMMDev() { return m_pVMMDev; }
Index: /trunk/src/VBox/Main/include/MachineImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/MachineImpl.h	(revision 48296)
+++ /trunk/src/VBox/Main/include/MachineImpl.h	(revision 48297)
@@ -1070,9 +1070,5 @@
                               IN_BSTR aEndID, BOOL fDeleteAllChildren,
                               MachineState_T *aMachineState, IProgress **aProgress);
-    STDMETHOD(FinishOnlineMergeMedium)(IMediumAttachment *aMediumAttachment,
-                                       IMedium *aSource, IMedium *aTarget,
-                                       BOOL fMergeForward,
-                                       IMedium *pParentForTarget,
-                                       ComSafeArrayIn(IMedium *, aChildrenToReparent));
+    STDMETHOD(FinishOnlineMergeMedium)();
     STDMETHOD(RestoreSnapshot)(IConsole *aInitiator,
                                ISnapshot *aSnapshot,
@@ -1143,5 +1139,5 @@
     {
         ConsoleTaskData()
-            : mLastState(MachineState_Null)
+            : mLastState(MachineState_Null), mDeleteSnapshotInfo(NULL)
         { }
 
@@ -1151,4 +1147,7 @@
         // used when taking snapshot
         ComObjPtr<Snapshot> mSnapshot;
+
+        // used when deleting online snapshot
+        void *mDeleteSnapshotInfo;
 
         // used when saving state (either as part of a snapshot or separate)
@@ -1185,12 +1184,14 @@
                                         bool &fMergeForward,
                                         ComObjPtr<Medium> &pParentForTarget,
-                                        MediaList &aChildrenToReparent,
+                                        MediumLockList * &aChildrenToReparent,
                                         bool &fNeedOnlineMerge,
-                                        MediumLockList * &aMediumLockList);
+                                        MediumLockList * &aMediumLockList,
+                                        ComPtr<IToken> &aHDLockToken);
     void cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
                                     const ComObjPtr<Medium> &aSource,
-                                    const MediaList &aChildrenToReparent,
+                                    MediumLockList *aChildrenToReparent,
                                     bool fNeedsOnlineMerge,
                                     MediumLockList *aMediumLockList,
+                                    const ComPtr<IToken> &aHDLockToken,
                                     const Guid &aMediumId,
                                     const Guid &aSnapshotId);
@@ -1200,5 +1201,5 @@
                               bool fMergeForward,
                               const ComObjPtr<Medium> &pParentForTarget,
-                              const MediaList &aChildrenToReparent,
+                              MediumLockList *aChildrenToReparent,
                               MediumLockList *aMediumLockList,
                               ComObjPtr<Progress> &aProgress,
Index: /trunk/src/VBox/Main/include/MediumImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/MediumImpl.h	(revision 48296)
+++ /trunk/src/VBox/Main/include/MediumImpl.h	(revision 48297)
@@ -132,8 +132,6 @@
     STDMETHOD(GetSnapshotIds)(IN_BSTR aMachineId,
                               ComSafeArrayOut(BSTR, aSnapshotIds));
-    STDMETHOD(LockRead)(MediumState_T *aState);
-    STDMETHOD(UnlockRead)(MediumState_T *aState);
-    STDMETHOD(LockWrite)(MediumState_T *aState);
-    STDMETHOD(UnlockWrite)(MediumState_T *aState);
+    STDMETHOD(LockRead)(IToken **aToken);
+    STDMETHOD(LockWrite)(IToken **aToken);
     STDMETHOD(Close)();
     STDMETHOD(GetProperty)(IN_BSTR aName, BSTR *aValue);
@@ -226,4 +224,6 @@
 
     HRESULT close(AutoCaller &autoCaller);
+    HRESULT unlockRead(MediumState_T *aState);
+    HRESULT unlockWrite(MediumState_T *aState);
     HRESULT deleteStorage(ComObjPtr<Progress> *aProgress, bool aWait);
     HRESULT markForDeletion();
@@ -241,17 +241,17 @@
                            bool &fMergeForward,
                            ComObjPtr<Medium> &pParentForTarget,
-                           MediaList &aChildrenToReparent,
+                           MediumLockList * &aChildrenToReparent,
                            MediumLockList * &aMediumLockList);
     HRESULT mergeTo(const ComObjPtr<Medium> &pTarget,
                     bool fMergeForward,
                     const ComObjPtr<Medium> &pParentForTarget,
-                    const MediaList &aChildrenToReparent,
+                    MediumLockList *aChildrenToReparent,
                     MediumLockList *aMediumLockList,
                     ComObjPtr<Progress> *aProgress,
                     bool aWait);
-    void cancelMergeTo(const MediaList &aChildrenToReparent,
+    void cancelMergeTo(MediumLockList *aChildrenToReparent,
                        MediumLockList *aMediumLockList);
 
-    HRESULT fixParentUuidOfChildren(const MediaList &childrenToReparent);
+    HRESULT fixParentUuidOfChildren(MediumLockList *pChildrenToReparent);
 
     HRESULT exportFile(const char *aFilename,
Index: /trunk/src/VBox/Main/include/MediumLock.h
===================================================================
--- /trunk/src/VBox/Main/include/MediumLock.h	(revision 48296)
+++ /trunk/src/VBox/Main/include/MediumLock.h	(revision 48297)
@@ -7,5 +7,5 @@
 
 /*
- * Copyright (C) 2010-2012 Oracle Corporation
+ * Copyright (C) 2010-2013 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -90,9 +90,16 @@
 
     /**
+     * Check if this medium object has been locked by this MediumLock.
+     */
+    bool IsLocked() const;
+
+    /**
      * Acquire a medium lock.
      *
      * @return COM status code
-     */
-    HRESULT Lock();
+     * @param aIgnoreLockedMedia    If set ignore all media which is already
+     *                              locked in an incompatible way.
+     */
+    HRESULT Lock(bool aIgnoreLockedMedia = false);
 
     /**
@@ -105,4 +112,5 @@
 private:
     ComObjPtr<Medium> mMedium;
+    ComPtr<IToken> mToken;
     AutoCaller mMediumCaller;
     bool mLockWrite;
@@ -208,6 +216,12 @@
      *
      * @return COM status code
-     */
-    HRESULT Lock();
+     * @param aSkipOverLockedMedia  If set ignore all media which is already
+     *                              locked for reading or writing. For callers
+     *                              which need to know which medium objects
+     *                              have been locked by this lock list you
+     *                              can iterate over the list and check the
+     *                              MediumLock state.
+     */
+    HRESULT Lock(bool aSkipOverLockedMedia = false);
 
     /**
Index: /trunk/src/VBox/Main/include/SessionImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/SessionImpl.h	(revision 48296)
+++ /trunk/src/VBox/Main/include/SessionImpl.h	(revision 48297)
@@ -103,7 +103,4 @@
     STDMETHOD(OnlineMergeMedium)(IMediumAttachment *aMediumAttachment,
                                  ULONG aSourceIdx, ULONG aTargetIdx,
-                                 IMedium *aSource, IMedium *aTarget,
-                                 BOOL aMergeForward, IMedium *aParentForTarget,
-                                 ComSafeArrayIn(IMedium *, aChildrenToReparent),
                                  IProgress *aProgress);
     STDMETHOD(EnableVMMStatistics)(BOOL aEnable);
Index: /trunk/src/VBox/Main/include/TokenImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/TokenImpl.h	(revision 48297)
+++ /trunk/src/VBox/Main/include/TokenImpl.h	(revision 48297)
@@ -0,0 +1,107 @@
+/* $Id$ */
+
+/** @file
+ *
+ * Token COM class implementations: MachineToken and MediumLockToken
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef TOKEN_IMPL_H_
+#define TOKEN_IMPL_H_
+
+#include "TokenWrap.h"
+#include "MachineImpl.h"
+
+
+/**
+ * The MachineToken class automates cleanup of a SessionMachine object.
+ */
+class ATL_NO_VTABLE MachineToken :
+    public TokenWrap
+{
+public:
+
+    DECLARE_EMPTY_CTOR_DTOR(MachineToken)
+
+    HRESULT FinalConstruct();
+    void FinalRelease();
+
+    // public initializer/uninitializer for internal purposes only
+    HRESULT init(const ComObjPtr<SessionMachine> &pSessionMachine);
+    void uninit();
+
+private:
+
+    // wrapped IToken methods
+    HRESULT abandon(AutoCaller &aAutoCaller);
+    HRESULT dummy();
+
+    // data
+    struct Data
+    {
+        Data()
+	{
+	}
+
+        ComObjPtr<SessionMachine> pSessionMachine;
+    };
+
+    Data m;
+};
+
+
+class Medium;
+
+/**
+ * The MediumLockToken class automates cleanup of a Medium lock.
+ */
+class ATL_NO_VTABLE MediumLockToken :
+    public TokenWrap
+{
+public:
+
+    DECLARE_EMPTY_CTOR_DTOR(MediumLockToken)
+
+    HRESULT FinalConstruct();
+    void FinalRelease();
+
+    // public initializer/uninitializer for internal purposes only
+    HRESULT init(const ComObjPtr<Medium> &pMedium, bool fWrite);
+    void uninit();
+
+private:
+
+    // wrapped IToken methods
+    HRESULT abandon(AutoCaller &aAutoCaller);
+    HRESULT dummy();
+
+    // data
+    struct Data
+    {
+        Data() :
+            fWrite(false)
+        {
+        }
+
+        ComObjPtr<Medium> pMedium;
+        bool fWrite;
+    };
+
+    Data m;
+};
+
+
+#endif // TOKEN_IMPL_H_
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Index: /trunk/src/VBox/Main/include/VirtualBoxImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/VirtualBoxImpl.h	(revision 48296)
+++ /trunk/src/VBox/Main/include/VirtualBoxImpl.h	(revision 48297)
@@ -311,4 +311,5 @@
     AutostartDb* getAutostartDb() const;
 
+    RWLockHandle& getMachinesListLockHandle();
     RWLockHandle& getMediaTreeLockHandle();
 
Index: /trunk/src/VBox/Main/src-client/ConsoleImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/ConsoleImpl.cpp	(revision 48296)
+++ /trunk/src/VBox/Main/src-client/ConsoleImpl.cpp	(revision 48297)
@@ -5528,8 +5528,4 @@
 HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment,
                                    ULONG aSourceIdx, ULONG aTargetIdx,
-                                   IMedium *aSource, IMedium *aTarget,
-                                   BOOL aMergeForward,
-                                   IMedium *aParentForTarget,
-                                   ComSafeArrayIn(IMedium *, aChildrenToReparent),
                                    IProgress *aProgress)
 {
@@ -5708,7 +5704,5 @@
 
     /* Update medium chain and state now, so that the VM can continue. */
-    rc = mControl->FinishOnlineMergeMedium(aMediumAttachment, aSource, aTarget,
-                                           aMergeForward, aParentForTarget,
-                                           ComSafeArrayInArg(aChildrenToReparent));
+    rc = mControl->FinishOnlineMergeMedium();
 
     vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(),
Index: /trunk/src/VBox/Main/src-client/SessionImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/SessionImpl.cpp	(revision 48296)
+++ /trunk/src/VBox/Main/src-client/SessionImpl.cpp	(revision 48297)
@@ -980,8 +980,4 @@
 STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment,
                                         ULONG aSourceIdx, ULONG aTargetIdx,
-                                        IMedium *aSource, IMedium *aTarget,
-                                        BOOL aMergeForward,
-                                        IMedium *aParentForTarget,
-                                        ComSafeArrayIn(IMedium *, aChildrenToReparent),
                                         IProgress *aProgress)
 {
@@ -997,10 +993,7 @@
     AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE);
     CheckComArgNotNull(aMediumAttachment);
-    CheckComArgSafeArrayNotNull(aChildrenToReparent);
-
-    return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx,
-                                       aTargetIdx, aSource, aTarget,
-                                       aMergeForward, aParentForTarget,
-                                       ComSafeArrayInArg(aChildrenToReparent),
+
+    return mConsole->onlineMergeMedium(aMediumAttachment,
+                                       aSourceIdx, aTargetIdx,
                                        aProgress);
 #else
Index: /trunk/src/VBox/Main/src-server/MachineImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/MachineImpl.cpp	(revision 48296)
+++ /trunk/src/VBox/Main/src-server/MachineImpl.cpp	(revision 48297)
@@ -903,4 +903,8 @@
     LogFlowThisFunc(("ENTER\n"));
 
+    /* In some cases (medium registry related), it is necessary to be able to
+     * go through the list of all machines. Happens when an inaccessible VM
+     * has a sensible medium registry. */
+    AutoReadLock mllock(mParent->getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
Index: /trunk/src/VBox/Main/src-server/MediumImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/MediumImpl.cpp	(revision 48296)
+++ /trunk/src/VBox/Main/src-server/MediumImpl.cpp	(revision 48297)
@@ -17,4 +17,5 @@
 
 #include "MediumImpl.h"
+#include "TokenImpl.h"
 #include "ProgressImpl.h"
 #include "SystemPropertiesImpl.h"
@@ -515,5 +516,5 @@
               bool fMergeForward,
               Medium *aParentForTarget,
-              const MediaList &aChildrenToReparent,
+              MediumLockList *aChildrenToReparent,
               Progress *aProgress,
               MediumLockList *aMediumLockList,
@@ -523,30 +524,11 @@
           mfMergeForward(fMergeForward),
           mParentForTarget(aParentForTarget),
-          mChildrenToReparent(aChildrenToReparent),
+          mpChildrenToReparent(aChildrenToReparent),
           mpMediumLockList(aMediumLockList),
           mTargetCaller(aTarget),
           mParentForTargetCaller(aParentForTarget),
-          mfChildrenCaller(false),
           mfKeepMediumLockList(fKeepMediumLockList)
     {
         AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
-        for (MediaList::const_iterator it = mChildrenToReparent.begin();
-             it != mChildrenToReparent.end();
-             ++it)
-        {
-            HRESULT rc2 = (*it)->addCaller();
-            if (FAILED(rc2))
-            {
-                mRC = E_FAIL;
-                for (MediaList::const_iterator it2 = mChildrenToReparent.begin();
-                     it2 != it;
-                     --it2)
-                {
-                    (*it2)->releaseCaller();
-                }
-                return;
-            }
-        }
-        mfChildrenCaller = true;
     }
 
@@ -555,21 +537,14 @@
         if (!mfKeepMediumLockList && mpMediumLockList)
             delete mpMediumLockList;
-        if (mfChildrenCaller)
-        {
-            for (MediaList::const_iterator it = mChildrenToReparent.begin();
-                 it != mChildrenToReparent.end();
-                 ++it)
-            {
-                (*it)->releaseCaller();
-            }
-        }
+        if (mpChildrenToReparent)
+            delete mpChildrenToReparent;
     }
 
     const ComObjPtr<Medium> mTarget;
     bool mfMergeForward;
-    /* When mChildrenToReparent is empty then mParentForTarget is non-null.
-     * In other words: they are used in different cases. */
+    /* When mpChildrenToReparent is null then mParentForTarget is non-null and
+     * vice versa. In other words: they are used in different cases. */
     const ComObjPtr<Medium> mParentForTarget;
-    MediaList mChildrenToReparent;
+    MediumLockList *mpChildrenToReparent;
     MediumLockList *mpMediumLockList;
 
@@ -579,5 +554,4 @@
     AutoCaller mTargetCaller;
     AutoCaller mParentForTargetCaller;
-    bool mfChildrenCaller;
     bool mfKeepMediumLockList;
 };
@@ -2123,10 +2097,8 @@
 }
 
-/**
- * @note @a aState may be NULL if the state value is not needed (only for
- *       in-process calls).
- */
-STDMETHODIMP Medium::LockRead(MediumState_T *aState)
-{
+STDMETHODIMP Medium::LockRead(IToken **aToken)
+{
+    CheckComArgNotNull(aToken);
+
     AutoCaller autoCaller(this);
     if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -2152,8 +2124,4 @@
     }
 
-    /* return the current state before */
-    if (aState)
-        *aState = m->state;
-
     HRESULT rc = S_OK;
 
@@ -2175,4 +2143,17 @@
             m->state = MediumState_LockedRead;
 
+            ComObjPtr<MediumLockToken> pToken;
+            rc = pToken.createObject();
+            if (SUCCEEDED(rc))
+                rc = pToken->init(this, false /* fWrite */);
+            if (FAILED(rc))
+            {
+                --m->readers;
+                if (m->readers == 0)
+                    m->state = m->preLockState;
+                return rc;
+            }
+
+            pToken.queryInterfaceTo(aToken);
             break;
         }
@@ -2192,5 +2173,5 @@
  *       in-process calls).
  */
-STDMETHODIMP Medium::UnlockRead(MediumState_T *aState)
+HRESULT Medium::unlockRead(MediumState_T *aState)
 {
     AutoCaller autoCaller(this);
@@ -2239,10 +2220,8 @@
 }
 
-/**
- * @note @a aState may be NULL if the state value is not needed (only for
- *       in-process calls).
- */
-STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
-{
+STDMETHODIMP Medium::LockWrite(IToken **aToken)
+{
+    CheckComArgNotNull(aToken);
+
     AutoCaller autoCaller(this);
     if (FAILED(autoCaller.rc())) return autoCaller.rc();
@@ -2268,8 +2247,4 @@
     }
 
-    /* return the current state before */
-    if (aState)
-        *aState = m->state;
-
     HRESULT rc = S_OK;
 
@@ -2283,4 +2258,16 @@
             LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
             m->state = MediumState_LockedWrite;
+
+            ComObjPtr<MediumLockToken> pToken;
+            rc = pToken.createObject();
+            if (SUCCEEDED(rc))
+                rc = pToken->init(this, true /* fWrite */);
+            if (FAILED(rc))
+            {
+                m->state = m->preLockState;
+                return rc;
+            }
+
+            pToken.queryInterfaceTo(aToken);
             break;
         }
@@ -2300,5 +2287,5 @@
  *       in-process calls).
  */
-STDMETHODIMP Medium::UnlockWrite(MediumState_T *aState)
+HRESULT Medium::unlockWrite(MediumState_T *aState)
 {
     AutoCaller autoCaller(this);
@@ -2538,5 +2525,5 @@
 
     HRESULT rc = S_OK;
-    ComObjPtr <Progress> pProgress;
+    ComObjPtr<Progress> pProgress;
     Medium::Task *pTask = NULL;
 
@@ -2694,5 +2681,5 @@
     alock.release();
 
-    ComObjPtr <Progress> pProgress;
+    ComObjPtr<Progress> pProgress;
 
     ULONG mediumVariantFlags = 0;
@@ -2728,5 +2715,5 @@
     bool fMergeForward = false;
     ComObjPtr<Medium> pParentForTarget;
-    MediaList childrenToReparent;
+    MediumLockList *pChildrenToReparent = NULL;
     MediumLockList *pMediumLockList = NULL;
 
@@ -2734,13 +2721,13 @@
 
     rc = prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
-                        pParentForTarget, childrenToReparent, pMediumLockList);
+                        pParentForTarget, pChildrenToReparent, pMediumLockList);
     if (FAILED(rc)) return rc;
 
-    ComObjPtr <Progress> pProgress;
-
-    rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent,
+    ComObjPtr<Progress> pProgress;
+
+    rc = mergeTo(pTarget, fMergeForward, pParentForTarget, pChildrenToReparent,
                  pMediumLockList, &pProgress, false /* aWait */);
     if (FAILED(rc))
-        cancelMergeTo(childrenToReparent, pMediumLockList);
+        cancelMergeTo(pChildrenToReparent, pMediumLockList);
     else
         pProgress.queryInterfaceTo(aProgress);
@@ -2915,5 +2902,5 @@
 
     HRESULT rc = S_OK;
-    ComObjPtr <Progress> pProgress;
+    ComObjPtr<Progress> pProgress;
     Medium::Task *pTask = NULL;
 
@@ -2988,5 +2975,5 @@
 
     HRESULT rc = S_OK;
-    ComObjPtr <Progress> pProgress;
+    ComObjPtr<Progress> pProgress;
     Medium::Task *pTask = NULL;
 
@@ -3061,5 +3048,5 @@
 
     HRESULT rc = S_OK;
-    ComObjPtr <Progress> pProgress;
+    ComObjPtr<Progress> pProgress;
     Medium::Task *pTask = NULL;
 
@@ -4665,6 +4652,7 @@
  * @param fMergeForward Resulting merge direction (out).
  * @param pParentForTarget New parent for target medium after merge (out).
- * @param aChildrenToReparent List of children of the source which will have
- *                      to be reparented to the target after merge (out).
+ * @param aChildrenToReparent Medium lock list containing all children of the
+ *                      source which will have to be reparented to the target
+ *                      after merge (out).
  * @param aMediumLockList Medium locking information (out).
  *
@@ -4678,5 +4666,5 @@
                                bool &fMergeForward,
                                ComObjPtr<Medium> &pParentForTarget,
-                               MediaList &aChildrenToReparent,
+                               MediumLockList * &aChildrenToReparent,
                                MediumLockList * &aMediumLockList)
 {
@@ -4693,5 +4681,6 @@
     fMergeForward = false;
     pParentForTarget.setNull();
-    aChildrenToReparent.clear();
+    Assert(aChildrenToReparent == NULL);
+    aChildrenToReparent = NULL;
     Assert(aMediumLockList == NULL);
     aMediumLockList = NULL;
@@ -4876,4 +4865,5 @@
         {
             /* we will need to reparent children of the source */
+            aChildrenToReparent = new MediumLockList();
             for (MediaList::const_iterator it = getChildren().begin();
                  it != getChildren().end();
@@ -4881,12 +4871,13 @@
             {
                 pMedium = *it;
-                if (fLockMedia)
-                {
-                    rc = pMedium->LockWrite(NULL);
-                    if (FAILED(rc))
-                        throw rc;
-                }
-
-                aChildrenToReparent.push_back(pMedium);
+                aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
+            }
+            if (fLockMedia && aChildrenToReparent)
+            {
+                treeLock.release();
+                rc = aChildrenToReparent->Lock();
+                treeLock.acquire();
+                if (FAILED(rc))
+                    throw rc;
             }
         }
@@ -4947,6 +4938,14 @@
     if (FAILED(rc))
     {
-        delete aMediumLockList;
-        aMediumLockList = NULL;
+        if (aMediumLockList)
+        {
+            delete aMediumLockList;
+            aMediumLockList = NULL;
+        }
+        if (aChildrenToReparent)
+        {
+            delete aChildrenToReparent;
+            aChildrenToReparent = NULL;
+        }
     }
 
@@ -5033,7 +5032,7 @@
                         bool fMergeForward,
                         const ComObjPtr<Medium> &pParentForTarget,
-                        const MediaList &aChildrenToReparent,
+                        MediumLockList *aChildrenToReparent,
                         MediumLockList *aMediumLockList,
-                        ComObjPtr <Progress> *aProgress,
+                        ComObjPtr<Progress> *aProgress,
                         bool aWait)
 {
@@ -5050,5 +5049,5 @@
 
     HRESULT rc = S_OK;
-    ComObjPtr <Progress> pProgress;
+    ComObjPtr<Progress> pProgress;
     Medium::Task *pTask = NULL;
 
@@ -5122,5 +5121,5 @@
  * @note Locks the media from the chain for writing.
  */
-void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
+void Medium::cancelMergeTo(MediumLockList *aChildrenToReparent,
                            MediumLockList *aMediumLockList)
 {
@@ -5154,14 +5153,8 @@
     delete aMediumLockList;
 
-    /* unlock the children which had to be reparented */
-    for (MediaList::const_iterator it = aChildrenToReparent.begin();
-         it != aChildrenToReparent.end();
-         ++it)
-    {
-        const ComObjPtr<Medium> &pMedium = *it;
-
-        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
-        pMedium->UnlockWrite(NULL);
-    }
+    /* unlock the children which had to be reparented, the destructor will do
+     * the work */
+    if (aChildrenToReparent)
+        delete aChildrenToReparent;
 }
 
@@ -5170,5 +5163,5 @@
  * parent.
  */
-HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
+HRESULT Medium::fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
 {
     Assert(!isWriteLockOnCurrentThread());
@@ -5211,14 +5204,17 @@
             }
 
-            for (MediaList::const_iterator it = childrenToReparent.begin();
-                 it != childrenToReparent.end();
+            MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
+            MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
+            for (MediumLockList::Base::iterator it = childrenBegin;
+                 it != childrenEnd;
                  ++it)
             {
+                Medium *pMedium = it->GetMedium();
                 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
                 vrc = VDOpen(hdd,
-                             (*it)->m->strFormat.c_str(),
-                             (*it)->m->strLocationFull.c_str(),
+                             pMedium->m->strFormat.c_str(),
+                             pMedium->m->strLocationFull.c_str(),
                              VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
-                             (*it)->m->vdImageIfaces);
+                             pMedium->m->vdImageIfaces);
                 if (RT_FAILURE(vrc))
                     throw vrc;
@@ -5231,6 +5227,4 @@
                 if (RT_FAILURE(vrc))
                     throw vrc;
-
-                (*it)->UnlockWrite(NULL);
             }
         }
@@ -5648,8 +5642,9 @@
     /* Lock the medium, which makes the behavior much more consistent */
     alock.release();
+    ComPtr<IToken> pToken;
     if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
-        rc = LockRead(NULL);
+        rc = LockRead(pToken.asOutParam());
     else
-        rc = LockWrite(NULL);
+        rc = LockWrite(pToken.asOutParam());
     if (FAILED(rc)) return rc;
     alock.acquire();
@@ -5989,11 +5984,7 @@
         m->preLockState = MediumState_Inaccessible;
 
-    HRESULT rc2;
-    if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
-        rc2 = UnlockRead(NULL);
-    else
-        rc2 = UnlockWrite(NULL);
-    if (SUCCEEDED(rc) && FAILED(rc2))
-        rc = rc2;
+    pToken->Abandon();
+    pToken.setNull();
+    
     if (FAILED(rc)) return rc;
 
@@ -6006,5 +5997,5 @@
     if (fRepairImageZeroParentUuid)
     {
-        rc = LockWrite(NULL);
+        rc = LockWrite(pToken.asOutParam());
         if (FAILED(rc)) return rc;
 
@@ -6044,7 +6035,6 @@
         }
 
-        rc = UnlockWrite(NULL);
-        if (SUCCEEDED(rc) && FAILED(rc2))
-            rc = rc2;
+        pToken->Abandon();
+        pToken.setNull();
         if (FAILED(rc)) return rc;
     }
@@ -7214,16 +7204,19 @@
                  * which cannot be part of the container at once so
                  * add each one in there individually */
-                if (task.mChildrenToReparent.size() > 0)
+                if (task.mpChildrenToReparent)
                 {
-                    for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
-                         it != task.mChildrenToReparent.end();
+                    MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
+                    MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
+                    for (MediumLockList::Base::iterator it = childrenBegin;
+                         it != childrenEnd;
                          ++it)
                     {
+                        Medium *pMedium = it->GetMedium();
                         /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
                         vrc = VDOpen(hdd,
-                                     (*it)->m->strFormat.c_str(),
-                                     (*it)->m->strLocationFull.c_str(),
+                                     pMedium->m->strFormat.c_str(),
+                                     pMedium->m->strLocationFull.c_str(),
                                      VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
-                                     (*it)->m->vdImageIfaces);
+                                     pMedium->m->vdImageIfaces);
                         if (RT_FAILURE(vrc))
                             throw vrc;
@@ -7237,6 +7230,4 @@
                         if (RT_FAILURE(vrc))
                             throw vrc;
-
-                        (*it)->UnlockWrite(NULL);
                     }
                 }
@@ -7301,14 +7292,16 @@
             /* reparent source's children and disconnect the deleted
              * branch at the younger end */
-            if (task.mChildrenToReparent.size() > 0)
+            if (task.mpChildrenToReparent)
             {
                 /* obey {parent,child} lock order */
                 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
 
-                for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
-                     it != task.mChildrenToReparent.end();
-                     it++)
+                MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
+                MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
+                for (MediumLockList::Base::iterator it = childrenBegin;
+                     it != childrenEnd;
+                     ++it)
                 {
-                    Medium *pMedium = *it;
+                    Medium *pMedium = it->GetMedium();
                     AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
 
@@ -7396,8 +7389,5 @@
          * don't own the merge chain, so release it in this case. */
         if (task.isAsync())
-        {
-            Assert(task.mChildrenToReparent.size() == 0);
-            cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
-        }
+            cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
     }
 
Index: /trunk/src/VBox/Main/src-server/MediumLock.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/MediumLock.cpp	(revision 48296)
+++ /trunk/src/VBox/Main/src-server/MediumLock.cpp	(revision 48297)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2010 Oracle Corporation
+ * Copyright (C) 2010-2013 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -75,5 +75,10 @@
 }
 
-HRESULT MediumLock::Lock()
+bool MediumLock::IsLocked() const
+{
+    return mIsLocked;
+}
+
+HRESULT MediumLock::Lock(bool aIgnoreLockedMedia)
 {
     if (mIsLocked)
@@ -102,7 +107,18 @@
         default:
             if (mLockWrite)
-                rc = mMedium->LockWrite(NULL);
+            {
+                if (aIgnoreLockedMedia && (   state == MediumState_LockedRead
+                                           || state == MediumState_LockedWrite))
+                    return S_OK;
+                else
+                    rc = mMedium->LockWrite(mToken.asOutParam());
+            }
             else
-                rc = mMedium->LockRead(NULL);
+            {
+                if (aIgnoreLockedMedia && state == MediumState_LockedWrite)
+                    return S_OK;
+                else
+                    rc = mMedium->LockRead(mToken.asOutParam());
+            }
     }
     if (SUCCEEDED(rc))
@@ -121,10 +137,8 @@
 {
     HRESULT rc = S_OK;
-    if (mIsLocked && !mLockSkipped)
-    {
-        if (mLockWrite)
-            rc = mMedium->UnlockWrite(NULL);
-        else
-            rc = mMedium->UnlockRead(NULL);
+    if (mIsLocked && !mLockSkipped && mToken)
+    {
+        mToken->Abandon();
+        mToken.setNull();
     }
     mMediumCaller.attach(NULL);
@@ -202,5 +216,5 @@
 }
 
-HRESULT MediumLockList::Lock()
+HRESULT MediumLockList::Lock(bool fSkipOverLockedMedia /* = false */)
 {
     if (mIsLocked)
@@ -211,5 +225,5 @@
          it++)
     {
-        rc = it->Lock();
+        rc = it->Lock(fSkipOverLockedMedia);
         if (FAILED(rc))
         {
Index: /trunk/src/VBox/Main/src-server/SnapshotImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/SnapshotImpl.cpp	(revision 48296)
+++ /trunk/src/VBox/Main/src-server/SnapshotImpl.cpp	(revision 48297)
@@ -2104,5 +2104,5 @@
 
 /**
- * Implementation for IInternalMachineControl::deleteSnapshot().
+ * Implementation for IInternalMachineControl::DeleteSnapshot().
  *
  * Gets called from Console::DeleteSnapshot(), and that's basically the
@@ -2294,7 +2294,8 @@
                     bool fMergeForward,
                     const ComObjPtr<Medium> &aParentForTarget,
-                    const MediaList &aChildrenToReparent,
+                    MediumLockList *aChildrenToReparent,
                     bool fNeedsOnlineMerge,
-                    MediumLockList *aMediumLockList)
+                    MediumLockList *aMediumLockList,
+                    const ComPtr<IToken> &aHDLockToken)
         : mpHD(aHd),
           mpSource(aSource),
@@ -2303,7 +2304,8 @@
           mfMergeForward(fMergeForward),
           mpParentForTarget(aParentForTarget),
-          mChildrenToReparent(aChildrenToReparent),
+          mpChildrenToReparent(aChildrenToReparent),
           mfNeedsOnlineMerge(fNeedsOnlineMerge),
-          mpMediumLockList(aMediumLockList)
+          mpMediumLockList(aMediumLockList),
+          mpHDLockToken(aHDLockToken)
     {}
 
@@ -2314,7 +2316,8 @@
                     bool fMergeForward,
                     const ComObjPtr<Medium> &aParentForTarget,
-                    const MediaList &aChildrenToReparent,
+                    MediumLockList *aChildrenToReparent,
                     bool fNeedsOnlineMerge,
                     MediumLockList *aMediumLockList,
+                    const ComPtr<IToken> &aHDLockToken,
                     const Guid &aMachineId,
                     const Guid &aSnapshotId)
@@ -2325,7 +2328,8 @@
           mfMergeForward(fMergeForward),
           mpParentForTarget(aParentForTarget),
-          mChildrenToReparent(aChildrenToReparent),
+          mpChildrenToReparent(aChildrenToReparent),
           mfNeedsOnlineMerge(fNeedsOnlineMerge),
           mpMediumLockList(aMediumLockList),
+          mpHDLockToken(aHDLockToken),
           mMachineId(aMachineId),
           mSnapshotId(aSnapshotId)
@@ -2338,7 +2342,9 @@
     bool mfMergeForward;
     ComObjPtr<Medium> mpParentForTarget;
-    MediaList mChildrenToReparent;
+    MediumLockList *mpChildrenToReparent;
     bool mfNeedsOnlineMerge;
     MediumLockList *mpMediumLockList;
+    /** optional lock token, used only in case mpHD is not merged/deleted */
+    ComPtr<IToken> mpHDLockToken;
     /* these are for reattaching the hard disk in case of a failure: */
     Guid mMachineId;
@@ -2450,9 +2456,10 @@
             bool fMergeForward = false;
             ComObjPtr<Medium> pParentForTarget;
-            MediaList childrenToReparent;
+            MediumLockList *pChildrenToReparent = NULL;
             bool fNeedsOnlineMerge = false;
             bool fOnlineMergePossible = aTask.m_fDeleteOnline;
             MediumLockList *pMediumLockList = NULL;
             MediumLockList *pVMMALockList = NULL;
+            ComPtr<IToken> pHDLockToken;
             ComObjPtr<MediumAttachment> pOnlineMediumAttachment;
             if (fOnlineMergePossible)
@@ -2487,7 +2494,8 @@
                                              pVMMALockList, pSource, pTarget,
                                              fMergeForward, pParentForTarget,
-                                             childrenToReparent,
+                                             pChildrenToReparent,
                                              fNeedsOnlineMerge,
-                                             pMediumLockList);
+                                             pMediumLockList,
+                                             pHDLockToken);
             treeLock.acquire();
             if (FAILED(rc))
@@ -2545,7 +2553,8 @@
                                                    fMergeForward,
                                                    pParentForTarget,
-                                                   childrenToReparent,
+                                                   pChildrenToReparent,
                                                    fNeedsOnlineMerge,
                                                    pMediumLockList,
+                                                   pHDLockToken,
                                                    replaceMachineId,
                                                    replaceSnapshotId));
@@ -2556,7 +2565,8 @@
                                                    fMergeForward,
                                                    pParentForTarget,
-                                                   childrenToReparent,
+                                                   pChildrenToReparent,
                                                    fNeedsOnlineMerge,
-                                                   pMediumLockList));
+                                                   pMediumLockList,
+                                                   pHDLockToken));
         }
 
@@ -2756,4 +2766,9 @@
                 if (it->mfNeedsOnlineMerge)
                 {
+                    // Put the medium merge information (MediumDeleteRec) where
+                    // SessionMachine::FinishOnlineMergeMedium can get at it.
+                    // This callback will arrive while onlineMergeMedium is
+                    // still executing, and there can't be two tasks.
+                    mConsoleTaskData.mDeleteSnapshotInfo = (void *)&(*it);
                     // online medium merge, in the direction decided earlier
                     rc = onlineMergeMedium(it->mpOnlineMediumAttachment,
@@ -2762,8 +2777,9 @@
                                            it->mfMergeForward,
                                            it->mpParentForTarget,
-                                           it->mChildrenToReparent,
+                                           it->mpChildrenToReparent,
                                            it->mpMediumLockList,
                                            aTask.pProgress,
                                            &fNeedsSave);
+                    mConsoleTaskData.mDeleteSnapshotInfo = NULL;
                 }
                 else
@@ -2773,5 +2789,5 @@
                                                it->mfMergeForward,
                                                it->mpParentForTarget,
-                                               it->mChildrenToReparent,
+                                               it->mpChildrenToReparent,
                                                it->mpMediumLockList,
                                                &aTask.pProgress,
@@ -2912,8 +2928,8 @@
         {
             cancelDeleteSnapshotMedium(it->mpHD, it->mpSource,
-                                       it->mChildrenToReparent,
+                                       it->mpChildrenToReparent,
                                        it->mfNeedsOnlineMerge,
-                                       it->mpMediumLockList, it->mMachineId,
-                                       it->mSnapshotId);
+                                       it->mpMediumLockList, it->mpHDLockToken,
+                                       it->mMachineId, it->mSnapshotId);
         }
     }
@@ -2966,6 +2982,6 @@
  * @param aMergeForward Merge direction decision (out).
  * @param aParentForTarget New parent if target needs to be reparented (out).
- * @param aChildrenToReparent Children which have to be reparented to the
- *                      target (out).
+ * @param aChildrenToReparent MediumLockList with children which have to be
+ *                      reparented to the target (out).
  * @param fNeedsOnlineMerge Whether this merge needs to be done online (out).
  *                      If this is set to @a true then the @a aVMMALockList
@@ -2974,4 +2990,6 @@
  * @param aMediumLockList Where to store the created medium lock list (may
  *                      return NULL if no real merge is necessary).
+ * @param aHDLockToken  Where to store the write lock token for aHD, in case
+ *                      it is not merged or deleted (out).
  *
  * @note Caller must hold media tree lock for writing. This locks this object
@@ -2987,7 +3005,8 @@
                                                     bool &aMergeForward,
                                                     ComObjPtr<Medium> &aParentForTarget,
-                                                    MediaList &aChildrenToReparent,
+                                                    MediumLockList * &aChildrenToReparent,
                                                     bool &fNeedsOnlineMerge,
-                                                    MediumLockList * &aMediumLockList)
+                                                    MediumLockList * &aMediumLockList,
+                                                    ComPtr<IToken> &aHDLockToken)
 {
     Assert(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
@@ -3002,4 +3021,5 @@
                  && type != MediumType_Readonly, E_FAIL);
 
+    aChildrenToReparent = NULL;
     aMediumLockList = NULL;
     fNeedsOnlineMerge = false;
@@ -3018,5 +3038,5 @@
              * is completed */
             alock.release();
-            return aHD->LockWrite(NULL);
+            return aHD->LockWrite(aHDLockToken.asOutParam());
         }
 
@@ -3049,5 +3069,5 @@
             childLock.release();
             alock.release();
-            return aHD->LockWrite(NULL);
+            return aHD->LockWrite(aHDLockToken.asOutParam());
         }
 
@@ -3146,37 +3166,35 @@
             {
                 /* we will lock the children of the source for reparenting */
-                for (MediaList::const_iterator it = aChildrenToReparent.begin();
-                     it != aChildrenToReparent.end();
-                     ++it)
+                if (aChildrenToReparent && !aChildrenToReparent->IsEmpty())
                 {
-                    ComObjPtr<Medium> pMedium = *it;
-                    AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
-                    if (pMedium->getState() == MediumState_Created)
+                    /* Cannot just call aChildrenToReparent->Lock(), as one of
+                     * the children is the one under which the current state of
+                     * the VM is located, and this means it is already locked
+                     * (for reading). Note that no special unlocking is needed,
+                     * because cancelMergeTo will unlock everything locked in
+                     * its context (using the unlock on destruction), and both
+                     * cancelDeleteSnapshotMedium (in case something fails) and
+                     * FinishOnlineMergeMedium re-define the read/write lock
+                     * state of everything which the VM need, search for the
+                     * UpdateLock method calls. */
+                    childLock.release();
+                    alock.release();
+                    rc = aChildrenToReparent->Lock(true /* fSkipOverLockedMedia */);
+                    alock.acquire();
+                    childLock.acquire();
+                    MediumLockList::Base::iterator childrenToReparentBegin = aChildrenToReparent->GetBegin();
+                    MediumLockList::Base::iterator childrenToReparentEnd = aChildrenToReparent->GetEnd();
+                    for (MediumLockList::Base::iterator it = childrenToReparentBegin;
+                         it != childrenToReparentEnd;
+                         ++it)
                     {
-                        mediumLock.release();
-                        childLock.release();
-                        alock.release();
-                        rc = pMedium->LockWrite(NULL);
-                        alock.acquire();
-                        childLock.acquire();
-                        mediumLock.acquire();
-                        if (FAILED(rc))
-                            throw rc;
-                    }
-                    else
-                    {
-                        mediumLock.release();
-                        childLock.release();
-                        alock.release();
-                        rc = aVMMALockList->Update(pMedium, true);
-                        alock.acquire();
-                        childLock.acquire();
-                        mediumLock.acquire();
-                        if (FAILED(rc))
+                        ComObjPtr<Medium> pMedium = it->GetMedium();
+                        AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
+                        if (!it->IsLocked())
                         {
                             mediumLock.release();
                             childLock.release();
                             alock.release();
-                            rc = pMedium->LockWrite(NULL);
+                            rc = aVMMALockList->Update(pMedium, true);
                             alock.acquire();
                             childLock.acquire();
@@ -3265,4 +3283,5 @@
  * @param fNeedsOnlineMerge Whether this merge needs to be done online.
  * @param aMediumLockList Medium locks to cancel.
+ * @param aHDLockToken  Optional write lock token for aHD.
  * @param aMachineId    Machine id to attach the medium to.
  * @param aSnapshotId   Snapshot id to attach the medium to.
@@ -3272,7 +3291,8 @@
 void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD,
                                                 const ComObjPtr<Medium> &aSource,
-                                                const MediaList &aChildrenToReparent,
+                                                MediumLockList *aChildrenToReparent,
                                                 bool fNeedsOnlineMerge,
                                                 MediumLockList *aMediumLockList,
+                                                const ComPtr<IToken> &aHDLockToken,
                                                 const Guid &aMachineId,
                                                 const Guid &aSnapshotId)
@@ -3286,6 +3306,10 @@
         if (aHD->getParent().isNull())
         {
-            HRESULT rc = aHD->UnlockWrite(NULL);
-            AssertComRC(rc);
+            Assert(!aHDLockToken.isNull());
+            if (!aHDLockToken.isNull())
+            {
+                HRESULT rc = aHDLockToken->Abandon();
+                AssertComRC(rc);
+            }
         }
         else
@@ -3354,6 +3378,6 @@
  * @param aMergeForward Merge direction.
  * @param aParentForTarget New parent if target needs to be reparented.
- * @param aChildrenToReparent Children which have to be reparented to the
- *                      target.
+ * @param aChildrenToReparent Medium lock list with children which have to be
+ *                      reparented to the target.
  * @param aMediumLockList Where to store the created medium lock list (may
  *                      return NULL if no real merge is necessary).
@@ -3366,5 +3390,5 @@
                                           bool fMergeForward,
                                           const ComObjPtr<Medium> &aParentForTarget,
-                                          const MediaList &aChildrenToReparent,
+                                          MediumLockList *aChildrenToReparent,
                                           MediumLockList *aMediumLockList,
                                           ComObjPtr<Progress> &aProgress,
@@ -3415,10 +3439,4 @@
                        && uTargetIdx != (unsigned)-1, E_FAIL);
 
-        // For forward merges, tell the VM what images need to have their
-        // parent UUID updated. This cannot be done in VBoxSVC, as opening
-        // the required parent images is not safe while the VM is running.
-        // For backward merges this will be simply an array of size 0.
-        com::SafeIfaceArray<IMedium> childrenToReparent(aChildrenToReparent);
-
         ComPtr<IInternalSessionControl> directControl;
         {
@@ -3436,7 +3454,4 @@
         rc = directControl->OnlineMergeMedium(aMediumAttachment,
                                               uSourceIdx, uTargetIdx,
-                                              aSource, aTarget,
-                                              fMergeForward, aParentForTarget,
-                                              ComSafeArrayAsInParam(childrenToReparent),
                                               aProgress);
         if (FAILED(rc))
@@ -3454,5 +3469,5 @@
 
 /**
- * Implementation for IInternalMachineControl::finishOnlineMergeMedium().
+ * Implementation for IInternalMachineControl::FinishOnlineMergeMedium().
  *
  * Gets called after the successful completion of an online merge from
@@ -3463,15 +3478,9 @@
  * can continue with the updated state of the medium chain.
  */
-STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumAttachment,
-                                                     IMedium *aSource,
-                                                     IMedium *aTarget,
-                                                     BOOL aMergeForward,
-                                                     IMedium *aParentForTarget,
-                                                     ComSafeArrayIn(IMedium *, aChildrenToReparent))
+STDMETHODIMP SessionMachine::FinishOnlineMergeMedium()
 {
     HRESULT rc = S_OK;
-    ComObjPtr<Medium> pSource(static_cast<Medium *>(aSource));
-    ComObjPtr<Medium> pTarget(static_cast<Medium *>(aTarget));
-    ComObjPtr<Medium> pParentForTarget(static_cast<Medium *>(aParentForTarget));
+    MediumDeleteRec *pDeleteRec = (MediumDeleteRec *)mConsoleTaskData.mDeleteSnapshotInfo;
+    AssertReturn(pDeleteRec, E_FAIL);
     bool fSourceHasChildren = false;
 
@@ -3486,26 +3495,26 @@
     ComObjPtr<Medium> targetChild;
 
-    if (aMergeForward)
+    if (pDeleteRec->mfMergeForward)
     {
         // first, unregister the target since it may become a base
         // hard disk which needs re-registration
-        rc = mParent->unregisterMedium(pTarget);
+        rc = mParent->unregisterMedium(pDeleteRec->mpTarget);
         AssertComRC(rc);
 
         // then, reparent it and disconnect the deleted branch at
         // both ends (chain->parent() is source's parent)
-        pTarget->deparent();
-        pTarget->setParent(pParentForTarget);
-        if (pParentForTarget)
-            pSource->deparent();
+        pDeleteRec->mpTarget->deparent();
+        pDeleteRec->mpTarget->setParent(pDeleteRec->mpParentForTarget);
+        if (pDeleteRec->mpParentForTarget)
+            pDeleteRec->mpSource->deparent();
 
         // then, register again
-        rc = mParent->registerMedium(pTarget, &pTarget, DeviceType_HardDisk);
+        rc = mParent->registerMedium(pDeleteRec->mpTarget, &pDeleteRec->mpTarget, DeviceType_HardDisk);
         AssertComRC(rc);
     }
     else
     {
-        Assert(pTarget->getChildren().size() == 1);
-        targetChild = pTarget->getChildren().front();
+        Assert(pDeleteRec->mpTarget->getChildren().size() == 1);
+        targetChild = pDeleteRec->mpTarget->getChildren().front();
 
         // disconnect the deleted branch at the elder end
@@ -3514,6 +3523,5 @@
         // Update parent UUIDs of the source's children, reparent them and
         // disconnect the deleted branch at the younger end
-        com::SafeIfaceArray<IMedium> childrenToReparent(ComSafeArrayInArg(aChildrenToReparent));
-        if (childrenToReparent.size() > 0)
+        if (pDeleteRec->mpChildrenToReparent && !pDeleteRec->mpChildrenToReparent->IsEmpty())
         {
             fSourceHasChildren = true;
@@ -3523,24 +3531,25 @@
             // we must continue. The worst possible result is that the images
             // need manual fixing via VBoxManage to adjust the parent UUID.
-            MediaList toReparent;
-            for (size_t i = 0; i < childrenToReparent.size(); i++)
+            treeLock.release();
+            pDeleteRec->mpTarget->fixParentUuidOfChildren(pDeleteRec->mpChildrenToReparent);
+            // The childen are still write locked, unlock them now and don't
+            // rely on the destructor doing it very late.
+            pDeleteRec->mpChildrenToReparent->Unlock();
+            treeLock.acquire();
+
+            // obey {parent,child} lock order
+            AutoWriteLock sourceLock(pDeleteRec->mpSource COMMA_LOCKVAL_SRC_POS);
+
+            MediumLockList::Base::iterator childrenBegin = pDeleteRec->mpChildrenToReparent->GetBegin();
+            MediumLockList::Base::iterator childrenEnd = pDeleteRec->mpChildrenToReparent->GetEnd();
+            for (MediumLockList::Base::iterator it = childrenBegin;
+                 it != childrenEnd;
+                 ++it)
             {
-                Medium *pMedium = static_cast<Medium *>(childrenToReparent[i]);
-                toReparent.push_back(pMedium);
-            }
-            treeLock.release();
-            pTarget->fixParentUuidOfChildren(toReparent);
-            treeLock.acquire();
-
-            // obey {parent,child} lock order
-            AutoWriteLock sourceLock(pSource COMMA_LOCKVAL_SRC_POS);
-
-            for (size_t i = 0; i < childrenToReparent.size(); i++)
-            {
-                Medium *pMedium = static_cast<Medium *>(childrenToReparent[i]);
+                Medium *pMedium = it->GetMedium();
                 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
 
                 pMedium->deparent();  // removes pMedium from source
-                pMedium->setParent(pTarget);
+                pMedium->setParent(pDeleteRec->mpTarget);
             }
         }
@@ -3549,7 +3558,6 @@
     /* unregister and uninitialize all hard disks removed by the merge */
     MediumLockList *pMediumLockList = NULL;
-    MediumAttachment *pMediumAttachment = static_cast<MediumAttachment *>(aMediumAttachment);
-    rc = mData->mSession.mLockedMedia.Get(pMediumAttachment, pMediumLockList);
-    const ComObjPtr<Medium> &pLast = aMergeForward ? pTarget : pSource;
+    rc = mData->mSession.mLockedMedia.Get(pDeleteRec->mpOnlineMediumAttachment, pMediumLockList);
+    const ComObjPtr<Medium> &pLast = pDeleteRec->mfMergeForward ? pDeleteRec->mpTarget : pDeleteRec->mpSource;
     AssertReturn(SUCCEEDED(rc) && pMediumLockList, E_FAIL);
     MediumLockList::Base::iterator lockListBegin =
@@ -3567,5 +3575,5 @@
 
         /* The target and all images not merged (readonly) are skipped */
-        if (   pMedium == pTarget
+        if (   pMedium == pDeleteRec->mpTarget
             || pMedium->getState() == MediumState_LockedRead)
         {
@@ -3589,8 +3597,8 @@
              * and therefore we cannot uninit() it (it's therefore
              * the caller's responsibility) */
-            if (pMedium == aSource)
+            if (pMedium == pDeleteRec->mpSource)
             {
-                Assert(pSource->getChildren().size() == 0);
-                Assert(pSource->getFirstMachineBackrefId() == NULL);
+                Assert(pDeleteRec->mpSource->getChildren().size() == 0);
+                Assert(pDeleteRec->mpSource->getFirstMachineBackrefId() == NULL);
             }
 
@@ -3628,8 +3636,8 @@
      * attachment, as the previously associated one (source) is now deleted.
      * Without the immediate update the VM could not continue running. */
-    if (!aMergeForward && !fSourceHasChildren)
-    {
-        AutoWriteLock attLock(pMediumAttachment COMMA_LOCKVAL_SRC_POS);
-        pMediumAttachment->updateMedium(pTarget);
+    if (!pDeleteRec->mfMergeForward && !fSourceHasChildren)
+    {
+        AutoWriteLock attLock(pDeleteRec->mpOnlineMediumAttachment COMMA_LOCKVAL_SRC_POS);
+        pDeleteRec->mpOnlineMediumAttachment->updateMedium(pDeleteRec->mpTarget);
     }
 
Index: /trunk/src/VBox/Main/src-server/TokenImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/TokenImpl.cpp	(revision 48297)
+++ /trunk/src/VBox/Main/src-server/TokenImpl.cpp	(revision 48297)
@@ -0,0 +1,217 @@
+/* $Id$ */
+/** @file
+ *
+ * Token COM class implementation: MachineToken and MediumLockToken
+ */
+
+/*
+ * Copyright (C) 2013 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "TokenImpl.h"
+#include "MachineImpl.h"
+#include "MediumImpl.h"
+#include "AutoCaller.h"
+#include "Logging.h"
+
+// constructor / destructor
+/////////////////////////////////////////////////////////////////////////////
+
+DEFINE_EMPTY_CTOR_DTOR(MachineToken)
+
+HRESULT MachineToken::FinalConstruct()
+{
+    return BaseFinalConstruct();
+}
+
+void MachineToken::FinalRelease()
+{
+    uninit();
+
+    BaseFinalRelease();
+}
+
+// public initializer/uninitializer for internal purposes only
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Initializes the token object.
+ *
+ * @param pSessionMachine   Pointer to a SessionMachine object.
+ */
+HRESULT MachineToken::init(const ComObjPtr<SessionMachine> &pSessionMachine)
+{
+    LogFlowThisFunc(("pSessionMachine=%p\n", *pSessionMachine));
+
+    ComAssertRet(!pSessionMachine.isNull(), E_INVALIDARG);
+
+    /* Enclose the state transition NotReady->InInit->Ready */
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    m.pSessionMachine = pSessionMachine;
+
+    /* Confirm a successful initialization */
+    autoInitSpan.setSucceeded();
+
+    return S_OK;
+}
+
+/**
+ * Uninitializes the instance and sets the ready flag to FALSE.
+ * Called either from FinalRelease() or by the parent when it gets destroyed.
+ */
+void MachineToken::uninit()
+{
+    LogFlowThisFunc(("\n"));
+
+    /* Enclose the state transition Ready->InUninit->NotReady */
+    AutoUninitSpan autoUninitSpan(this);
+    if (autoUninitSpan.uninitDone())
+        return;
+
+    /* Destroy the SessionMachine object, check is paranoia */
+    if (!m.pSessionMachine.isNull())
+    {
+        m.pSessionMachine->uninit();
+        m.pSessionMachine.setNull();
+    }
+}
+
+// IToken methods
+/////////////////////////////////////////////////////////////////////////////
+
+HRESULT MachineToken::abandon(AutoCaller &aAutoCaller)
+{
+    /* have to release the AutoCaller before calling uninit(), self-deadlock */
+    aAutoCaller.release();
+
+    /* uninit does everything we need */
+    uninit();
+    return S_OK;
+}
+
+HRESULT MachineToken::dummy()
+{
+    /* Remember, the wrapper contains the AutoCaller, which means that after
+     * uninit() this code won't be reached any more. */
+
+    /* this is a NOOP, no need to lock */
+
+    return S_OK;
+}
+
+// public methods only for internal purposes
+/////////////////////////////////////////////////////////////////////////////
+
+
+// constructor / destructor
+/////////////////////////////////////////////////////////////////////////////
+
+DEFINE_EMPTY_CTOR_DTOR(MediumLockToken)
+
+HRESULT MediumLockToken::FinalConstruct()
+{
+    return BaseFinalConstruct();
+}
+
+void MediumLockToken::FinalRelease()
+{
+    uninit();
+
+    BaseFinalRelease();
+}
+
+// public initializer/uninitializer for internal purposes only
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Initializes the token object.
+ *
+ * @param pMedium   Pointer to a Medium object.
+ * @param fWrite    True if this is a write lock, false otherwise.
+ */
+HRESULT MediumLockToken::init(const ComObjPtr<Medium> &pMedium, bool fWrite)
+{
+    LogFlowThisFunc(("pMedium=%p\n", *pMedium));
+
+    ComAssertRet(!pMedium.isNull(), E_INVALIDARG);
+
+    /* Enclose the state transition NotReady->InInit->Ready */
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    m.pMedium = pMedium;
+    m.fWrite = fWrite;
+
+    /* Confirm a successful initialization */
+    autoInitSpan.setSucceeded();
+
+    return S_OK;
+}
+
+/**
+ * Uninitializes the instance and sets the ready flag to FALSE.
+ * Called either from FinalRelease() or by the parent when it gets destroyed.
+ */
+void MediumLockToken::uninit()
+{
+    LogFlowThisFunc(("\n"));
+
+    /* Enclose the state transition Ready->InUninit->NotReady */
+    AutoUninitSpan autoUninitSpan(this);
+    if (autoUninitSpan.uninitDone())
+        return;
+
+    /* Release the appropriate lock, check is paranoia */
+    if (!m.pMedium.isNull())
+    {
+        if (m.fWrite)
+        {
+            HRESULT rc = m.pMedium->unlockWrite(NULL);
+            AssertComRC(rc);
+        }
+        else
+        {
+            HRESULT rc = m.pMedium->unlockRead(NULL);
+            AssertComRC(rc);
+        }
+        m.pMedium.setNull();
+    }
+}
+
+// IToken methods
+/////////////////////////////////////////////////////////////////////////////
+
+HRESULT MediumLockToken::abandon(AutoCaller &aAutoCaller)
+{
+    /* have to release the AutoCaller before calling uninit(), self-deadlock */
+    aAutoCaller.release();
+
+    /* uninit does everything we need */
+    uninit();
+    return S_OK;
+}
+
+HRESULT MediumLockToken::dummy()
+{
+    /* Remember, the wrapper contains the AutoCaller, which means that after
+     * uninit() this code won't be reached any more. */
+
+    /* this is a NOOP, no need to lock */
+
+    return S_OK;
+}
+
+// public methods only for internal purposes
+/////////////////////////////////////////////////////////////////////////////
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Index: /trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp	(revision 48296)
+++ /trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp	(revision 48297)
@@ -4732,4 +4732,16 @@
 
 /**
+ * Returns the lock handle which protects the machines list. As opposed
+ * to version 3.1 and earlier, these lists are no longer protected by the
+ * VirtualBox lock, but by this more specialized lock. Mind the locking
+ * order: always request this lock after the VirtualBox object lock but
+ * before the locks of any machine object. See AutoLock.h.
+ */
+RWLockHandle& VirtualBox::getMachinesListLockHandle()
+{
+    return m->lockMachines;
+}
+
+/**
  * Returns the lock handle which protects the media trees (hard disks,
  * DVDs, floppies). As opposed to version 3.1 and earlier, these lists
Index: /trunk/src/VBox/Main/testcase/tstMediumLock.cpp
===================================================================
--- /trunk/src/VBox/Main/testcase/tstMediumLock.cpp	(revision 48296)
+++ /trunk/src/VBox/Main/testcase/tstMediumLock.cpp	(revision 48297)
@@ -123,11 +123,12 @@
     {
         RTTestSub(hTest, "Write locks");
-
-        MediumState_T mediumState = MediumState_NotCreated;
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
-        if (mediumState != MediumState_Created)
-            RTTestFailed(hTest, "wrong medium state %d", mediumState);
-
-        TEST_COM_SUCCESS(hTest, pMedium->LockWrite(&mediumState), "write lock");
+	ComPtr<IToken> pToken1, pToken2;
+
+        MediumState_T mediumState = MediumState_NotCreated;
+        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+        if (mediumState != MediumState_Created)
+            RTTestFailed(hTest, "wrong medium state %d", mediumState);
+
+        TEST_COM_SUCCESS(hTest, pMedium->LockWrite(pToken1.asOutParam()), "write lock");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock write state");
@@ -135,5 +136,7 @@
             RTTestFailed(hTest, "wrong lock write medium state %d", mediumState);
 
-        TEST_COM_FAILURE(hTest, pMedium->LockWrite(&mediumState), "nested write lock succeeded");
+        TEST_COM_FAILURE(hTest, pMedium->LockWrite(pToken2.asOutParam()), "nested write lock succeeded");
+	if (!pToken2.isNull())
+            RTTestFailed(hTest, "pToken2 is not null");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock write state");
@@ -141,5 +144,8 @@
             RTTestFailed(hTest, "wrong after nested lock write medium state %d", mediumState);
 
-        TEST_COM_SUCCESS(hTest, pMedium->UnlockWrite(&mediumState), "write unlock");
+	if (!pToken1.isNull())
+            TEST_COM_SUCCESS(hTest, pToken1->Abandon(), "write unlock");
+	else
+            RTTestFailed(hTest, "pToken1 is null");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock write state");
@@ -151,11 +157,12 @@
     {
         RTTestSub(hTest, "Read locks");
-
-        MediumState_T mediumState = MediumState_NotCreated;
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
-        if (mediumState != MediumState_Created)
-            RTTestFailed(hTest, "wrong medium state %d", mediumState);
-
-        TEST_COM_SUCCESS(hTest, pMedium->LockRead(&mediumState), "read lock");
+	ComPtr<IToken> pToken1, pToken2;
+
+        MediumState_T mediumState = MediumState_NotCreated;
+        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+        if (mediumState != MediumState_Created)
+            RTTestFailed(hTest, "wrong medium state %d", mediumState);
+
+        TEST_COM_SUCCESS(hTest, pMedium->LockRead(pToken1.asOutParam()), "read lock");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock read state");
@@ -163,5 +170,5 @@
             RTTestFailed(hTest, "wrong lock read medium state %d", mediumState);
 
-        TEST_COM_SUCCESS(hTest, pMedium->LockRead(&mediumState), "nested read lock failed");
+        TEST_COM_SUCCESS(hTest, pMedium->LockRead(pToken2.asOutParam()), "nested read lock failed");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock read state");
@@ -169,5 +176,8 @@
             RTTestFailed(hTest, "wrong after nested lock read medium state %d", mediumState);
 
-        TEST_COM_SUCCESS(hTest, pMedium->UnlockRead(&mediumState), "read nested unlock");
+	if (!pToken2.isNull())
+            TEST_COM_SUCCESS(hTest, pToken2->Abandon(), "read nested unlock");
+	else
+            RTTestFailed(hTest, "pToken2 is null");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock read state");
@@ -175,5 +185,8 @@
             RTTestFailed(hTest, "wrong after nested lock read medium state %d", mediumState);
 
-        TEST_COM_SUCCESS(hTest, pMedium->UnlockRead(&mediumState), "read unlock");
+	if (!pToken1.isNull())
+            TEST_COM_SUCCESS(hTest, pToken1->Abandon(), "read nested unlock");
+	else
+            RTTestFailed(hTest, "pToken1 is null");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock read state");
@@ -185,11 +198,12 @@
     {
         RTTestSub(hTest, "Mixing write and read locks");
-
-        MediumState_T mediumState = MediumState_NotCreated;
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
-        if (mediumState != MediumState_Created)
-            RTTestFailed(hTest, "wrong medium state %d", mediumState);
-
-        TEST_COM_SUCCESS(hTest, pMedium->LockWrite(&mediumState), "write lock");
+	ComPtr<IToken> pToken1, pToken2;
+
+        MediumState_T mediumState = MediumState_NotCreated;
+        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+        if (mediumState != MediumState_Created)
+            RTTestFailed(hTest, "wrong medium state %d", mediumState);
+
+        TEST_COM_SUCCESS(hTest, pMedium->LockWrite(pToken1.asOutParam()), "write lock");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock write state");
@@ -197,5 +211,7 @@
             RTTestFailed(hTest, "wrong lock write medium state %d", mediumState);
 
-        TEST_COM_FAILURE(hTest, pMedium->LockRead(&mediumState), "write+read lock succeeded");
+        TEST_COM_FAILURE(hTest, pMedium->LockRead(pToken2.asOutParam()), "write+read lock succeeded");
+	if (!pToken2.isNull())
+            RTTestFailed(hTest, "pToken2 is not null");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock write state");
@@ -203,5 +219,8 @@
             RTTestFailed(hTest, "wrong after nested lock write medium state %d", mediumState);
 
-        TEST_COM_SUCCESS(hTest, pMedium->UnlockWrite(&mediumState), "write unlock");
+	if (!pToken1.isNull())
+            TEST_COM_SUCCESS(hTest, pToken1->Abandon(), "write unlock");
+	else
+            RTTestFailed(hTest, "pToken1 is null");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock write state");
@@ -213,11 +232,12 @@
     {
         RTTestSub(hTest, "Mixing read and write locks");
-
-        MediumState_T mediumState = MediumState_NotCreated;
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
-        if (mediumState != MediumState_Created)
-            RTTestFailed(hTest, "wrong medium state %d", mediumState);
-
-        TEST_COM_SUCCESS(hTest, pMedium->LockRead(&mediumState), "read lock");
+	ComPtr<IToken> pToken1, pToken2;
+
+        MediumState_T mediumState = MediumState_NotCreated;
+        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
+        if (mediumState != MediumState_Created)
+            RTTestFailed(hTest, "wrong medium state %d", mediumState);
+
+        TEST_COM_SUCCESS(hTest, pMedium->LockRead(pToken1.asOutParam()), "read lock");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock read state");
@@ -225,5 +245,7 @@
             RTTestFailed(hTest, "wrong lock read medium state %d", mediumState);
 
-        TEST_COM_FAILURE(hTest, pMedium->LockWrite(&mediumState), "read+write lock succeeded");
+        TEST_COM_FAILURE(hTest, pMedium->LockWrite(pToken2.asOutParam()), "read+write lock succeeded");
+	if (!pToken2.isNull())
+            RTTestFailed(hTest, "pToken2 is not null");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after nested lock read state");
@@ -231,61 +253,8 @@
             RTTestFailed(hTest, "wrong after nested lock read medium state %d", mediumState);
 
-        TEST_COM_SUCCESS(hTest, pMedium->UnlockRead(&mediumState), "read unlock");
-
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock read state");
-        if (mediumState != MediumState_Created)
-            RTTestFailed(hTest, "wrong unlock read medium state %d", mediumState);
-    }
-
-    if (!RTTestSubErrorCount(hTest))
-    {
-        RTTestSub(hTest, "Write locks, read unlocks");
-
-        MediumState_T mediumState = MediumState_NotCreated;
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
-        if (mediumState != MediumState_Created)
-            RTTestFailed(hTest, "wrong medium state %d", mediumState);
-
-        TEST_COM_SUCCESS(hTest, pMedium->LockWrite(&mediumState), "write lock");
-
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock write state");
-        if (mediumState != MediumState_LockedWrite)
-            RTTestFailed(hTest, "wrong lock write medium state %d", mediumState);
-
-        TEST_COM_FAILURE(hTest, pMedium->UnlockRead(&mediumState), "read unlocking a write lock succeeded");
-
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after read unlock write state");
-        if (mediumState != MediumState_LockedWrite)
-            RTTestFailed(hTest, "wrong after read lock write medium state %d", mediumState);
-
-        TEST_COM_SUCCESS(hTest, pMedium->UnlockWrite(&mediumState), "write unlock");
-
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock write state");
-        if (mediumState != MediumState_Created)
-            RTTestFailed(hTest, "wrong unlock write medium state %d", mediumState);
-    }
-
-    if (!RTTestSubErrorCount(hTest))
-    {
-        RTTestSub(hTest, "Read locks, write unlocks");
-
-        MediumState_T mediumState = MediumState_NotCreated;
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting state");
-        if (mediumState != MediumState_Created)
-            RTTestFailed(hTest, "wrong medium state %d", mediumState);
-
-        TEST_COM_SUCCESS(hTest, pMedium->LockRead(&mediumState), "read lock");
-
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting lock read state");
-        if (mediumState != MediumState_LockedRead)
-            RTTestFailed(hTest, "wrong lock write medium state %d", mediumState);
-
-        TEST_COM_FAILURE(hTest, pMedium->UnlockWrite(&mediumState), "write unlocking a read lock succeeded");
-
-        TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting after write unlock read state");
-        if (mediumState != MediumState_LockedRead)
-            RTTestFailed(hTest, "wrong after write lock read medium state %d", mediumState);
-
-        TEST_COM_SUCCESS(hTest, pMedium->UnlockRead(&mediumState), "read unlock");
+	if (!pToken1.isNull())
+            TEST_COM_SUCCESS(hTest, pToken1->Abandon(), "read unlock");
+	else
+            RTTestFailed(hTest, "pToken1 is null");
 
         TEST_COM_SUCCESS(hTest, pMedium->COMGETTER(State)(&mediumState), "getting unlock read state");
