tink_runloop 0.1.0

Cross platform run loop abstraction

Released 2016-04-14.

To install, run:

haxelib install tink_runloop 0.1.0

See using Haxelib in Haxelib documentation for more information.

Current version0.1.0
StatisticsInstalled 826 times
Tags cross, event, tink, utility


Tink Runloop

Build Status

This library provides a cross platform run loop abstraction. It works in a single threaded mode, but does leverage multiple threads when available.

The very basis of the library is the concept of a worker, with the runloop itself being also a worker. Each worker can performed tasks. In this README we shall only sketch out minimal versions of the types involved to avoid complicating the matter.

A tasks, simply put, look very much like this:

abstract Task {
	var recurring(get, never):Bool;
	var state(get, never):TaskState;
	function cancel():Void;	
	function perform():Void;
  @:from static function ofFunction(f:Void->Void):Task;
  @:from static function repeat(f:Void->TaskRepeat):Task;
  static var NOOP(default, null):Task;

enum TaskState {

enum TaskRepeat {

Most of the time, you will want to simply create tasks from anonymous functions through the implicit conversion.

Tasks are meant to be run by workers, which essentially boild down to this:

interface Worker {	
	function work(task:Task):Task;
	function atNextStep(task:Task):Task;
	function asap(task:Task):Task;
  function kill():Void;

The default implementation of the worker has an internal queue of scheduled tasks, that are performed step by step. Commonly, you will want to add tasks at the end of the queue through work, but you can also use atNextStep to add the task at the beginning. If you're in a rush, then you can perform a task through asap, which if the calling thread is the thread that the worker runs on (which is always the case in single threaded environments), will perform the task immediately, and otherwise will add it at the beginning of the worker's queue. Try using this sparsely. Without calls to asap, it is guaranteed that a worker performs only one task at a time. Also, any task can only be performed by one worker at a time.

Run loops are particular implementors of the Worker interface and can be described like so:

class RunLoop implements Worker {
  static var current(get, never):RunLoop;
  function createSlave():Worker;

Currently, there is only one run loop, but that may change in the future - if a use case presents itself. You may get by, simply scheduling all tasks on the run loop and be done. But you can also create slaves. In single threaded mode, they progress when the run loop itself is idle, so they are suitable for background tasks.

Consider something like this:

import haxe.zip.*;
import tink.core.*;
class BackgroundCompression {
  static public function compress(entries:List<Entry>, level:Int, worker:Worker):Future<Noise> {
    for (e in entries)
      worker.work(function () if (!e.compressed) e.data = haxe.zip.Compress.run(e.data, level));
    return RunLoop.current.delegate(Noise, worker);

This allows you offloading compression into a slave like so:

BackgroundCompression.compress(someEntries, 9, RunLoop.current.createSlave());

Ideally, you'll want to pool slaves to avoid creating too many.