ticker not firing often enough in minimal mac app with run loop

113 views
Skip to first unread message

Tor Langballe

unread,
Sep 4, 2019, 4:19:42 AM9/4/19
to golang-nuts
I'm running a mac cocoa event loop, and calling a go ticker:

void StartApp() {
   
[NSAutoreleasePool new];
   
[NSApplication sharedApplication];
   
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
   
[NSApp run];
}

func test() {
 
last := time.Now()
 ticker
:= time.NewTicker(time.Second)
 
for range ticker.C {
     t
:= time.Now()
     diff
:= t.Sub(last)
     
if diff > time.Second*2 {
         fmt
.Println("Tick slow:", diff)
     
}
     
last = t
 
}
}


func main
() {
 go test
()
 C
.StartApp()
}

currenly just running the go program from the terminal, though I got similar results packaging it into an app.

I soon get very long periods between ticker fires:

Tick slow: 3.501350114s
Tick slow: 5.551485377s

This seems to only happen with 

[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];

NSApplicationActivationPolicyProhibited and NSApplicationActivationPolicyAccessory don't cause this.

I assume it's the event loop in [NSApp run] that is causing this, but don't know how to start making a mac appliction with goroutines and tickers that don't get blocked.

Does anybody know how to avoid this and why it is happening?

tor



Marcin Romaszewicz

unread,
Sep 4, 2019, 1:12:21 PM9/4/19
to Tor Langballe, golang-nuts
I'm not quite sure how to avoid this, but I'm pretty sure that I know the cause. Read up on Grand Central Dispatch and Centralized Task Scheduling (https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/DiscretionaryTasks.html). It's been a while since I've written OS X code, but I used to do it quite heavily (I'm one of the authors of the mac version of Google Earth).

OS X tries very hard to keep the CPU's in their lowest power state, the the scheduler has all sorts of heuristics for when an application needs to be woken up. By default, it tries very hard to keep the applications scheduled as little as possible, and you're supposed to provide hints to the system about the urgency of the work you're doing.

Now, here's where I'm rusty, but I believe that at very low nice levels, you can bypass this behavior. Try this:

sudo nice -1 /path/to/your/application

If your timers start working correctly, that's probably the reason.

Generally, when you interact with Cocoa system libraries, there's all sorts of interaction with GCD and CTS to keep your app running in an efficiency/performance sweet spot, but when you call out to another runtime, like Go's, you bypass all those hooks.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/62325cef-7441-4a7c-abf9-ff58454ff5f4%40googlegroups.com.

Gregory Pomerantz

unread,
Sep 4, 2019, 2:31:17 PM9/4/19
to golan...@googlegroups.com, torlan...@gmail.com

> currenly just running the go program from the terminal, though I got
> similar results packaging it into an app.
>
> I soon get very long periods between ticker fires:
>
> Tick slow: 3.501350114s
> Tick slow: 5.551485377s
>
I am seeing the same thing (Late 2012 13" MacBook Pro Retina).

My first guess is that MacOS must be putting your App into a background
low-power mode and scheduling it infrequently. My ticks go up to just
about 11 seconds. because clicking on the App in the dock seems to
(temporarily) fix this. I would check the AppKit docs for how to avoid
background throttling, or see if the OS is sending you a message first
that you can respond to and prevent it from happening. An alternative is
to create a pure Go background task that interacts with a MacOS frontend
through a socket, grpc or some other mechanism. Good luck and let me
know if anything works.

Gregory Pomerantz

unread,
Sep 4, 2019, 3:45:45 PM9/4/19
to golan...@googlegroups.com, torlan...@gmail.com
Let me know if this works, it seems to fix the issue over here. Not sure
what impact this has generally on power management, sleep mode etc. but
you can read the AppKit docs for that.

void StartApp() {

    [NSAutoreleasePool new];
    [NSApplication sharedApplication];
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    id activity = [[NSProcessInfo processInfo]
beginActivityWithOptions:NSActivityBackground reason:@"Good Reason"];
    [NSApp run];
    [[NSProcessInfo processInfo] endActivity:activity];
}

Subramanian Sridharan

unread,
Sep 5, 2019, 2:28:07 AM9/5/19
to golang-nuts
I came across a similar issue when I was working with a heartbeat feature for Macs.
The same code ran as expected on Linux machines. 
In the end, the issue was with Sleep Mode in the Mac machine.
When a Mac goes to sleep, time based functions seem to pause. They resume when the machine is woken up again.
But if you are doing critical tasks that are based on time duration, they will fail.

Tor Langballe

unread,
Sep 5, 2019, 3:39:11 AM9/5/19
to golang-nuts
Thanks for all the replies everybody!

id activity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityBackground reason:@"Good Reason"]; 
Seems to do the trick, and cpu usage stays at 0.01%

I tried using sudo nice -1 and even -20, the cpu usage went up from 0.01 to 0.1, but I still got the big pauses, I guess it still puts the app to sleep when it thinks it isn't doing anything, even if it gets more CPU when it's not sleeping.

tor


Reply all
Reply to author
Forward
0 new messages