haxe.Timer in cpp(win.console)

307 views
Skip to first unread message

Ma rk

unread,
Sep 3, 2014, 5:23:36 AM9/3/14
to haxe...@googlegroups.com
hi guys,

I'm trying to create a very simple test app in win.console: it starts a timer and writes text to stdout. It looks like this does not work.

Could you please advice?


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 the compiler params are:
>haxe.exe -lib nme -main TimerCallback -cpp .\bin

If I'm not adding "-lib nme" I get an error trying to create a new Timer (haxe.Timer's static functions available only for cpp, which is "by design" based on API doc - so I must use 'nme').
With this param all seems OK, code compiles, however, timer_OnTick event handler is never called.


I'm sure it is a very simple and silly mistake (based on lack of knowledge) but can't find out what's wrong. I also find it confusing that Timer.hx is pretty much everywhere.
[There's an nme.utils.Timer implementation too but for that, did not really see how could I actually catch the tick event]

Ma rk

unread,
Sep 3, 2014, 7:09:18 AM9/3/14
to haxe...@googlegroups.com
Somebodyanybody? back2dos? Please? :)

Dan Korostelev

unread,
Sep 3, 2014, 9:03:22 AM9/3/14
to haxe...@googlegroups.com
I think haxe.Timer just doesn't have an implementation for C++, because there's no single runtime with an event loop. The problem is that it silently fail instead of throwing an exception. Correct me if I'm wrong.

среда, 3 сентября 2014 г., 13:23:36 UTC+4 пользователь Ma rk написал:

Juraj Kirchheim

unread,
Sep 3, 2014, 9:05:26 AM9/3/14
to haxe...@googlegroups.com
Given that the hxcpp runtime doesn't have its own event loop, you will
need to find a different way of tackling the problem.

Generally you will want to have a main thread that operates the loop like so:

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<T> {
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
);
}

Please note that none of this is tested, but I hope you get the idea ;)

Best,
Juraj
> --
> To post to this group haxe...@googlegroups.com
> http://groups.google.com/group/haxelang?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Haxe" group.
> For more options, visit https://groups.google.com/d/optout.

Ma rk

unread,
Sep 3, 2014, 9:42:13 AM9/3/14
to haxe...@googlegroups.com
Thank you! I get the idea indeed. It seems to me now, however, Haxe is a little too fault-tolerant (IMHO). I realized it sinks most of the silly I implement, which is not lucky with my trial&error learning style :)

On the other hand, I do not understand the following, so a little more help would come handy:
If I add -lib nme as a compiler option, and the Timer.hx really is taken from nme (I checked it), and it has a "implementation for C++ and Neko" branch, and nme is said to be implementing it's own event loop (is that correct?), then how come the code did not work after all?
(My guessing is, nme implements only UI event loop -but I did not look into it yet-, so technically, a WM_* loop only so to speak, but I want to be sure I am correct -or not-)

Also, this Deque is the CPP one or the "neko" vm one? (No offense, but I would prefer CPP :) Or, "depends" (on what? :)?

Thank you again for your time and for all the help!

Juraj Kirchheim

unread,
Sep 3, 2014, 10:04:09 AM9/3/14
to haxe...@googlegroups.com
On Wed, Sep 3, 2014 at 3:42 PM, Ma rk <markk...@gmail.com> wrote:
> Thank you! I get the idea indeed. It seems to me now, however, Haxe is a
> little too fault-tolerant (IMHO). I realized it sinks most of the silly I
> implement, which is not lucky with my trial&error learning style :)
>
> On the other hand, I do not understand the following, so a little more help
> would come handy:
> If I add -lib nme as a compiler option, and the Timer.hx really is taken
> from nme (I checked it), and it has a "implementation for C++ and Neko"
> branch, and nme is said to be implementing it's own event loop (is that
> correct?), then how come the code did not work after all?
> (My guessing is, nme implements only UI event loop -but I did not look into
> it yet-, so technically, a WM_* loop only so to speak, but I want to be sure
> I am correct -or not-)

