> You have to re-start the timer after it expires, i.e. call again
> expires_from_now(), async_wait() in the handler.

Didn't I do this in sample code?
 
ah... I didn't pay attention to the sample code :)).
Well, the problem is that poll() - according to the asio reference - just processes *ready* handlers, w/o blocking(), so timer isn't waited. So, as for your sample code, it shouldn't work even once, because noone waits for the timer - but problably in your actual code there're some other parts that run io_service and that's why it works sometimes.
However, if you call run() instead of poll() then the execution will be blocked until the timer fires, and your sample code will work as you expect.