// Called on the worker thread. nsresult BackgroundFileSaver::ProcessAttention() { nsresult rv; // This function is called whenever the attention of the worker thread has // been requested. This may happen in these cases: // * We are about to start the copy for the first time. In this case, we are // called from an event posted on the worker thread from the control thread // by GetWorkerThreadAttention, and mAsyncCopyContext is null. // * We have interrupted the copy for some reason. In this case, we are // called by AsyncCopyCallback, and mAsyncCopyContext is null. // * We are currently executing ProcessStateChange, and attention is requested // by the control thread, for example because SetTarget or Finish have been // called. In this case, we are called from from an event posted through // GetWorkerThreadAttention. While mAsyncCopyContext was always null when // the event was posted, at this point mAsyncCopyContext may not be null // anymore, because ProcessStateChange may have started the copy before the // event that called this function was processed on the worker thread. // If mAsyncCopyContext is not null, we interrupt the copy and re-enter // through AsyncCopyCallback. This allows us to check if, for instance, we // should rename the target file. We will then restart the copy if needed. if (mAsyncCopyContext) { NS_CancelAsyncCopy(mAsyncCopyContext, NS_ERROR_ABORT); return NS_OK; } // Use the current shared state to determine the next operation to execute. rv = ProcessStateChange(); if (NS_FAILED(rv)) { // If something failed while processing, terminate the operation now. { MutexAutoLock lock(mLock); if (NS_SUCCEEDED(mStatus)) { mStatus = rv; } } // Ensure we notify completion now that the operation failed. CheckCompletion(); } return NS_OK; }
// Called on the control thread. nsresult BackgroundFileSaver::GetWorkerThreadAttention(bool aShouldInterruptCopy) { nsresult rv; MutexAutoLock lock(mLock); // We only require attention one time. If this function is called two times // before the worker thread wakes up, and the first has aShouldInterruptCopy // false and the second true, we won't forcibly interrupt the copy from the // control thread. However, that never happens, because calling Finish with a // success code is the only case that may result in aShouldInterruptCopy being // false. In that case, we won't call this function again, because consumers // should not invoke other methods on the control thread after calling Finish. // And in any case, Finish already closes one end of the pipe, causing the // copy to finish properly on its own. if (mWorkerThreadAttentionRequested) { return NS_OK; } if (!mAsyncCopyContext) { // Copy is not in progress, post an event to handle the change manually. nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &BackgroundFileSaver::ProcessAttention); NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); rv = mWorkerThread->Dispatch(event, NS_DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, rv); } else if (aShouldInterruptCopy) { // Interrupt the copy. The copy will be resumed, if needed, by the // ProcessAttention function, invoked by the AsyncCopyCallback function. NS_CancelAsyncCopy(mAsyncCopyContext, NS_ERROR_ABORT); } // Indicate that attention has been requested successfully, there is no need // to post another event until the worker thread processes the current one. mWorkerThreadAttentionRequested = true; return NS_OK; }