My guess is that the nme timer implementation just starts the
background thread for the timer which then pushes the callbacks to
some deque (so that its not execute on the background thread itself),
but there's no main loop to ever pick up the result, hence the
callbacks are never run. The main loop probably doesn't run because
that would require you to actually build with the nme build tool
rather than just -lib nme.

But Hugh can probably give a much better answer here ;)

> Also, this Deque is the CPP one or the "neko" vm one? (No offense, but I
> would prefer CPP :) Or, "depends" (on what? :)?

That depends on which one you import. You can just make a typedef that
picks the right one based on conditional compilation. They have the
same interface.

Best,
Juraj

Franco Ponticelli

unread,
Sep 3, 2014, 11:28:26 AM9/3/14
to haxe...@googlegroups.com
Juraj, your implementation is very interesting. Do you think there is a macro way to automagically inject/append the required code in Main.main() as soon as someone uses a Timer definition?
That would make for a seamless implementation of Timer for cpp/neko, right?


Zjnue Brzavi

unread,
Sep 3, 2014, 1:09:22 PM9/3/14
to haxe...@googlegroups.com
Hi,
 
(My guessing is, nme implements only UI event loop -but I did not look into it yet-, so technically, a WM_* loop only so to speak, but I want to be sure I am correct -or not-)

Also, this Deque is the CPP one or the "neko" vm one? (No offense, but I would prefer CPP :) Or, "depends" (on what? :)?

Hugh posted the essence of his approach to the list in the past.
I've picked it up and modified it slightly to the digested version below, which I've just tested for various targets using Haxe 3.1.3

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<haxe.Timer>;
    #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<TTimerData>;
    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<Float> = null;
            var now = haxe.Timer.stamp();
            var ready = new Array<TTimerData>();
            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

Ma rk

unread,
Sep 4, 2014, 3:08:02 AM9/4/14
to haxe...@googlegroups.com
Juraj, Zjnue, thank you for the solutions!

I'll put them out on SO too in case anyone needs them. Let me know if you mind me doing this :)

Juraj Kirchheim

unread,
Sep 4, 2014, 4:31:34 AM9/4/14
to haxe...@googlegroups.com
No problem ;)

dlots

unread,
Sep 5, 2014, 11:25:41 AM9/5/14
to haxe...@googlegroups.com
Back2dos, what is the advantage of the event loop vs:

self contained Timer_manager thread (similar to what has been posted, except with a decrementing delta correctly from repeating non-executed Timer_tasks)
In the instance of this example, deactivating the timer instant subsequent to the while loop exiting instead of queing it into an the event loop

I guess the question are:

1. What other functionality uses an event loop (as opposed to spawning yet another thread for self contained functionality)?
2. What other other patterns casually use an event queue in your experience? (eg perhaps it is quite convenient having a general purpose event queue to use instead of having to write self contained threading for a fucntionality)

Juraj Kirchheim

unread,
Sep 5, 2014, 3:42:41 PM9/5/14
to haxe...@googlegroups.com
Multithreading is really not my strong suit, but I'll explain what I
think to understand ;)

In my example, the main thread operates the run loop. The
implementation of asynchronous tasks runs on background threads, the
callbacks are dispatched back onto the main thread.

This has two advantages:

1. The run loop will exit when its done. The alternative solution puts
the main thread into a busy wait (that'll max out one core) and
requires an explicit Sys.exit or something to terminate.
2. The programming model is very similar to that of the flash runtime
or the browser or nodejs. If you structure the code so that background
threads do not fiddle with data belonging to the main thread, you are
thread safe. It's a convenient way to just pass to an event based
model, where you can largely pretend everything is single threaded.
Still, because you have actual threads at your disposal, you can mix
in multithreading more intricately (although I'm not saying you should
;)).

