VirtualBox

Ignore:
Timestamp:
Mar 29, 2010 11:28:45 AM (15 years ago)
Author:
vboxsync
Message:

Main: fix locking in Appliance import/export

File:
1 edited

Legend:

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

    r27730 r27779  
    6969struct Appliance::Data
    7070{
     71    enum ApplianceState { ApplianceIdle, ApplianceImporting, ApplianceExporting };
     72
    7173    Data()
    72       : pReader(NULL) {}
     74      : state(ApplianceIdle),
     75        pReader(NULL)
     76    {
     77    }
    7378
    7479    ~Data()
     
    8186    }
    8287
    83     LocationInfo locInfo; /* The location info for the currently processed OVF */
    84 
    85     OVFReader *pReader;
    86 
    87     list< ComObjPtr<VirtualSystemDescription> > virtualSystemDescriptions;
    88 
    89     list<Utf8Str> llWarnings;
    90 
    91     ULONG ulWeightPerOperation;
    92     Utf8Str strOVFSHA1Digest;
     88    ApplianceState  state;
     89
     90    LocationInfo    locInfo;       // location info for the currently processed OVF
     91
     92    OVFReader       *pReader;
     93
     94    bool            fBusyWriting;          // state protection; while this is true nobody else can call methods
     95
     96    list< ComObjPtr<VirtualSystemDescription> >
     97                    virtualSystemDescriptions;
     98
     99    list<Utf8Str>   llWarnings;
     100
     101    ULONG           ulWeightPerOperation;
     102    Utf8Str         strOVFSHA1Digest;
    93103};
    94104
     
    371381////////////////////////////////////////////////////////////////////////////////
    372382
     383/**
     384 * Returns true if the appliance is in "idle" state. This should always be the
     385 * case unless an import or export is currently in progress. Similar to machine
     386 * states, this permits the Appliance implementation code to let go of the
     387 * Appliance object lock while a time-consuming disk conversion is in progress
     388 * without exposing the appliance to conflicting calls.
     389 *
     390 * This sets an error on "this" (the appliance) and returns false if the appliance
     391 * is busy. The caller should then return E_ACCESSDENIED.
     392 *
     393 * Must be called from under the object lock!
     394 *
     395 * @return
     396 */
     397bool Appliance::isApplianceIdle() const
     398{
     399    if (m->state == Data::ApplianceImporting)
     400        setError(VBOX_E_INVALID_OBJECT_STATE, "The appliance is busy importing files");
     401    else if (m->state == Data::ApplianceExporting)
     402        setError(VBOX_E_INVALID_OBJECT_STATE, "The appliance is busy exporting files");
     403    else
     404        return true;
     405
     406    return false;
     407}
     408
    373409HRESULT Appliance::searchUniqueVMName(Utf8Str& aName) const
    374410{
     
    885921    HRESULT rc = S_OK;
    886922
    887     switch(task->taskType)
     923    switch (task->taskType)
    888924    {
    889925        case TaskImportOVF::Read:
     
    11031139
    11041140    AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
     1141
     1142    if (!isApplianceIdle())
     1143        return VERR_ACCESS_DENIED;
     1144
     1145    // Change the appliance state so we can safely leave the lock while doing time-consuming
     1146    // disk imports; also the below method calls do all kinds of locking which conflicts with
     1147    // the appliance object lock
     1148    m->state = Data::ApplianceImporting;
     1149    appLock.release();
    11051150
    11061151    HRESULT rc = S_OK;
     
    19061951    }
    19071952
     1953    // restore the appliance state
     1954    appLock.acquire();
     1955    m->state = Data::ApplianceIdle;
     1956
    19081957    pTask->rc = rc;
    19091958
     
    22572306    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    22582307
    2259     AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
    2260 
    22612308    HRESULT rc = S_OK;
    22622309
    22632310    try
    22642311    {
     2312        AutoMultiWriteLock2 multiLock(&mVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
     2313
    22652314        xml::Document doc;
    22662315        xml::ElementNode *pelmRoot = doc.createRootElement("Envelope");
     
    29352984        }
    29362985
    2937         // finally, fill in the network section we set up empty above according
     2986        // now, fill in the network section we set up empty above according
    29382987        // to the networks we found with the hardware items
    29392988        map<Utf8Str, bool>::const_iterator itN;
     
    29482997        }
    29492998
     2999        // Finally, write out the disks!
     3000
     3001        // This can take a very long time so leave the locks; in particular, we have the media tree
     3002        // lock which Medium::CloneTo() will request, and that would deadlock. Instead, protect
     3003        // the appliance by resetting its state so we can safely leave the lock
     3004        m->state = Data::ApplianceExporting;
     3005        multiLock.release();
     3006
    29503007        list<Utf8Str> diskList;
    29513008        map<Utf8Str, const VirtualSystemDescriptionEntry*>::const_iterator itS;
     
    30033060                if (!pTask->progress.isNull())
    30043061                    pTask->progress->SetNextOperation(BstrFmt(tr("Exporting virtual disk image '%s'"), strSrcFilePath.c_str()),
    3005                                                      pDiskEntry->ulSizeMB);     // operation's weight, as set up with the IProgress originally);
     3062                                                      pDiskEntry->ulSizeMB);     // operation's weight, as set up with the IProgress originally);
    30063063
    30073064                // now wait for the background disk operation to complete; this throws HRESULTs on error
     
    30793136        rc = aRC;
    30803137    }
     3138
     3139    AutoWriteLock appLock(this COMMA_LOCKVAL_SRC_POS);
     3140    // reset the state so others can call methods again
     3141    m->state = Data::ApplianceIdle;
    30813142
    30823143    pTask->rc = rc;
     
    32783339    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    32793340
     3341    if (!isApplianceIdle())
     3342        return E_ACCESSDENIED;
     3343
    32803344    Bstr bstrPath(m->locInfo.strPath);
    32813345    bstrPath.cloneTo(aPath);
     
    32973361
    32983362    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     3363
     3364    if (!isApplianceIdle())
     3365        return E_ACCESSDENIED;
    32993366
    33003367    if (m->pReader) // OVFReader instantiated?
     
    33563423    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    33573424
     3425    if (!isApplianceIdle())
     3426        return E_ACCESSDENIED;
     3427
    33583428    SafeIfaceArray<IVirtualSystemDescription> sfaVSD(m->virtualSystemDescriptions);
    33593429    sfaVSD.detachTo(ComSafeArrayOutArg(aVirtualSystemDescriptions));
     
    33773447    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    33783448
     3449    if (!isApplianceIdle())
     3450        return E_ACCESSDENIED;
     3451
    33793452    if (m->pReader)
    33803453    {
     
    34223495
    34233496    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     3497
     3498    if (!isApplianceIdle())
     3499        return E_ACCESSDENIED;
    34243500
    34253501    HRESULT rc = S_OK;
     
    34393515        return setError(E_FAIL,
    34403516                        tr("Cannot interpret appliance without reading it first (call read() before interpret())"));
     3517
     3518    // Change the appliance state so we can safely leave the lock while doing time-consuming
     3519    // disk imports; also the below method calls do all kinds of locking which conflicts with
     3520    // the appliance object lock
     3521    m->state = Data::ApplianceImporting;
     3522    alock.release();
    34413523
    34423524    /* Try/catch so we can clean up on error */
     
    38533935    }
    38543936
     3937    // reset the appliance state
     3938    alock.acquire();
     3939    m->state = Data::ApplianceIdle;
     3940
    38553941    return rc;
    38563942}
     
    38703956    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    38713957
     3958    if (!isApplianceIdle())
     3959        return E_ACCESSDENIED;
     3960
    38723961    if (!m->pReader)
    38733962        return setError(E_FAIL,
     
    39003989
    39013990    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     3991
     3992    if (!isApplianceIdle())
     3993        return E_ACCESSDENIED;
    39023994
    39033995    ComObjPtr<VFSExplorer> explorer;
     
    39344026
    39354027    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     4028
     4029    if (!isApplianceIdle())
     4030        return E_ACCESSDENIED;
    39364031
    39374032    // see if we can handle this file; for now we insist it has an ".ovf" extension
     
    39844079
    39854080    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     4081
     4082    if (!isApplianceIdle())
     4083        return E_ACCESSDENIED;
    39864084
    39874085    com::SafeArray<BSTR> sfaWarnings(m->llWarnings.size());
     
    44064504    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    44074505
    4408     AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS);
    4409 
    44104506    ComObjPtr<VirtualSystemDescription> pNewDesc;
    44114507
     
    44234519        ComPtr<IUSBController> pUsbController;
    44244520        ComPtr<IAudioAdapter> pAudioAdapter;
     4521
     4522        // first, call the COM methods, as they request locks
     4523        rc = COMGETTER(USBController)(pUsbController.asOutParam());
     4524        if (FAILED(rc))
     4525            fUSBEnabled = false;
     4526        else
     4527            rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
     4528
     4529        // request the machine lock while acessing internal members
     4530        AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS);
     4531
     4532        pAudioAdapter = mAudioAdapter;
     4533        rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
     4534        if (FAILED(rc)) throw rc;
     4535        rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
     4536        if (FAILED(rc)) throw rc;
    44254537
    44264538        // get name
     
    44434555        // snapshotFolder?
    44444556        // VRDPServer?
    4445 
    4446         // this is more tricky so use the COM method
    4447         rc = COMGETTER(USBController)(pUsbController.asOutParam());
    4448         if (FAILED(rc))
    4449             fUSBEnabled = false;
    4450         else
    4451             rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled);
    4452 
    4453         pAudioAdapter = mAudioAdapter;
    4454         rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled);
    4455         if (FAILED(rc)) throw rc;
    4456         rc = pAudioAdapter->COMGETTER(AudioController)(&audioController);
    4457         if (FAILED(rc)) throw rc;
    44584557
    44594558        // create a new virtual system
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