• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Idle-time processing in a runloop
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Idle-time processing in a runloop


  • Subject: Idle-time processing in a runloop
  • From: Jakob Olesen <email@hidden>
  • Date: Wed, 28 Jun 2006 19:55:11 +0200

I am writing a Cocoa app that needs to do some calculations in the background. I understand from previous threads that something like that should be done on a separate thread to keep the GUI responsive. No problem.

Some of my background tasks do network communications, so I would like to use a runloop. Then I can also communicate with the background thread using distributed objects or a mach port. Very cool. I would like the background thread to respond to requests from the main thread as quickly as possible to avoid stalling the GUI.

In other words, I would like my runloop to process any pending sources, and only when there are no sources should one of my background tasks be run. One way of doing it is by polling the runloop:

Assume two functions task_queue_empty() and run_single_task() doing the obvious thing.

void
threadMain()
{
for(;;) {
SInt32 rlExit = kCFRunLoopRunHandledSource;
// wait for a task to appear, handle all sources first
while(task_queue_empty() || rlExit==kCFRunLoopRunHandledSource) {
rlExit = CFRunLoopRunInMode(kCFRunLoopDefaultMode, task_queue_empty() ? 1e10 : 0, YES);
}
run_single_task();
}
}


This works very nicely: I run a single task only when all sources are exhausted, so they get highest priority. There is no waiting for idle timers to fire.

Two problems:
1. I am "hijacking" the runloop, so this method cannot be used in the main thread where NSApplicationMain() likes to call [NSRunLoop run]. Not a big problem, but it would be useful to run in the main thread during development.
2. Recursive runloop invocations stop the background tasks. It would be nice to be able to sneak in a task or two during a DO callout for instance.


Another approach is to use a runloop observer:

void threadMain()
{
CFRunLoopObserverRef obs = CFRunLoopObserverCreate(..., kCFRunLoopBeforeWaiting, ..., beforeWaitingObserver, ...);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), obs, kCFRunLoopCommonModes);
CFRunLoopRun();
}


void
beforeWaitingObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
run_single_task();
// Make sure run loop wakes up immediately for the next task
if (!task_queue_empty)
CFRunLoopWakeUp(CFRunLoopGetCurrent());
}


This would work in the main thread as well, and I get to decide in which runloop modes my tasks are running.
Problem: CFRunLoop calls the beforeWaitingObserver before EVERY (version 1) source, not just before going to sleep. In other words, my background tasks get the same priority as the runloop sources. Not good.


Third approach: Prime a timer from the beforeWaitingObserver:

CFRunLoopTimerRef timer;
void threadMain()
{
timer = CFRunLoopTimerCreate(..., CFAbsoluteTimeGetCurrent() +1e10, 1e10, 0, 0, timerCallBack, ...);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);


CFRunLoopObserverRef obs = CFRunLoopObserverCreate(..., kCFRunLoopBeforeWaiting, ..., beforeWaitingObserver, ...);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), obs, kCFRunLoopCommonModes);


    CFRunLoopRun();
}

void
beforeWaitingObserver(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
if (!task_queue_empty)
CFRunLoopTimerSetNextFireDate(timer, CFAbsoluteTimeGetCurrent ()+0.010);
}


void
timerCallBack(CFRunLoopTimerRef timer, void *info)
{
    run_single_task();
}

This gets the priorities straight: As long as each source can do its business in 10ms, all sources will be processed before any background tasks are run.

Problem: To keep the background thread responsive, each task is very short, say 10ms. The timer causes a 10ms delay between tasks, so they run at half speed for no reason.

To fix this, we can poll the runloop from the timer callback:

void
timerCallBack(CFRunLoopTimerRef timer, void *info)
{
CFStringRef mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
while (!task_queue_empty && CFRunLoopRunInMode(mode,0,YES)! =kCFRunLoopRunHandledSource) {
run_single_task();
}
CFRelease(mode);
}


This actually seems to do the trick. The timer still causes a 10ms delay every time a source has been handled, but that might actually be a good thing. The main thread is likely to send multiple DO requests at a time, and we will be able to handle them quickly this way.

There should be some code to prevent the timer callback from being called recursively - I have omitted that for clarity.

It is not a very simple solution, though. There are also potential problems with CFRunLoopStop() and recursive runloop invocations.

I guess it would be possible to use a NSNotificationQueue with NSPostWhenIdle, but I suspect that causes delays too. I haven't tried it.

What is the usual approach to idle-time processing in a Cocoa app? Use a timer and live with the delays?

From http://developer.apple.com/documentation/Cocoa/Conceptual/ CocoaFundamentals/WhatIsCocoa/chapter_2_section_3.html
"Performance—To enhance the performance of your application, Cocoa provides programmatic support for multithreading, idle-time processing, lazy loading of resources, memory management, and run- loop manipulation."


I can't seem to find that support...

NSLayoutManager claims to do layout "when the runloop is idle". How is that done?



_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Prev by Date: Re: Bind to element in dictionary in array
  • Next by Date: Re: Objective-C++ Exceptions (was: PICT control problems)
  • Previous by thread: Re: Binding beginner question
  • Next by thread: Newb question, Website for Syntax search lookup ?
  • Index(es):
    • Date
    • Thread