Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade_in_event_loop not always processed #5699

Open
Justyna-JustCode opened this issue Jul 26, 2024 · 3 comments · Fixed by #5729
Open

upgrade_in_event_loop not always processed #5699

Justyna-JustCode opened this issue Jul 26, 2024 · 3 comments · Fixed by #5729
Labels
a:platform-android Android platform integration (mO,bS) bug Something isn't working priority:low Lowest priority. The issue is kept open for tracking purpose, but noone is actively working on this upstream Needs a fix upstream

Comments

@Justyna-JustCode
Copy link
Contributor

Slint 1.7 + Rust
Android

I noticed that the code provided to the upgrade_in_event_loop is not always processed. So far, I have only been able to reproduce the issue on Android, and it happens only occasionally.

Minimal example:

Slint code
import { Button } from "std-widgets.slint";

export component MainWindow inherits Window {
  callback start-processing();
  callback rescue();

  public function processing-finished() {
      overlay.visible = false;
  }

  Button {
      text: "Process";

      clicked => {
          overlay.visible = true;
          root.start-processing();
      }
  }

  overlay := Rectangle {
      background: black;
      opacity: 50%;
      visible: false;

      TouchArea {} // touch blocker

      Button {
          text: "Rescue";
          y: 0;

          clicked => {
              root.rescue();
          }
      }
  }
}
Rust code
slint::include_modules!();

#[no_mangle]
fn android_main(app: slint::android::AndroidApp) -> Result<(), slint::PlatformError> {
  slint::android::init(app).unwrap();
  android_logger::init_once(android_logger::Config::default().with_max_level(log::LevelFilter::Debug));

  let ui = MainWindow::new()?;
  ui.on_start_processing({
      let ui_weak = ui.as_weak();
          move || {
              let ui_weak = ui_weak.clone();

              log::debug!("In on_start_processing");
              async_std::task::spawn(async move {
                  std::thread::sleep(std::time::Duration::from_millis(100));
                  log::debug!("In spawn thread");
                  
                  ui_weak.upgrade_in_event_loop(|ui| {
                      log::debug!("In upgrade_in_event_loop (processing)");
                      ui.invoke_processing_finished();
                  }).ok();

                  log::debug!("Out spawn thread");
              });
      }
  });

  ui.on_rescue({
      let ui_weak = ui.as_weak();
      move || {
          log::debug!("In on_rescue");
          ui_weak.upgrade_in_event_loop(|_| {
              log::debug!("In upgrade_in_event_loop (rescue)");
          }).ok();
      }
  });

  ui.run()
}
Log output
07-26 12:19:38.114 16671 16764 D tester_slint: In on_start_processing
07-26 12:19:38.214 16671 16937 D tester_slint: In spawn thread
07-26 12:19:38.214 16671 16937 D tester_slint: Out spawn thread
07-26 12:19:38.214 16671 16764 D tester_slint: In upgrade_in_event_loop (processing)
07-26 12:19:38.414 16671 16764 D tester_slint: In on_start_processing
07-26 12:19:38.515 16671 16937 D tester_slint: In spawn thread
07-26 12:19:38.515 16671 16937 D tester_slint: Out spawn thread
07-26 12:19:38.518 16671 16764 D tester_slint: In upgrade_in_event_loop (processing)
07-26 12:19:38.729 16671 16764 D tester_slint: In on_start_processing
07-26 12:19:38.830 16671 16937 D tester_slint: In spawn thread
07-26 12:19:38.830 16671 16937 D tester_slint: Out spawn thread

<-- LOCK HERE -->

07-26 12:19:43.706 16671 16764 D tester_slint: In on_rescue
07-26 12:19:43.711 16671 16764 D tester_slint: In upgrade_in_event_loop (processing)
07-26 12:19:43.711 16671 16764 D tester_slint: In upgrade_in_event_loop (rescue)

Explanation:

When we click a "Process" button, a callback is invoked, handled on the Rust side. In the callback, we spawn an async task that sleeps for some time. Before the processing request is invoked, an overlay (busy screen) is displayed in the UI. The overlay is then hidden from the Rust code using the upgrade_in_event_loop function.

If you keep clicking on the button, you will eventually end up in a situation where the overlay is not hidden. From the logs, I can tell that the upgrade_in_event_loop function was called, but its content was not.

Now, if we click on a "Rescue" button, it calls another Rust code, which calls the upgrade_in_event_loop again. This causes both closures to be processed, first from the "Process" button and then from the "Rescue" button.

It seems like the code from the upgrade_in_event_loop is put on a queue but sometimes not executed, and only the next call of the function triggers the execution.

slint-event-loop.mp4
@tronical tronical added bug Something isn't working a:platform-android Android platform integration (mO,bS) labels Jul 26, 2024
@ogoffart
Copy link
Member

We do store the event on a queue, then we wake the thread and process these events.

This is what happens when posting an event:

self.event_queue.lock().unwrap().push(Event::Other(event));
self.waker.wake();

And this is where we process them.

PollEvent::Wake => {
let queue = std::mem::take(&mut *self.event_queue.lock().unwrap());
for e in queue {

I was able to reproduce the bug with your testcase, and added logs in the android-activity backend confirm that we do call AndroidAppWaker::wake , but the PollEvent::Wake is not received.
I also confirmed that there is no re-entrency (wake is not called as we are processing the event)

What I could do is to process the pending even from the queue as long as the event loops wakes up (regardless of the event, even if it's not PollEvent::Wake), this would mean than any other touch or timeout would result in the event being processed. But I don't know why we don't get an PollEvent::Wake

ogoffart added a commit that referenced this issue Jul 31, 2024
We observe, in issue #5699 that the call to `AndroidAppWaker::wake`
doesn't always result in a `PollEvent::Wake` event.
So to work around that, always process event from any event we recieve
so that any timeout or input event would make sure event gets processed.

Closes #5699
@ogoffart
Copy link
Member

Workaround in #5729

@Justyna-JustCode
Copy link
Contributor Author

Thanks for merging the workaround! How about keeping this ticket open until a real solution is found? Even if you're not actively looking at this, it's worth remembering that the problem exists.

@ogoffart ogoffart reopened this Aug 12, 2024
@ogoffart ogoffart added priority:low Lowest priority. The issue is kept open for tracking purpose, but noone is actively working on this upstream Needs a fix upstream labels Aug 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:platform-android Android platform integration (mO,bS) bug Something isn't working priority:low Lowest priority. The issue is kept open for tracking purpose, but noone is actively working on this upstream Needs a fix upstream
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants