Opened 5 years ago

Closed 4 years ago

#11797 closed defect (fixed)

Idle processing broken in wxOSX/Cocoa

Reported by: retrakker Owned by: csomor
Priority: high Milestone: 2.9.1
Component: wxOSX-Cocoa Version: stable-latest
Keywords: Idle Cc: norfolc@…
Blocked By: Blocking:
Patch: yes

Description

It seems that either idle processing is broken or needs a special treatment as Cocoa has quite specific requirements around the runloop. I couldn't find any documentation nor somebody on the forum to shed light on it.

However, not being able to do idle processing makes it quite hard to move over from Carbon (2.8) to wxOSX/Cocoa (2.9) - with a simulation software.
Other avenues for these type of processing I tried are using a wxTimer and a thread. wxTimer also make the application sticky (menues don't react in first approach, resizing doesn't work) - which is expected. Threading is one possibility but again syncing with the idle loop is needed so that for instance a wxGLCanvas can safely be updated.

To see the behavior I attached a patch against the trunk. The idle loop works but the internal idle processing does not work.

Attachments (3)

test_idle_gl.patch download (1.0 KB) - added by retrakker 5 years ago.
Example to show the problem with Idle processing using the GLCanvas
idle_test_minimal.patch download (998 bytes) - added by retrakker 5 years ago.
Example to show the problem with Idle processing using the Minimal example
cocoa_evtloopfix.patch download (1.0 KB) - added by retrakker 5 years ago.
Fix for the wxGUIEventLoop::Pending method - fixed OnIdle() and proper exit

Download all attachments as: .zip

Change History (23)

Changed 5 years ago by retrakker

Example to show the problem with Idle processing using the GLCanvas

comment:1 Changed 5 years ago by vadz

  • Milestone changed from 3.0 to 2.9.1

Is it wxGLCanvas-specific or does it happen with any window?

In any case, this does look like a serious breakage.

Changed 5 years ago by retrakker

Example to show the problem with Idle processing using the Minimal example

comment:2 Changed 5 years ago by retrakker

Sorry, no it seems to be broken for wxFrame in general - the idle event callback is being triggered but the application becomes unresponsive - also the menu doesn't appear. Attached now a patch to demonstrate the behavior in the minimal example.

comment:3 follow-up: Changed 5 years ago by csomor

  • Owner set to csomor
  • Status changed from new to accepted

I'll look at it, I don't understand the sentence

"The idle loop works but the internal idle processing does not work."

I assume that the OnIdle is not called or only called once, in your minimal sample now, is this correct ?

comment:4 in reply to: ↑ 3 Changed 5 years ago by retrakker

Replying to csomor:

I'll look at it, I don't understand the sentence

"The idle loop works but the internal idle processing does not work."

I assume that the OnIdle is not called or only called once, in your minimal sample now, is this correct ?

Bad wording on my side. The eventhandler method MyFrame::OnIdle() is being called (in the minimal example I do a statusbar update) but the rest of the application is not responding to input - thus, I assume the internal idle processing is not being triggered. I tried to do a event.Skip() or just leave it - no difference.

To replicate/illustrate it I added patches for the minimal app and the opengl/cube. My development environment is OSX 10.6.1 with using 10.5 SDK and i386 as target platform. I am at SVN revision 63106.

comment:5 Changed 5 years ago by csomor

  • Status changed from accepted to infoneeded

and the minimal sample without your patch works correctly for you ? ie the idle handler puts it into a non-responsive state ?

comment:6 Changed 5 years ago by csomor

  • Status changed from infoneeded to accepted

reproduced, leads to a tight loop because of the unconditional use of NeedsMore, I'll look at it.

But: using Idle like this will by the way lock-down a fast machine without need. So I'd do a 50ms timer for the GLCanvas, I'm doing this even on the iPhone

comment:7 Changed 5 years ago by SC

  • Resolution set to fixed
  • Status changed from accepted to closed

(In [63652]) pending events detected didn't work properly, go the safe route now, fixes #11797

comment:8 Changed 5 years ago by vadz

It's true that calling RequestMore() all the time like this is a bad idea and shouldn't be done constantly, normally you'd only do it for a relatively brief amount of time. However what is supposed to happen in this case is that the next idle event shouldn't be generated immediately even if RequestMore() was called, any other pending events should be processed first. IOW if there are no other events, then calling RequestMore() ensures that another idle event will be nevertheless generated. But if there are other events, they should be processed and idle event will be only sent when the event queue becomes empty, i.e. as usual and RequestMore() is effectively ignored in this case.

I don't know if this is how wxOSX works after the latest changes but this is how it's supposed to work at wx API level.

comment:9 Changed 5 years ago by csomor

yes, the Pending() method was not delivering true in this situation for some reason, so I changed it to always return true, since the 'waiting for event' time is still calculated according to the RequestMore this still allows the app to consume less CPU if no idle events are needed

comment:10 Changed 5 years ago by norfolc

Setting Pending() to always return true causes infinite loop in src\common\evtloopcmn.cpp at line 150. So an app does not exit.

comment:11 Changed 5 years ago by norfolc

  • Cc norfolc@… added

comment:12 follow-up: Changed 5 years ago by retrakker

  • Resolution fixed deleted
  • Status changed from closed to reopened

This fix only makes OnIdle() work correctly however it breaks exiting the application (see comment by norfolc). Would it be possible to either document a better usage of OnIdle(), which seem to have changed from 2.8.x (or 2.6 for that matter) or fix the return value of ::Pending().

Just to explain the objective: OnIdle() was the way to do simulation updates in a non-blocking way and without threads. It works very nicely in wxWidgets 2.8.x (wxMac, wxGTK and wxMSW). Timers in the frequency I would need do bog down the whole application. I'm using OnIdle() to capture a video image and do image processing and rendering in a GLCanvas. Usually, on a fast machine this is a matter of 2ms, and will only commence when there is a new image. In order to prevent brute force updates I usually add a wxMilliSleep(1) in the OnIdle() method.

comment:13 Changed 5 years ago by retrakker

  • Patch set

Attached a patch to fix the Pending() method to return true if there are events and reinject them into the runloop. The code is slightly inspired by Qt's solution - however, it is taking into consideration the way wxWidgets does the its inner loop. This corrects the OnIdle() behaviour and fixes the problem with exiting the application.

Tested on OSX 10.6 with target being 10.5/i386

Changed 5 years ago by retrakker

Fix for the wxGUIEventLoop::Pending method - fixed OnIdle() and proper exit

comment:14 in reply to: ↑ 12 ; follow-up: Changed 5 years ago by vadz

Replying to retrakker:

Would it be possible to either document a better usage of OnIdle(), which seem to have changed from 2.8.x (or 2.6 for that matter)

I don't think it ever changed, it was never recommended to use it like this.

Replying to retrakker:

Attached a patch to fix the Pending() method to return true if there are events and reinject them into the runloop.

Thanks for the patch! I hope Stefan can look at it but I don't understand why do we have to dequeue and then reinject the events. Does this mean that the code only "reliably detects pending events" when you dequeue them and doesn't do it otherwise? If so, this would be a nice bug on Apple part...

comment:15 in reply to: ↑ 14 Changed 5 years ago by retrakker

Replying to vadz:

Replying to retrakker:

Would it be possible to either document a better usage of OnIdle(), which seem to have changed from 2.8.x (or 2.6 for that matter)

I don't think it ever changed, it was never recommended to use it like this.

I had another read through the docs of wxIdleEvent, indeed the RequestMore() part is been described as non-recommended. However, working with a number of 3D engines, I think all of them use this method in their examples (see osgviewerWX, wxOgre and the wxWidgets+Irrlicht examples).
As OSG is my main target I will update their example. I am using now a chained method of a wxTimer to call wxWakeUpIdle() in order to not stall the application - which from some testing seems to work reliably on both Mac + Win32.

Replying to retrakker:

Attached a patch to fix the Pending() method to return true if there are events and reinject them into the runloop.

Thanks for the patch! I hope Stefan can look at it but I don't understand why do we have to dequeue and then reinject the events. Does this mean that the code only "reliably detects pending events" when you dequeue them and doesn't do it otherwise? If so, this would be a nice bug on Apple part...

I've seen in several forums (non wxWidgets) people having problems with this call. And yes not dequeing them seems to not work, at least not with this event mask. There was also indication that behavior for the original code changed from 10.4 to 10.5 - thus, the code needs to be tested thoroughly. Also in the Qt code there is a comment about the "experimental" nature of this call.

comment:16 Changed 5 years ago by SC

(In [63687]) streamlining OSX event support first step, see #11805, see #11797

comment:17 follow-up: Changed 5 years ago by csomor

could you please check with the last revision and report back, thanks

comment:18 Changed 5 years ago by SC

(In [63689]) streamlining OSX event support second step, moving pending and idle event handling to runloop-observer, see #11805, see #11797

comment:19 in reply to: ↑ 17 Changed 5 years ago by retrakker

Replying to csomor:

could you please check with the last revision and report back, thanks

I checked against rev 63689 and it works fine. Tested:

  • combination of wxTimer & wxWakeUpIdle and OnIdle
  • just OnIdle() with RequestMore()

both methods work the same as on other platforms



comment:20 Changed 4 years ago by csomor

  • Resolution set to fixed
  • Status changed from reopened to closed
Note: See TracTickets for help on using tickets.