Reputation: 65
A part of my program need to sleep for 10 milliseconds. Normally I use the boost lib for this, but it sometimes sleep for 10010 milliseconds, so I tried replacing
boost::this_thread::sleep_for(boost::chrono::milliseconds(read_delay_ms));
with
struct timespec a;
a.tv_sec = 0;
a.tv_nsec = read_delay_ms * 1000000;
int rc = nanosleep( &a, NULL );
Not surprisingly the use of nanosleep also sometimes sleep for 10010 milliseconds (sleep_for is implemented using nanosleep() on mac).
My program is complex so I have not been able to create a small example that illustrates the issue, I am working on this. Here are some highlights:
It is a python extension written in C++ using boost::python as bridge
Uses boost::threads for asynchrony tasks using boost::asio
The issue is only seen on mac OS X 10.9. It is not seen on Mac OS X 10.8 and below, and not seen on linux, win, iOS and android.
In order to find the error in either my code, boost lib or sys functions, any help or suggestions are more that welcome.
Upvotes: 3
Views: 1257
Reputation: 645
Here's a programmatic solution written in C that will compile and run on both older and newer OS X versions (i.e. you don't need 10.9 or to be working in Objective C to compile this).
Call osx_latencycritical_start()
at the start of your program, or at least before timing critical operations begin.
Call osx_latencycritical_end()
if your code is no longer doing timing critical work.
(The code is recursion safe, but not thread safe. I place this code in the public domain.)
#if defined(__APPLE__)
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
# include <objc/runtime.h>
# include <objc/message.h>
#else
# include <objc/objc-runtime.h>
#endif
/* Globals */
static int osx_latencycritical_count = 0;
static id osx_latencycritical_activity = nil;
/* Tell App Nap that this is latency critical */
void osx_latencycritical_start() {
Class pic; /* Process info class */
SEL pisl; /* Process info selector */
SEL bawo; /* Begin Activity With Options selector */
id pi; /* Process info */
id str; /* Reason string */
if (osx_latencycritical_count++ != 0)
return;
/* Avoid triggering an exception when run on older OS X */
if ((pic = (Class)objc_getClass("NSProcessInfo")) == nil)
return;
if (class_getClassMethod(pic, (pisl = sel_getUid("processInfo"))) == NULL)
return;
if (class_getInstanceMethod(pic,
(bawo = sel_getUid("beginActivityWithOptions:reason:"))) == NULL)
return;
/* Get the process instance */
if ((pi = objc_msgSend((id)pic, pisl)) == nil)
return;
/* Create a reason string */
str = objc_msgSend(objc_getClass("NSString"), sel_getUid("alloc"));
str = objc_msgSend(str, sel_getUid("initWithUTF8String:"), "Timing Crititcal");
/* Start activity that tells App Nap to mind its own business: */
/* NSActivityUserInitiatedAllowingIdleSystemSleep */
/* | NSActivityLatencyCritical */
osx_latencycritical_activity = objc_msgSend(pi, bawo,
0x00FFFFFFULL | 0xFF00000000ULL, str);
}
/* Done with latency critical */
void osx_latencycritical_end() {
if (osx_latencycritical_count > 0) {
osx_latencycritical_count--;
if (osx_latencycritical_count == 0
&& osx_latencycritical_activity != nil) {
objc_msgSend(
objc_msgSend(objc_getClass("NSProcessInfo"),
sel_getUid("processInfo")),
sel_getUid("endActivity:"),
osx_latencycritical_activity);
osx_latencycritical_activity = nil;
}
}
}
#endif /* __APPLE__ */
Upvotes: 3
Reputation: 24439
App Nap is most probably the reason for this. It was introduced in 10.9 and is already known for causing such surprises.
NSProcessInfo
has new three methods for temporarily disabling App Nap: beginActivityWithOptions:reason:
, endActivity:
, performActivityWithOptions:reason:block:
.
You can also disable it by writing boolean YES into NSAppSleepDisabled
user default of your application's domain.
Upvotes: 5