Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I run code block in background periodically using GCD?

How can I run code block in background periodically using GCD? I am trying to write a game engine with several subsystems, like rendering, physics, game logic and so on. Some tasks should be event-driven, but some (like physics system) should be called periodically in the background with constant time (for example after 1/100 sec). I created a block of code, but how can I run this block periodically in background? Is GCD right tool here?

like image 209
Nik Avatar asked Dec 18 '11 13:12

Nik


3 Answers

What you want is a GCD dispatch source. For sample code, see the Creating a Timer Example.

like image 65
matt Avatar answered Nov 02 '22 16:11

matt


As @matt notes in the comments, you can use timer dispatch sources for this. See his answer for the right approach there.

For posterity, here's my original answer with some alternatives:

  1. In your render callback (say, using CADisplayLink), fire off a GCD job that computes the physics for this (or the next?) frame. If you need to do more than one update per render frame, just have that job loop a couple times.

  2. Have a physics thread that sleeps itself until it next needs to do some computation. If you have a separate thread for this, NSTimer might have sufficient resolution to wake your thread up at 100 Hz. (On the main thread, this wouldn't work because other input sources on the runloop will prevent it from firing that fast consistently.) If NSTimer doesn't work, just compute how much time is left until the next physics update you need and sleep your thread (via, e.g., [NSThread sleepForTimeInterval:])

  3. Have a block that uses dispatch_after.

Something like this:

dispatch_time_t next = dispatch_time(DISPATCH_TIME_NOW, 0);
block = ^{
    next = dispatch_time(next, 10000000L); // increment by 10 ms
    // do physics here
    dispatch_after(next, queue, block);
}

You might need to be a bit smarter about dropping frames, (i.e. detect if your next time is in the past before calling dispatch_after it, etc.)

like image 6
Jesse Rusak Avatar answered Nov 02 '22 15:11

Jesse Rusak


In Swift you can create timer using GCD:

func CreateTimerDispatchSource(interval: UInt64, leeway: UInt64, queue: dispatch_queue_t, block: dispatch_block_t) -> dispatch_source_t {
    let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
    dispatch_source_set_timer(timer, dispatch_walltime(nil, 0), interval, leeway)
    dispatch_source_set_event_handler(timer, block)
    return timer;
}

var timer = CreateTimerDispatchSource(5*NSEC_PER_SEC, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
  // do some serious stuff
}

Start or resume timer:

dispatch_resume(timer)

Suspend timer:

dispatch_suspend(timer)
like image 3
mixel Avatar answered Nov 02 '22 15:11

mixel