+ /**
+ * Actually run any "atomic section cancel" callbacks.
+ *
+ * @param int $trigger IDatabase::TRIGGER_* constant
+ * @param AtomicSectionIdentifier[]|null $sectionIds Section IDs to cancel,
+ * null on transaction rollback
+ */
+ private function runOnAtomicSectionCancelCallbacks(
+ $trigger, array $sectionIds = null
+ ) {
+ /** @var Exception|Throwable $e */
+ $e = null; // first exception
+
+ $notCancelled = [];
+ do {
+ $callbacks = $this->trxSectionCancelCallbacks;
+ $this->trxSectionCancelCallbacks = []; // consumed (recursion guard)
+ foreach ( $callbacks as $entry ) {
+ if ( $sectionIds === null || in_array( $entry[2], $sectionIds, true ) ) {
+ try {
+ $entry[0]( $trigger, $this );
+ } catch ( Exception $ex ) {
+ ( $this->errorLogger )( $ex );
+ $e = $e ?: $ex;
+ } catch ( Throwable $ex ) {
+ // @todo: Log?
+ $e = $e ?: $ex;
+ }
+ } else {
+ $notCancelled[] = $entry;
+ }
+ }
+ } while ( count( $this->trxSectionCancelCallbacks ) );
+ $this->trxSectionCancelCallbacks = $notCancelled;
+
+ if ( $e !== null ) {
+ throw $e; // re-throw any first Exception/Throwable
+ }
+ }
+