Search code examples
haxenmehxcpp

Timer in command-line application


Trying to create a simple command-line application in Haxe which has a ticking Timer, but it does not seem to work out; the Timer never actually starts 'ticking'.

package;

import haxe.Timer;

class TimerCallback {
    private static inline var CHAR_SPACE : Int = 32;

    public static function main() : Void {
        var myME = new TimerTicker();
        while (Sys.getChar(false) != CHAR_SPACE) {
            //loop until [space] detected, do nothing here
        }
    }
}

class TimerTicker {
    private var myTimer : Timer = null;

    public function new() {
        myTimer = new Timer(20);
        myTimer.run = timer_OnTick;
    }

    private function timer_OnTick() : Void {
        Sys.println ("foobar");
    }
/* destructor?! */
}


And this is the build command:

>haxe.exe -lib nme -main TimerCallback -cpp .\bin

If I am not adding -lib nme, the code does not compile (based on API doc it's OK as Timer is not supported for cpp, so only static functions are available)

If I am adding this, however, the code is compiled -as nme supports cpp Timers-, and the exe is created (win), but timer_OnTick() is never called. So exe starts, nothing happens, one press SPACE and app. quits.

Additional info:
- The imported Timer.hx file is this one: haxe\lib\nme\5,1,8\haxe. And if I am right, this should be OK & working.
- Using haxe 3.1.3, nme 5.1.8, hxcpp 3.1.39 (and haxelib 3.1.0-rc.4 if matters)

Any help would be much appreciated.


Solution

  • Okay, I've got help from the Haxe Community (mailing list). Here are the solutions if anyone happen to need them:

    PSEUDO CODE (not tested)

    class RunLoop {
       static var queue = new Deque<Void->Void>();
       static var keepAlives:Int; = 1;

    static public function release() enque(function () keepAlives--);

    static public function retain() enque(function () keepAlives++);

    static public function enque(task:Void->Void) queue.add(task);

    static function main() { enque(entryPoint); release(); } static function entryPoint() { //code goes here } static function run() while (keepAlives:Int > 0) queue.pop()(); }

    //Now you can go an implement a timer like so:

    class Timer { var active:Bool = true; public function new(msecs:Int) { RunLoop.retain(); Thread.create(function () while(active) { Sys.sleep(msecs / 1000); if (active) RunLoop.enque(this.run); }); } public dynamic function run() {} public function stop() { active = false; RunLoop.release(); } }

    //And a helper for blocking code:

    class Task { var task:Void->T; var onDone:T->Void; public function new(task:Void->T, onDone:T->Void) { RunLoop.retain(); Thread.create(function () { var result = task(); RunLoop.enque(onDone.bind(result)); }); } }

    //So then the code you want would look roughly like this:

    static function entryPoint() { var timer = new Timer(); timer.run = function () trace('foobar'); function waitForSpace() { while (Sys.getChar(false) != CHAR_SPACE) { //loop until [space] detected, do nothing here } return true; } new Task( waitForSpace, function (_) timer.stop() //stop the timer so that the run loop can exit ); }

    (solution provided by back2dos)

    /*
    1. Neko : works
    2. C++: works, However, incrementing count in the if statement instead ( if( count++ == 0 ) { ... ) fails to increment count!  Fixed on Git?
    3. Flash : works
    4. Java : fails using Haxe 3.1.3
    */

    ### build.hxml ###

    -main Main -swf main.swf -swf-version 12

    --next

    -main Main -neko main.n

    --next

    -main Main -cpp cpp -cmd cp cpp/Main ./main

    --next

    -main Main -java java -cmd cp java/Main.jar ./main-jar

    ### Main.hx ###

    class Main { public static function main() { #if sys var count = 0; while( true ) { if( count == 0 ) { Timer.delay(function() trace("doThing1"), 3000); Timer.delay(function() trace("doThing2"), 1000); count++; } } #else Timer.delay(function() trace("doThing1"), 3000); Timer.delay(function() trace("doThing2"), 1000); #end } }

    ### Timer.hx ###

    #if neko import neko.vm.Deque; import neko.vm.Thread; import neko.vm.Mutex; import neko.vm.Lock; #elseif cpp import cpp.vm.Deque; import cpp.vm.Thread; import cpp.vm.Mutex; import cpp.vm.Lock; #elseif java import java.vm.Deque; import java.vm.Thread; import java.vm.Mutex; import java.vm.Lock; #end

    class Timer { #if sys static var timerThread : TimerThread; #else static var timers : Array; #end

    static function __init__() {
        #if sys
        timerThread = new TimerThread();
        #else
        timers = [];
        #end
    }
    
    public static function stop() {
        #if sys
        timerThread.quit();
        #else
        for( t in timers )
            t.stop();
        #end
    }
    
    public static function delay( func : Void -> Void, delayMillis : Int ) {
        #if sys
        timerThread.addTimer(delayMillis/1000, func);
        #else
        timers.push( haxe.Timer.delay(func, delayMillis) );
        #end
    }
    

    } #if sys typedef TTimerData = { time : Float, func : Void -> Void
    }

    class TimerThread { var mutex : Mutex; var queueLock : Lock; var queue : Array; var running : Bool; public function new() { queue = []; queueLock = new Lock(); mutex = new Mutex(); running = true; Thread.create( mainLoop ); } public function addTimer( delaySec : Float, cb : Void -> Void ) { mutex.acquire(); var time = haxe.Timer.stamp() + delaySec; var index = 0; while( index < queue.length && time >= queue[index].time ) index++; queue.insert(index, { time : time, func : cb }); mutex.release(); queueLock.release(); } public function quit( ?cb : Void -> Void ) { var me = this; addTimer( 0, function() { me.running = false; if( cb != null ) cb(); } ); } function mainLoop() { while( running ) { var wake : Null = null; var now = haxe.Timer.stamp(); var ready = new Array(); mutex.acquire(); while( queue.length > 0 ) if( queue[0].time <= now ) ready.push(queue.shift()); else { wake = queue[0].time; break; } mutex.release(); for( d in ready ) { d.func(); if( !running ) break; } if( !running ) break; if( wake == null ) queueLock.wait(); else { var delay = wake - haxe.Timer.stamp(); if( delay > 0 ) queueLock.wait(delay); } } } } #end

    (Solution provided by Zjnue, modified code of Hugh)