ProgressImpl.cpp revision d19316699d7f91959d88c850fd7e0d64840f39a7
//////////////////////////////////////////////////////////////////////////////// * Initializes the progress base object. * Subclasses should call this or any other #protectedInit() method from their * init() implementations. * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass. * @param aParent Parent object (only for server-side Progress objects). * @param aInitiator Initiator of the task (for server-side objects. Can be * NULL which means initiator = parent, otherwise must not * @param aDescription ask description. * @param aID Address of result GUID structure (optional). * @return COM result indicator. /* Guarantees subclasses call this method at the proper time */ /* share parent weakly */ /* assign (and therefore addref) initiator only if it is not VirtualBox * (to avoid cycling); otherwise mInitiator will remain null which means * that it is the same as the parent */ /* add to the global collection of progress operations (note: after * Initializes the progress base object. * This is a special initializer that doesn't initialize any field. Used by one * of the Progress::init() forms to create sub-progress operations combined * together using a CombinedProgress instance, so it doesn't require the parent, * initiator, description and doesn't create an ID. * Subclasses should call this or any other #protectedInit() method from their * init() implementations. * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass. /* Guarantees subclasses call this method at the proper time */ * Uninitializes the instance. * Subclasses should call this from their uninit() implementations. * @param aAutoUninitSpan AutoUninitSpan object instantiated by a subclass. * @note Using the mParent member after this method returns is forbidden. /* release initiator (effective only if mInitiator has been assigned in /* remove the added progress on failure to complete the initialization */ ///////////////////////////////////////////////////////////////////////////// /* mId is constant during life time, no need to lock */ /* mDescription is constant during life time, no need to lock */ * Internal helper to compute the total percent value based on the member values and * returns it as a "double". This is used both by GetPercent (which returns it as a * rounded ULONG) and GetTimeRemaining(). * Requires locking by the caller! * @return fractional percentage as a double value. // avoid division by zero * Internal helper for automatically timing out the operation. * The caller should hold the object write lock. // Log(("ProgressBase::GetTimeRemaining: dPercentDone %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n", // (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining)); /* checkForAutomaticTimeout requires a write lock. */ // do not report 100% until we're really really done with everything as the Qt GUI dismisses progress dialogs in that case tr(
"Result code is not available, operation is still in progress"));
tr(
"Error info is not available, operation is still in progress"));
tr(
"Operation cannot be canceled"));
// public methods only for internal purposes //////////////////////////////////////////////////////////////////////////////// * Sets the error info stored in the given progress object as the error info on * This method is useful if some other COM method uses IProgress to wait for * something and then wants to return a failed result of the operation it was * waiting for as its own result retaining the extended error info. * If the operation tracked by this progress object is completed successfully * and returned S_OK, this method does nothing but returns S_OK. Otherwise, the * failed warning or error result code specified at progress completion is * returned and the extended error info object (if any) is set on the current * Note that the given progress object must be completed, otherwise this method * Sets the cancelation callback, checking for cancelation first. * @returns Success indicator. * @retval true on success. * @retval false if the progress object has already been canceled or is in an * @param pfnCallback The function to be called upon cancelation. * @param pvUser The callback argument. //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// * Initializes the normal progress object. With this variant, one can have * an arbitrary number of sub-operation which IProgress can analyze to * have a weighted progress computed. * For example, say that one IProgress is supposed to track the cloning * of two hard disk images, which are 100 MB and 1000 MB in size, respectively, * and each of these hard disks should be one sub-operation of the IProgress. * Obviously the progress would be misleading if the progress displayed 50% * after the smaller image was cloned and would then take much longer for * With weighted progress, one can invoke the following calls: * 1) create progress object with cOperations = 2 and ulTotalOperationsWeight = * 1100 (100 MB plus 1100, but really the weights can be any ULONG); pass * in ulFirstOperationWeight = 100 for the first sub-operation * 2) Then keep calling setCurrentOperationProgress() with a percentage * for the first image; the total progress will increase up to a value * of 9% (100MB / 1100MB * 100%). * 3) Then call setNextOperation with the second weight (1000 for the megabytes * 4) Then keep calling setCurrentOperationProgress() with a percentage for * the second image, where 100% of the operation will then yield a 100% * progress of the entire task. * Weighting is optional; you can simply assign a weight of 1 to each operation * and pass ulTotalOperationsWeight == cOperations to this constructor (but * for that variant and for backwards-compatibility a simpler constructor exists * Even simpler, if you need no sub-operations at all, pass in cOperations = * ulTotalOperationsWeight = ulFirstOperationWeight = 1. * @param aParent See ProgressBase::init(). * @param aInitiator See ProgressBase::init(). * @param aDescription See ProgressBase::init(). * @param aCancelable Flag whether the task maybe canceled. * @param cOperations Number of operations within this task (at least 1). * @param ulTotalOperationsWeight Total weight of operations; must be the sum of ulFirstOperationWeight and * what is later passed with each subsequent setNextOperation() call. * @param bstrFirstOperationDescription Description of the first operation. * @param ulFirstOperationWeight Weight of first sub-operation. * @param aId See ProgressBase::init(). LogFlowThisFunc((
"aDescription=\"%ls\", cOperations=%d, ulTotalOperationsWeight=%d, bstrFirstOperationDescription=\"%ls\", ulFirstOperationWeight=%d\n",
/* Enclose the state transition NotReady->InInit->Ready */ /* Confirm a successful initialization when it's the case */ * Initializes the sub-progress object that represents a specific operation of * Objects initialized with this method are then combined together into the * single task using a CombinedProgress instance, so it doesn't require the * parent, initiator, description and doesn't create an ID. Note that calling * respective getter methods on an object initialized with this method is * useless. Such objects are used only to provide a separate wait semaphore and * store individual operation descriptions. * @param aCancelable Flag whether the task maybe canceled. * @param aOperationCount Number of sub-operations within this task (at least 1). * @param aOperationDescription Description of the individual operation. /* Enclose the state transition NotReady->InInit->Ready */ // for this variant we assume for now that all operations are weighed "1" // and equal total weight = operation count /* Confirm a successful initialization when it's the case */ * Uninitializes the instance and sets the ready flag to FALSE. * Called either from FinalRelease() or by the parent when it gets destroyed. /* Enclose the state transition Ready->InUninit->NotReady */ /* wake up all threads still waiting on occasion */ LogFlow ((
"WARNING: There are still %d threads waiting for '%ls' completion!\n",
///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// * @note XPCOM: when this method is not called on the main XPCOM thread, it * simply blocks the thread until mCompletedSem is signalled. If the * thread has its own event queue (hmm, what for?) that it must run, then * calling this method will definitely freeze event processing. /* if we're already completed, take a shortcut */ RTTimeNow(&
time);
/** @todo r=bird: Use monotonic time (RTTimeMilliTS()) here because of daylight saving and things like that. */ /* the last waiter resets the semaphore */ tr(
"Failed to wait for the task completion (%Rrc)"),
* @note XPCOM: when this method is not called on the main XPCOM thread, it * simply blocks the thread until mCompletedSem is signalled. If the * thread has its own event queue (hmm, what for?) that it must run, then * calling this method will definitely freeze event processing. /* if we're already completed or if the given operation is already done, * then take a shortcut */ /* the last waiter resets the semaphore */ tr(
"Failed to wait for the operation completion (%Rrc)"),
tr(
"Operation cannot be canceled"));
* Updates the percentage value of the current operation. * @param aPercent New percentage value of the operation in progress * Signals that the current operation is successfully completed and advances to * the next operation. The operation percentage is reset to 0. * @param aOperationDescription Description of the next operation. * @note The current operation must not be the last one. Log((
"Progress::setNextOperation(%ls): ulNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
/* wake up all waiting threads */ // public methods only for internal purposes ///////////////////////////////////////////////////////////////////////////// * Sets the internal result code and attempts to retrieve additional error * info from the current thread. Gets called from Progress::notifyComplete(), * but can be called again to override a previous result set with /* try to import error info from the current thread */ #
else /* !defined (VBOX_WITH_XPCOM) */#
endif /* !defined (VBOX_WITH_XPCOM) */ * Marks the whole task as complete and sets the result code. * If the result code indicates a failure (|FAILED(@a aResultCode)|) then this * method will import the error info from the current thread and assign it to * the errorInfo attribute (it will return an error if no info is available in * If the result code indicates a success (|SUCCEEDED(@a aResultCode)|) then * the current operation is set to the last. * Note that this method may be called only once for the given Progress object. * Subsequent calls will assert. * @param aResultCode Operation result code. /* remove from the global collection of pending progress operations */ /* wake up all waiting threads */ * Marks the operation as complete and attaches full error info. * See com::SupportErrorInfoImpl::setError(HRESULT, const GUID &, const wchar_t * *, const char *, ...) for more info. * @param aResultCode Operation result (error) code, must not be S_OK. * @param aIID IID of the interface that defines the error. * @param aComponent Name of the component that generates the error. * @param aText Error message (must not be null), an RTStrPrintf-like * format string in UTF-8 encoding. * @param ... List of arguments for the format string. /* remove from the global collection of pending progress operations */ /* wake up all waiting threads */ * Notify the progress object that we're almost at the point of no return. * This atomically checks for and disables cancelation. Calls to * IProgress::Cancel() made after a successfull call to this method will fail * and the user can be told. While this isn't entirely clean behavior, it * prevents issues with an irreversible actually operation succeeding while the * user belive it was rolled back. * @returns Success indicator. * @retval true on success. * @retval false if the progress object has already been canceled or is in an //////////////////////////////////////////////////////////////////////////////// // CombinedProgress class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// * Initializes this object based on individual combined progresses. * Must be called only from #init()! * @param aAutoInitSpan AutoInitSpan object instantiated by a subclass. * @param aParent See ProgressBase::init(). * @param aInitiator See ProgressBase::init(). * @param aDescription See ProgressBase::init(). * @param aId See ProgressBase::init(). * Initializes the combined progress object given two normal progress * @param aParent See ProgressBase::init(). * @param aInitiator See ProgressBase::init(). * @param aDescription See ProgressBase::init(). * @param aProgress1 First normal progress object. * @param aProgress2 Second normal progress object. * @param aId See ProgressBase::init(). /* Enclose the state transition NotReady->InInit->Ready */ /* Confirm a successful initialization when it's the case */ * Uninitializes the instance and sets the ready flag to FALSE. * Called either from FinalRelease() or by the parent when it gets destroyed. /* Enclose the state transition Ready->InUninit->NotReady */ //////////////////////////////////////////////////////////////////////////////// /* checkProgress needs a write lock */ * (100 / m_cOperations) * mOperation + * ((100 / m_cOperations) / 100) * m_ulOperationPercent */ /* checkProgress needs a write lock */ /* checkProgress needs a write lock */ /* checkProgress needs a write lock */ /* checkProgress needs a write lock */ /* checkProgress needs a write lock */ /* checkProgress needs a write lock */ /* checkProgress needs a write lock */ ///////////////////////////////////////////////////////////////////////////// * @note XPCOM: when this method is called not on the main XPCOM thread, it * simply blocks the thread until mCompletedSem is signalled. If the * thread has its own event queue (hmm, what for?) that it must run, then * calling this method will definitely freeze event processing. /* if we're already completed, take a shortcut */ * @note XPCOM: when this method is called not on the main XPCOM thread, it * simply blocks the thread until mCompletedSem is signalled. If the * thread has its own event queue (hmm, what for?) that it must run, then * calling this method will definitely freeze event processing. /* if we're already completed or if the given operation is already done, * then take a shortcut */ /* find the right progress object to wait for */ /* found the right progress object */ /* wait for the appropriate progress operation completion */ /** @todo Teleportation: Shouldn't this be propagated to mProgresses? If * powerUp creates passes a combined progress object to the client, I * won't get called back since I'm only getting the powerupProgress ... //////////////////////////////////////////////////////////////////////////////// * Fetches the properties of the current progress object and, if it is * successfully completed, advances to the next uncompleted or unsuccessfully * completed object in the vector of combined progress objects. * @note Must be called from under this object's write lock! /* do nothing if we're already marked ourselves as completed */ /* vi: set tabstop=4 shiftwidth=4 expandtab: */