I think this covers it pretty well: http://en.wikipedia.org/wiki/Reactor_pattern

Best,
Juraj

dlots

unread,
Sep 5, 2014, 4:51:55 PM9/5/14
to haxe...@googlegroups.com
Ahh, I see the advantage with regards to general purpose concurrency. I dont think one should spawn a new thread for every Timer though. I think Timer object should be managed in a thread. I think the event loop is a matter of preference and convenience with regards to where the callback is executed to provide thread safety. I think your implementation is of course preferred in almost all instances.

Can you elaborate/psuedocode what you mean when you say the alternative would be to have the main thread in a busy wait?
Long running applications (clients,servers) the main thread is managed to never end and never overloop.
Perhaps you mean incorrectly enforcing a cli application's life past it's main execution... but I think that's a matter of context. Again I dont know what you mean.

lock.wait() doesn't appear to cause 100% cpu utiliization like a while(true) {}...

Zjnue Brzavi

unread,
Sep 5, 2014, 5:48:31 PM9/5/14
to haxe...@googlegroups.com
Hi dlots,
 
lock.wait() doesn't appear to cause 100% cpu utiliization like a while(true) {}...

Absolutely right - my rushed attempt to help by wrapping some copy-n-pasted code may have done more harm than good.
while(true) is certainly not the suggested use, the Timer management does not clean up after itself and there is no finer grained per-Timer control.

As far as I understand (speaking from limited experience), event-driven environments typically utilize a blocking queue to store all events to be processed.
When one processing cycle completes, the queue is checked (with a queue.dequeue() operation) and if empty, the execution waits there until the next one arrives (enters the queue), after which execution continues with the corresponding processing cycle.

If interested in some pseudo code, there is a nice example here: http://www.w3.org/TR/scxml/
Just search for the string "externalEvent = externalQueue.dequeue()".

br,
Zjnue

dlots

unread,
Sep 5, 2014, 8:41:06 PM9/5/14
to haxe...@googlegroups.com
@Zjnue I was referring to back2dos' statements, though your input on the general patterns with regards to concurrency are valuable. I didn't contemplate your code in depth, as I intend to implement a full implement of Timer, but I don't see any problem with your code in particular with regards to what it aims to accomplish (namely non repeating Timer.delays).

On Wednesday, September 3, 2014 5:23:36 AM UTC-4, Ma rk wrote:

Juraj Kirchheim

unread,
Sep 6, 2014, 2:24:48 AM9/6/14
to haxe...@googlegroups.com
On Fri, Sep 5, 2014 at 10:51 PM, dlots <image...@gmail.com> wrote:
> Ahh, I see the advantage with regards to general purpose concurrency. I dont
> think one should spawn a new thread for every Timer though. I think Timer
> object should be managed in a thread. I think the event loop is a matter of
> preference and convenience with regards to where the callback is executed to
> provide thread safety. I think your implementation is of course preferred in
> almost all instances.

Well, I don't know about my implementation. I've never even tried to
compile that code so be warned. In fact upon some thought it seems the
loop will actually just die off, because any `retain` will be queued
after the first `release`. This should work though:

static function main() {
entryPoint();
release();
}

But more importantly: it's a matter of two different approaches. In
the one I illustrated you're getting rid of concurrency at the cost of
non-linear execution. I prefer it, because I'm used to it so I find it
easier to reason about. I'm not sure though it's generally the best
solution.

> Can you elaborate/psuedocode what you mean when you say the alternative
> would be to have the main thread in a busy wait?
> Long running applications (clients,servers) the main thread is managed to
> never end and never overloop.
> Perhaps you mean incorrectly enforcing a cli application's life past it's
> main execution... but I think that's a matter of context. Again I dont know
> what you mean.
>
> lock.wait() doesn't appear to cause 100% cpu utiliization like a while(true)
> {}...

I am referring to this piece of code from the main thread:

