Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-130662

QFuture::cancel() is impractical on continuation chains

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P2: Important
    • 6.10.0 FF
    • 6.8.0
    • Core: QtConcurrent
    • None
    • 25
    • cdc71532e (dev), 3164746eb (dev), b0e82f598 (dev)
    • Foundation Sprint 124, Foundation Sprint 125

    Description

      QFuture::cancel() behaves unintuitively and is impractical when used in conjunction with continuation chains.

      Specifically, there's no obvious way to cancel such a chain. Suppose we have:

      auto f = foo()
          .then(...)  // a
          .then(...)  // b
          .then(...)  // c
          .then(...); // d
      

      Calling f.cancel() will only cancel the very last continuation (`d`), nothing else. This is contrary to basic expectations - I'd expect every not-yet-finished continuation to be canceled as well. It's also not documented.

      Having read the impl of QFutureInterfaceBase::cancel(), I thought that I could maybe cancel() the very first future:

      auto f = foo();
      auto firstF = f;
      f = f
          .then(...)  // a
          .then(...)  // b
          .then(...)  // c
          .then(...); // d
      firstF.cancel();
      

      But that only works when `firstF` has not yet finished! If it is finished already by the time I cancel it, nothing happens and the chain runs to the end.

      In the end, I have to keep a list of all intermediate futures and cancel each one in order to reliably cancel the chain:

      QList<QFuture<void>> futures;
      auto f = foo();
      futures << f;
      f = f.then(...)  // a
      futures << f;
      f = f.then(...)  // b
      futures << f;
      f = f.then(...)  // c
      futures << f;
      f = f.then(...); // d
      futures << f;
      
      for (auto &f : futures) {
          f.cancel();
      }
      

      This is super inconvenient (and unexpected).

      It would be cool if there was an overload to forcibly cancel the whole chain that a future belongs to.

      Reproduction

          QPromise<void> p1, p2;
          p1.start();
          p2.start();
      
          auto f = QtFuture::makeReadyVoidFuture()
          .then([f = p1.future()]() {
              qDebug() << "Init";
              return f;
          }).unwrap().then([f = p2.future()]() {
              qDebug() << "Continue1";
              return f;
          }).unwrap().then([&]{
              qDebug() << "Continue2";
          }).onCanceled([] {
              qDebug() << "Cancelled";
          });
      
          p1.finish();
          f.cancel();
          p2.finish();
      

      Expected output:

      Init
      Continue1
      Cancelled
      

      Actual output:

      Init
      Continue1
      Continue2
      

      Note: Is the problem actually the nested and unwrapped futures?

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            ivan.solovev Ivan Solovev
            msarehn Arno Rehn
            Vladimir Minenko Vladimir Minenko
            Alex Blasche Alex Blasche
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes