← Back to team overview

ubuntu-webapps-bugs team mailing list archive

[Bug 1536797] [NEW] Fix nested message loop handling

 

Public bug reported:

I've reviewed the behaviour of Oxide if Qt or an application executes a
nested QEventLoop on the main thread. This is needed to ensure we can
safely handle drag and drop:

- Scenario 1: QEventLoop executed with no Chromium code on the stack.
In this case, the nested loop will pump the Chromium event queue as normal, and this is fine because there are no nested Chromium tasks.

- Scenario 2: QEventLoop executed from a non-nested Chromium task.
In this case, the nested loop will pump the Chromium event queue by re-entering oxide::qt::MessagePump::RunOneTask()
  - It doesn't look like there's any re-entrancy issues here.
  - MessageLoop::DoWork() will not run any tasks because the MessageLoop is in a task and nestable tasks haven't been explicitly allowed (MessageLoop::nestable_tasks_allowed_ is set to false in RunTask).
  - MessageLoop::DoDelayedWork() - same as DoWork()
  - MessageLoop::DoIdleWork() will process tasks from the deferred non-nestable work queue.

The last point is a bug, as DoIdleWork() should not run tasks in a
nested loop. However, it seems like an edge case - tasks are only added
to this work queue via nested calls to DoWork() and DoDelayedWork() when
nestable tasks are allowed but the task isn't nestable. We don't do this
anywhere in Oxide, although that doesn't mean it couldn't happen
elsewhere in Chromium.

- Scenario 3: QEventLoop executed from a nested Chromium task (via RunLoop::Run)
As 2, above. Nested tasks are still blocked in DoIdle and DoDelayedWork because RunTask clears nestable_tasks_allowed_ before running the nested task. However, DoIdleWork will behave correctly here because the RunLoop::run_depth_ check in MessageLoop::ProcessNextDelayedNonNestableTask will work.

It seems like we should detect re-entrancy from a nested QEventLoop in oxide::qt::MessagePump::RunOneTask(). We could do this by adding an extra bit to RunState, that we set when calling in to MessageLoop.
- If we re-enter from a nested QEventLoop, the extra bit on the current RunState will be set. In this case, we should create a new RunLoop instance before calling in to MessageLoop.
- A nested RunLoop creates a new RunState in our MessagePump. In this case, we won't trigger the re-entrancy detection (and we don't need to)

After this is fixed, a nested QEventLoop created outside of Oxide will
process Qt events but won't run any Oxide or Chromium tasks. The only
way to run a nested loop that processes nestable Oxide or Chromium tasks
is to use MessageLoop::ScopedNestableTasksAllower and RunLoop, but that
can only be done from code in Oxide.

** Affects: oxide
     Importance: Medium
         Status: Triaged

** Changed in: oxide
   Importance: Undecided => Medium

** Changed in: oxide
       Status: New => Triaged

-- 
You received this bug notification because you are a member of Ubuntu
WebApps bug tracking, which is subscribed to Oxide.
https://bugs.launchpad.net/bugs/1536797

Title:
  Fix nested message loop handling

Status in Oxide:
  Triaged

Bug description:
  I've reviewed the behaviour of Oxide if Qt or an application executes
  a nested QEventLoop on the main thread. This is needed to ensure we
  can safely handle drag and drop:

  - Scenario 1: QEventLoop executed with no Chromium code on the stack.
  In this case, the nested loop will pump the Chromium event queue as normal, and this is fine because there are no nested Chromium tasks.

  - Scenario 2: QEventLoop executed from a non-nested Chromium task.
  In this case, the nested loop will pump the Chromium event queue by re-entering oxide::qt::MessagePump::RunOneTask()
    - It doesn't look like there's any re-entrancy issues here.
    - MessageLoop::DoWork() will not run any tasks because the MessageLoop is in a task and nestable tasks haven't been explicitly allowed (MessageLoop::nestable_tasks_allowed_ is set to false in RunTask).
    - MessageLoop::DoDelayedWork() - same as DoWork()
    - MessageLoop::DoIdleWork() will process tasks from the deferred non-nestable work queue.

  The last point is a bug, as DoIdleWork() should not run tasks in a
  nested loop. However, it seems like an edge case - tasks are only
  added to this work queue via nested calls to DoWork() and
  DoDelayedWork() when nestable tasks are allowed but the task isn't
  nestable. We don't do this anywhere in Oxide, although that doesn't
  mean it couldn't happen elsewhere in Chromium.

  - Scenario 3: QEventLoop executed from a nested Chromium task (via RunLoop::Run)
  As 2, above. Nested tasks are still blocked in DoIdle and DoDelayedWork because RunTask clears nestable_tasks_allowed_ before running the nested task. However, DoIdleWork will behave correctly here because the RunLoop::run_depth_ check in MessageLoop::ProcessNextDelayedNonNestableTask will work.

  It seems like we should detect re-entrancy from a nested QEventLoop in oxide::qt::MessagePump::RunOneTask(). We could do this by adding an extra bit to RunState, that we set when calling in to MessageLoop.
  - If we re-enter from a nested QEventLoop, the extra bit on the current RunState will be set. In this case, we should create a new RunLoop instance before calling in to MessageLoop.
  - A nested RunLoop creates a new RunState in our MessagePump. In this case, we won't trigger the re-entrancy detection (and we don't need to)

  After this is fixed, a nested QEventLoop created outside of Oxide will
  process Qt events but won't run any Oxide or Chromium tasks. The only
  way to run a nested loop that processes nestable Oxide or Chromium
  tasks is to use MessageLoop::ScopedNestableTasksAllower and RunLoop,
  but that can only be done from code in Oxide.

To manage notifications about this bug go to:
https://bugs.launchpad.net/oxide/+bug/1536797/+subscriptions


Follow ups