+ /**
+ * @see FileBackend::doQuickOperationsInternal()
+ * @return Status
+ * @throws MWException
+ */
+ final protected function doQuickOperationsInternal( array $ops ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ $status = Status::newGood();
+
+ $supportedOps = array( 'create', 'store', 'copy', 'move', 'delete', 'null' );
+ $async = ( $this->parallelize === 'implicit' );
+ $maxConcurrency = $this->concurrency; // throttle
+
+ $statuses = array(); // array of (index => Status)
+ $fileOpHandles = array(); // list of (index => handle) arrays
+ $curFileOpHandles = array(); // current handle batch
+ // Perform the sync-only ops and build up op handles for the async ops...
+ foreach ( $ops as $index => $params ) {
+ if ( !in_array( $params['op'], $supportedOps ) ) {
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ throw new MWException( "Operation '{$params['op']}' is not supported." );
+ }
+ $method = $params['op'] . 'Internal'; // e.g. "storeInternal"
+ $subStatus = $this->$method( array( 'async' => $async ) + $params );
+ if ( $subStatus->value instanceof FileBackendStoreOpHandle ) { // async
+ if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
+ $fileOpHandles[] = $curFileOpHandles; // push this batch
+ $curFileOpHandles = array();
+ }
+ $curFileOpHandles[$index] = $subStatus->value; // keep index
+ } else { // error or completed
+ $statuses[$index] = $subStatus; // keep index
+ }
+ }
+ if ( count( $curFileOpHandles ) ) {
+ $fileOpHandles[] = $curFileOpHandles; // last batch
+ }
+ // Do all the async ops that can be done concurrently...
+ foreach ( $fileOpHandles as $fileHandleBatch ) {
+ $statuses = $statuses + $this->executeOpHandlesInternal( $fileHandleBatch );
+ }
+ // Marshall and merge all the responses...
+ foreach ( $statuses as $index => $subStatus ) {
+ $status->merge( $subStatus );
+ if ( $subStatus->isOK() ) {
+ $status->success[$index] = true;
+ ++$status->successCount;
+ } else {
+ $status->success[$index] = false;
+ ++$status->failCount;
+ }
+ }
+
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
+ /**
+ * Execute a list of FileBackendStoreOpHandle handles in parallel.
+ * The resulting Status object fields will correspond
+ * to the order in which the handles where given.
+ *
+ * @param $handles Array List of FileBackendStoreOpHandle objects
+ * @return Array Map of Status objects
+ * @throws MWException
+ */
+ final public function executeOpHandlesInternal( array $fileOpHandles ) {
+ wfProfileIn( __METHOD__ );
+ wfProfileIn( __METHOD__ . '-' . $this->name );
+ foreach ( $fileOpHandles as $fileOpHandle ) {
+ if ( !( $fileOpHandle instanceof FileBackendStoreOpHandle ) ) {
+ throw new MWException( "Given a non-FileBackendStoreOpHandle object." );
+ } elseif ( $fileOpHandle->backend->getName() !== $this->getName() ) {
+ throw new MWException( "Given a FileBackendStoreOpHandle for the wrong backend." );
+ }
+ }
+ $res = $this->doExecuteOpHandlesInternal( $fileOpHandles );
+ foreach ( $fileOpHandles as $fileOpHandle ) {
+ $fileOpHandle->closeResources();
+ }
+ wfProfileOut( __METHOD__ . '-' . $this->name );
+ wfProfileOut( __METHOD__ );
+ return $res;
+ }
+
+ /**
+ * @see FileBackendStore::executeOpHandlesInternal()
+ * @return Array List of corresponding Status objects
+ */
+ protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
+ foreach ( $fileOpHandles as $fileOpHandle ) { // OK if empty
+ throw new MWException( "This backend supports no asynchronous operations." );
+ }
+ return array();
+ }
+