var count = 0;
while( true ) {
if( count == 0 ) {
Timer.delay(function() trace("doThing1"), 3000);
Timer.delay(function() trace("doThing2"), 1000);
count++;
}
}

After the first iteration, this is an empty while (true) loop, or am I
missing something?

Best,
Juraj

Todor Angelov

unread,
Sep 6, 2014, 2:55:38 AM9/6/14
to haxe...@googlegroups.com
you don't need threads to run Timer or your cpp target may not support Threads. depends of use case there is many ways to catch UserInput / MainLoopExit. for example in MainLoop check if file / envvar exist, read web server response, sys signal etc ...

Jonas Malaco Filho

unread,
Sep 6, 2014, 3:36:36 AM9/6/14
to haxe...@googlegroups.com
An alternative Timer implementation can be made using `cpp.vm.Lock.wait`:

@:publicFields
class AnotherTimer {

   
var aux = new Lock();
   
var timeout:Float;  // in seconds

   
function new(time_ms:Int) {
       
this.timeout = 1e-3*time_ms;
   
}

   
dynamic
   
function run() {
       
// trace("run");
   
}

   
function start() {
        loop
();
   
}

   
function stop() {
        aux
.release();
   
}
   
   
private
   
function loop() {
       
while (!aux.wait(timeout)) run();
   
}

}


This could be used in several ways, with or without additional threads; since my `Std.getChar` is blocking here (the docs say it shouldn't but I haven't checked what's wrong) I'll show the threaded approach, but you could alternatively embed stop checks in the `onTick` function.

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

   
public static function main() : Void {
       
var myME = new TimerTicker();


       
function stopWhen() {

           
while (Sys.getChar(false) != CHAR_SPACE) {
               
//loop until [space] detected, do nothing here
           
}

            myME
.stop();
       
}

       
Thread.create(stopWhen);
        myME
.start();
   
}

}

class TimerTicker {
   
private var myTimer : AnotherTimer = null;

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

   
public function stop() {
        myTimer
.stop();
   
}

   
private function timer_OnTick() : Void {
       
Sys.print("foobar\n\r");  // \n\r instead of println is a workaround
                                 
// for a bug in the Sys.getChar version I
                                 
// have (some recent git)
   
}
}


Full source and another usage example: https://github.com/jonasmalacofilho/one-time/tree/master/another-timer


Todor Angelov

unread,
Sep 6, 2014, 6:50:39 AM9/6/14
to haxe...@googlegroups.com
Sys.getChar() will block until someone push the space button because depend of underlying  OS
So your MainLoop will be executed only one time if any ...
There is no universal and portable solution to fix this.
google "read key without blocking in c"

So
1. if you no need mouse/kbd input put Timer.run in MainLoop
2. OR use threads if there is no conflict with another library/app design, but threads are evil (http://www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf)


You received this message because you are subscribed to a topic in the Google Groups "Haxe" group.

Jonas Malaco Filho

unread,
Sep 6, 2014, 9:46:03 AM9/6/14
to haxe...@googlegroups.com
@Todor,

Thanks for the info on the `Sys.getChar`/blocking thing.

Also, what did you mean by "So your MainLoop will be executed only one time if any..."?

Todor Angelov

unread,
Sep 6, 2014, 10:03:58 AM9/6/14
to haxe...@googlegroups.com
it was a joke. i imagine a human creature in front of the pc hitting buttons to run that poor While cycle :))) but if the program is a daemon process who will do it?
sorry my terrible english, no Wales girl around. 

non-blocking getChar has a SDL, ncursess , etc but no sense for small app include those monsters ..




--

Todor Angelov

unread,
Sep 6, 2014, 1:38:19 PM9/6/14
to haxe...@googlegroups.com
=============  build.hxml ======================
-main TimerExample 
-D thread
-neko TimerExample.n
#-cmd neko TimerExample

--next
-main TimerExample 
-D thread
-cpp bin
============================================
---
==========  TimerExample.hx ===================
package;

#if thread
 #if neko
