- Timestamp:
- May 23, 2007 1:44:21 PM (18 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/MachineImpl.cpp
r2672 r2804 124 124 RTTIMESPEC time; 125 125 mLastStateChange = RTTimeSpecGetMilli (RTTimeNow (&time)); 126 127 mMachineStateDeps = 0; 128 mZeroMachineStateDepsSem = NIL_RTSEMEVENT; 129 mWaitingStateDeps = FALSE; 130 126 131 mCurrentStateModified = TRUE; 127 132 mHandleCfgFile = NIL_RTFILE; … … 133 138 Machine::Data::~Data() 134 139 { 140 if (mZeroMachineStateDepsSem != NIL_RTSEMEVENT) 141 { 142 RTSemEventDestroy (mZeroMachineStateDepsSem); 143 mZeroMachineStateDepsSem = NIL_RTSEMEVENT; 144 } 135 145 } 136 146 … … 2917 2927 AutoLock alock (this); 2918 2928 2929 /* wait for state dependants to drop to zero */ 2930 checkStateDependencies (alock); 2931 2919 2932 ComAssertRet (mData->mRegistered != aRegistered, E_FAIL); 2920 2933 … … 2996 3009 } 2997 3010 3011 /** 3012 * Increases the number of objects dependent on the machine state or on the 3013 * registered state. Guarantees that these two states will not change at 3014 * least until #releaseStateDependency() is called. 3015 * 3016 * Depending on the @a aDepType value, additional state checks may be 3017 * made. These checks will set extended error info on failure. 3018 * 3019 * If this method returns a failure, the dependency is not added and the 3020 * caller is not allowed to rely on any particular machine state or 3021 * registration state value and may return the failed result code to the 3022 * upper level. 3023 * 3024 * @param aDepType Dependency type to choose 3025 * @param aState Current machine state (NULL if not interested). 3026 * @param aRegistered Current registered state (NULL if not interested). 3027 */ 3028 HRESULT Machine::addStateDependency (StateDependency aDepType /* = AnyStateDep */, 3029 MachineState_T *aState /* = NULL */, 3030 BOOL *aRegistered /* = NULL */) 3031 { 3032 AutoCaller autoCaller (this); 3033 AssertComRCReturnRC (autoCaller.rc()); 3034 3035 AutoLock alock (this); 3036 3037 if (mData->mWaitingStateDeps && mData->mMachineStateDeps == 0) 3038 { 3039 /* checkStateDependencies() is at the point after RTSemEventWait() but 3040 * before entering the lock. Report an error. It would be better to 3041 * leave the lock now and re-schedule ourselves, but we don't have a 3042 * framework that can guarantee such a behavior in 100% cases. */ 3043 3044 AssertFailed(); /* <-- this is just to see how often it can happen */ 3045 3046 return setError (E_ACCESSDENIED, 3047 tr ("The machine is busy: state transition is in progress. " 3048 "Retry the operation (state is %d)"), 3049 mData->mMachineState); 3050 } 3051 3052 switch (aDepType) 3053 { 3054 case AnyStateDep: 3055 { 3056 break; 3057 } 3058 case MutableStateDep: 3059 { 3060 if (mData->mRegistered && 3061 (mType != IsSessionMachine || 3062 mData->mMachineState > MachineState_Paused || 3063 mData->mMachineState == MachineState_Saved)) 3064 return setError (E_ACCESSDENIED, 3065 tr ("The machine is not mutable (state is %d)"), 3066 mData->mMachineState); 3067 break; 3068 } 3069 case MutableOrSavedStateDep: 3070 { 3071 if (mData->mRegistered && 3072 (mType != IsSessionMachine || 3073 mData->mMachineState > MachineState_Paused)) 3074 return setError (E_ACCESSDENIED, 3075 tr ("The machine is not mutable (state is %d)"), 3076 mData->mMachineState); 3077 break; 3078 } 3079 } 3080 3081 if (aState) 3082 *aState = mData->mMachineState; 3083 if (aRegistered) 3084 *aRegistered = mData->mRegistered; 3085 3086 ++ mData->mMachineStateDeps; 3087 3088 return S_OK; 3089 } 3090 3091 /** 3092 * Decreases the number of objects dependent on the machine state. 3093 * Must always complete the #addStateDependency() call after the state 3094 * dependency no more necessary. 3095 */ 3096 void Machine::releaseStateDependency() 3097 { 3098 AutoCaller autoCaller (this); 3099 AssertComRCReturnVoid (autoCaller.rc()); 3100 3101 AutoLock alock (this); 3102 3103 AssertReturnVoid (mData->mMachineStateDeps > 0); 3104 -- mData->mMachineStateDeps; 3105 3106 if (mData->mMachineStateDeps == 0 && 3107 mData->mZeroMachineStateDepsSem != NIL_RTSEMEVENT) 3108 { 3109 /* inform checkStateDependencies() that there are no more deps */ 3110 RTSemEventSignal (mData->mZeroMachineStateDepsSem); 3111 } 3112 } 3113 2998 3114 // protected methods 2999 3115 ///////////////////////////////////////////////////////////////////////////// … … 3077 3193 } 3078 3194 3195 3196 /** 3197 * Chhecks that there are no state dependants. If necessary, waits for the 3198 * number of dependants to drop to zero. Must be called from under 3199 * this object's lock. 3200 * 3201 * @param aLock This object's lock. 3202 * 3203 * @note This method may leave the object lock during its execution! 3204 */ 3205 void Machine::checkStateDependencies (AutoLock &aLock) 3206 { 3207 AssertReturnVoid (isLockedOnCurrentThread()); 3208 AssertReturnVoid (aLock.belongsTo (this)); 3209 3210 /* Wait for all state dependants if necessary */ 3211 if (mData->mMachineStateDeps > 0) 3212 { 3213 /* lazy creation */ 3214 if (mData->mZeroMachineStateDepsSem == NIL_RTSEMEVENT) 3215 RTSemEventCreate (&mData->mZeroMachineStateDepsSem); 3216 3217 LogFlowThisFunc (("Waiting for state deps (%d) to drop to zero...\n", 3218 mData->mMachineStateDeps)); 3219 3220 mData->mWaitingStateDeps = TRUE; 3221 3222 aLock.leave(); 3223 3224 RTSemEventWait (mData->mZeroMachineStateDepsSem, RT_INDEFINITE_WAIT); 3225 3226 aLock.enter(); 3227 3228 mData->mWaitingStateDeps = FALSE; 3229 } 3230 } 3231 3079 3232 /** 3080 3233 * Helper to change the machine state. … … 3091 3244 3092 3245 AutoLock alock (this); 3246 3247 /* wait for state dependants to drop to zero */ 3248 /// @todo it may be potentially unsafe to leave the lock here as 3249 // the below method does. Needs some thinking. The easiest solution may 3250 // be to provide a separate mutex for mMachineState and mRegistered. 3251 checkStateDependencies (alock); 3093 3252 3094 3253 if (mData->mMachineState != aMachineState) … … 3377 3536 mSSData->mStateFilePath.setNull(); 3378 3537 3538 /* no need to use setMachineState() during init() */ 3379 3539 mData->mMachineState = MachineState_Aborted; 3380 3540 } 3381 3541 else if (mSSData->mStateFilePath) 3382 3542 { 3543 /* no need to use setMachineState() during init() */ 3383 3544 mData->mMachineState = MachineState_Saved; 3384 3545 } … … 7291 7452 * below, the following is enough. 7292 7453 */ 7293 LogFlowThisFunc (("Initialization failed \n"));7454 LogFlowThisFunc (("Initialization failed.\n")); 7294 7455 #if defined(__WIN__) 7295 7456 if (mIPCSem) … … 7315 7476 AutoMultiLock <2> alock (mParent->wlock(), this->wlock()); 7316 7477 7478 MachineState_T lastState = mData->mMachineState; 7479 7480 if (aReason == Uninit::Abnormal) 7481 { 7482 LogWarningThisFunc (("ABNORMAL client termination! (wasRunning=%d)\n", 7483 lastState >= MachineState_Running)); 7484 7485 /* reset the state to Aborted */ 7486 if (mData->mMachineState != MachineState_Aborted) 7487 setMachineState (MachineState_Aborted); 7488 } 7489 7317 7490 if (isModified()) 7318 7491 { … … 7334 7507 7335 7508 /* release all captured USB devices */ 7336 mParent->host()->releaseAllUSBDevices (this); 7509 if (aReason == Uninit::Abnormal && lastState >= MachineState_Running) 7510 { 7511 /* Console::captureUSBDevices() is called in the VM process only after 7512 * setting the machine state to Starting or Restoring. 7513 * Console::releaseAllUSBDevices() will be called upon successful 7514 * termination. So, we need to release USB devices only if there was 7515 * an abnormal termination of a running VM. */ 7516 ReleaseAllUSBDevices(); 7517 } 7337 7518 7338 7519 if (!mData->mSession.mType.isNull()) … … 7557 7738 7558 7739 /** 7559 * @note Locks the same as Host::autoCaptureUSBDevices() does. 7740 * Inserts all machine filters to the USB proxy service and then calls 7741 * Host::autoCaptureUSBDevices(). 7742 * 7743 * Called by Console from the VM process upon VM startup. 7744 * 7745 * @note Locks what called methods lock. 7560 7746 */ 7561 7747 STDMETHODIMP SessionMachine::AutoCaptureUSBDevices (IUSBDeviceCollection **aHostDevices) … … 7566 7752 AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); 7567 7753 7754 HRESULT rc = mUSBController->notifyProxy (true /* aInsertFilters */); 7755 AssertComRC (rc); 7756 NOREF (rc); 7757 7568 7758 return mParent->host()->autoCaptureUSBDevices (this, aHostDevices); 7569 7759 } 7570 7760 7571 7761 /** 7572 * @note Locks the same as Host::releaseAllUSBDevices() does. 7762 * Removes all machine filters from the USB proxy service and then calls 7763 * Host::releaseAllUSBDevices(). 7764 * 7765 * Called by Console from the VM process upon normal VM termination or by 7766 * SessionMachine::uninit() upon abnormal VM termination (from under the 7767 * Machine/SessionMachine lock). 7768 * 7769 * @note Locks what called methods lock. 7573 7770 */ 7574 7771 STDMETHODIMP SessionMachine::ReleaseAllUSBDevices() … … 7578 7775 AutoCaller autoCaller (this); 7579 7776 AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); 7777 7778 HRESULT rc = mUSBController->notifyProxy (false /* aInsertFilters */); 7779 AssertComRC (rc); 7780 NOREF (rc); 7580 7781 7581 7782 return mParent->host()->releaseAllUSBDevices (this); … … 8235 8436 AssertMsg (mIPCSem, ("semaphore must be created")); 8236 8437 8237 if (reason == Uninit::Abnormal)8238 {8239 LogWarningThisFunc (("ABNORMAL client termination! (wasRunning=%d)\n",8240 mData->mMachineState >= MachineState_Running));8241 8242 /* reset the state to Aborted */8243 if (mData->mMachineState != MachineState_Aborted)8244 setMachineState (MachineState_Aborted);8245 }8246 8247 8438 /* release the IPC mutex */ 8248 8439 ::ReleaseMutex (mIPCSem); … … 8260 8451 { 8261 8452 /* the semaphore is signaled, meaning the session is terminated */ 8262 8263 if (reason == Uninit::Abnormal)8264 {8265 LogWarningThisFunc (("ABNORMAL client termination! (wasRunning=%d)\n",8266 mData->mMachineState >= MachineState_Running));8267 8268 /* reset the state to Aborted */8269 if (mData->mMachineState != MachineState_Aborted)8270 setMachineState (MachineState_Aborted);8271 }8272 8273 8453 doUninit = true; 8274 8454 } -
trunk/src/VBox/Main/include/MachineImpl.h
r2567 r2804 59 59 * @param machine the machine object (must cast to Machine *) 60 60 */ 61 /// @todo replace with AutoStateDependency 61 62 #define CHECK_MACHINE_MUTABILITY(machine) \ 62 63 do { \ … … 64 65 return setError (E_ACCESSDENIED, tr ("The machine is not mutable")); \ 65 66 } while (0) 66 /** like CHECK_MACHINE_MUTABILITY but a saved state is ok, too */ 67 68 /** Like CHECK_MACHINE_MUTABILITY but a saved state is OK, too. */ 69 /// @todo replace with AutoStateDependency 67 70 #define CHECK_MACHINE_MUTABILITY_IGNORING_SAVED(machine) \ 68 71 do { \ 69 72 if (!machine->isMutableIgnoringSavedState()) \ 70 return setError (E_ACCESSDENIED, tr ("The machine is not mutable or in saved state")); \ 73 return setError (E_ACCESSDENIED, \ 74 tr ("The machine is not mutable or in saved state")); \ 71 75 } while (0) 72 76 … … 167 171 LONG64 mLastStateChange; 168 172 173 uint32_t mMachineStateDeps; 174 RTSEMEVENT mZeroMachineStateDepsSem; 175 BOOL mWaitingStateDeps; 176 169 177 BOOL mCurrentStateModified; 170 178 … … 277 285 */ 278 286 bool mHDAttachmentsChanged; 287 }; 288 289 enum StateDependency 290 { 291 AnyStateDep = 0, MutableStateDep, MutableOrSavedStateDep 292 }; 293 294 /** 295 * Helper class that safely manages the machine state dependency by 296 * calling Machine::addStateDependency() on construction and 297 * Machine::releaseStateDependency() on destruction. Intended for Machine 298 * children. The usage pattern is: 299 * 300 * @code 301 * Machine::AutoStateDependency <MutableStateDep> adep (mParent); 302 * CheckComRCReturnRC (stateDep.rc()); 303 * ... 304 * // code that depends on the particular machine state 305 * ... 306 * @endcode 307 * 308 * @param taDepType Dependecy type to manage. 309 */ 310 template <StateDependency taDepType = AnyStateDep> 311 class AutoStateDependency 312 { 313 public: 314 315 AutoStateDependency (Machine *aThat) 316 : mThat (aThat), mRC (S_OK) 317 , mMachineState (MachineState_InvalidMachineState) 318 , mRegistered (FALSE) 319 { 320 Assert (aThat); 321 mRC = aThat->addStateDependency (taDepType, &mMachineState, 322 &mRegistered); 323 } 324 ~AutoStateDependency() 325 { 326 if (SUCCEEDED (mRC)) 327 mThat->releaseStateDependency(); 328 } 329 330 /** Decreases the number of dependencies before the instance is 331 * destroyed. Note that will reset #rc() to E_FAIL. */ 332 void release() 333 { 334 AssertReturnVoid (SUCCEEDED (mRC)); 335 mThat->releaseStateDependency(); 336 mRC = E_FAIL; 337 } 338 339 /** Restores the number of callers after by #release(). #rc() will be 340 * reset to the result of calling addStateDependency() and must be 341 * rechecked to ensure the operation succeeded. */ 342 void add() 343 { 344 AssertReturnVoid (!SUCCEEDED (mRC)); 345 mRC = mThat->addStateDependency (taDepType, &mMachineState, 346 &mRegistered); 347 } 348 349 /** Returns the result of Machine::addStateDependency(). */ 350 HRESULT rc() const { return mRC; } 351 352 /** Shortcut to SUCCEEDED (rc()). */ 353 bool isOk() const { return SUCCEEDED (mRC); } 354 355 /** Returns the machine state value as returned by 356 * Machine::addStateDependency(). */ 357 MachineState_T machineState() const { return mMachineState; } 358 359 /** Returns the machine state value as returned by 360 * Machine::addStateDependency(). */ 361 BOOL machineRegistered() const { return mRegistered; } 362 363 protected: 364 365 Machine *mThat; 366 HRESULT mRC; 367 MachineState_T mMachineState; 368 BOOL mRegistered; 369 370 private: 371 372 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (AutoStateDependency) 373 DECLARE_CLS_NEW_DELETE_NOOP (AutoStateDependency) 279 374 }; 280 375 … … 375 470 // Note: these classes should enter Machine lock to keep the returned 376 471 // information valid! 472 /// @todo replace with AutoStateDependency 377 473 bool isMutable() 378 474 { … … 387 483 // Note: these classes should enter Machine lock to keep the returned 388 484 // information valid! 485 /// @todo replace with AutoStateDependency 389 486 bool isMutableIgnoringSavedState() 390 487 { … … 446 543 } 447 544 545 HRESULT addStateDependency (StateDependency aDepType = AnyStateDep, 546 MachineState_T *aState = NULL, 547 BOOL *aRegistered = NULL); 548 void releaseStateDependency(); 549 448 550 // for VirtualBoxSupportErrorInfoImpl 449 551 static const wchar_t *getComponentName() { return L"Machine"; } … … 458 560 459 561 void uninitDataAndChildObjects(); 562 563 void checkStateDependencies (AutoLock &aLock); 460 564 461 565 virtual HRESULT setMachineState (MachineState_T aMachineState);
Note:
See TracChangeset
for help on using the changeset viewer.