import neko.vm.Thread;
 #else
import cpp.vm.Thread;
 #end
#end

class TimerExample {

var count = 0;
var tm:Timer;
var CyclesPerSecond = 10;
var wake:Float;
var last:Float;
public function new()
{
#if thread
var child = Thread.create(newKbd);
child.sendMessage(Thread.current());
#end
var msg = #if thread "Enter" #else "Ctrl-C" #end + " to exit";
        Sys.println("Timer Example");
Sys.println(msg);
var ms = 1 / CyclesPerSecond;
var miliseconds = ms + 234 ;
tm = new Timer(miliseconds);
tm.run = info;
// MainLoop
        while (true) {
last = Timer.stamp();
tm.step();
mainCode();
wake = ms - (Timer.stamp() - last); 
if(wake > 0)Sys.sleep(wake);
count++;
#if thread
var msg = Thread.readMessage(false);
if (msg == 13) Sys.exit(0);
#end
}
}// new()
function mainCode() {
var t = 0;
for (i in 0...1000000)t = i * i;
}
function newKbd()
{
var tk = new ThreadKeyboard();
}//
function info()
{
Sys.println("Timer: "+tm.count + ", MainLoop: "+count);
}
    static function main() 
    {
var c = new TimerExample();
    }//main()
    
}// TimerExample

/**
 * Of course Timer class must be rewriten to hold many timers.
 **/ 
class Timer {
var ms:Float;
var last:Float;
var cur:Float;
public var count = 0;
public function new(tms = 500.)
    {
ms = tms / 1000;// seconds
Sys.println("Timer will run every "+ms*1000 + " miliseconds");
last = stamp(); //trace(last);
}// new()

public function step()
    { 
var go = false;
cur = stamp();
if ((cur - last) > ms) { 
last = cur;
run();
count++;
}
}// step()
public static function stamp(){ return Sys.time();}
public dynamic function run() { }
}// Timer

class ThreadKeyboard {

public function new()
    {
#if cpp
var owner = cpp.vm.Thread.readMessage(true);
#else
var owner = neko.vm.Thread.readMessage(true);
#end
while(true){ 
var msg = Sys.getChar(false);
owner.sendMessage(msg);
}
}// new()

}// ThreadKeyboard

============================================

On Wednesday, September 3, 2014 11:23:36 AM UTC+2, Ma rk wrote:

Hugh

unread,
Sep 8, 2014, 1:20:12 AM9/8/14
to haxe...@googlegroups.com
I guess the first question is do you want sync or async timer callbacks?  Sync is probably the most "cross platform" way.
I think  the haxe timer class could easily be extended for non event driver apps by having a function "processTimers()". So like this:

public static function main()
{
   
var count = 10;
   
Timer.delay(function() trace("Hi"), 1000 );
   
Timer.processTimers();
}




And a sync processTimers can be created without any threads, by creating a list of pending timers.

public static function processTimers()
{
   
while(true)
   
{
       
var nextTimer = findNextTimer();  // Get one with least due time
       
if (nextTimer==null)
         
break;
       
var delay = nextTimer.due - stamp();
       
if (delay>0)
          sleep
(delay);
       nextTimer
.run();
   
}

}



Whether you want to automagically call processTimers() via macro or otherwise is a minor matter, but explicit calling is not that hard.

Hugh

unread,
Sep 10, 2014, 12:54:11 AM9/10/14
to haxe...@googlegroups.com
Rereading your question, it seems like you clearly do not want sync timers, since your main thread is not just sitting around waiting for stuff to happen (like in nme/html/as3) but is 100% blocking doing other stuff, so I think Zjnue's solution is what you are after.

However, I do not think this is a good default implementation for the haxe.Timer class, since it is at odds with the way it works when it is supported natively, but would be a good alternate library, like "import async.Timer;"  and then carry on as you have written it.

Hugh
Reply all
Reply to author
Forward
0 new messages