Sometimes in an application, some actions have to be repeatedly performed in a certain interval. This could for example be some cleanup of external data. To do this I wrote a small class that takes an agent and an interval.
indexing
description: "Execute an action every interval until stopped."
author: "Patrick Ruckstuhl <patrick@tario.org>"
date: "$Date$"
revision: "$Revision$"
class
TIMER
inherit
THREAD
export
{NONE} all
end
create
make
feature {NONE} -- Initialization
make (a_action: like action; a_interval: like interval) is
-- Create a timer that executes a_action every a_interval milliseconds.
require
a_action_set: a_action /= Void
a_interval_valid: a_interval > 0
do
action := a_action
interval := a_interval
create timer_mutex.make
create timer_condition.make
ensure
action_set: action = a_action
interval_set: interval = a_interval
end
feature -- Status
is_stop: BOOLEAN
-- Is the timer stopped?
feature -- Access
action: PROCEDURE [ANY, TUPLE[]]
-- Action to execute every interval.
interval: INTEGER
-- Milliseconds to wait until action is called again.
feature -- Commands
start is
-- Start the timer.
do
is_stop := False
launch
end
stop is
-- Stop the timer.
do
is_stop := True
timer_mutex.lock
timer_condition.signal
timer_mutex.unlock
exit
end
feature {NONE} -- Implementation
timer_mutex: MUTEX
-- Mutex for timer.
timer_condition: CONDITION_VARIABLE
-- Condition variable to wait on during the interval.
execute is
-- Main loop, executes action every interval until stopped.
local
l_tmp: BOOLEAN
do
timer_mutex.lock
from
until
is_stop
loop
action.call ([])
l_tmp := timer_condition.wait_with_timeout (timer_mutex, interval)
end
timer_mutex.unlock
end
invariant
action_set: action /= Void
interval_valid: interval > 0
timer_mutex_not_void: timer_mutex /= Void
timer_condition_not_void: timer_condition /= Void
end
On start, a new thread is created which executes a loop until is_stop is true. The waiting for the interval is done by using a CONDITION_VARIABLE which also allows to stop the execution at any moment, simply by signaling this CONDITION_VARIABLE after which, the loop condition is evaluated again, which ends the loop.
Sample usage of the class looks like this
indexing
description: "Sample"
author: "Patrick Ruckstuhl <patrick@tario.org>"
date: "$Date$"
revision: "$Revision$"
class
SAMPLE
create
make
feature {NONE} -- Initialization
make is
-- Create.
local
l_timer: TIMER
do
-- create a new timer, that print's hello worlds every 10 seconds
create l_timer.make (agent print("Hello World%N"), 10*1000)
l_timer.start
end
end
To see something, you might add a loop
To see something, I would just add the following points:
1) add THREAD_CONTROL to the inherited class for SAMPLE 2) at the end of make, you could add (for instance)
from until False loop print (".") sleep (1_000_000_000) end
How to stop it?
How can the timer be stopped?
The procedure stop has to lock the timer_mutex, which is already locked in the procedure execute. I tried on EiffelStudio 6.5 GPL with a short test and I had to remove the "timer_mutex.lock" and "timer_mutex.unlock" statements in the stop procedure to make it work.
Perhaps am I missing something?
thread_capable: {PLATFORM}.is_thread_capable
I am getting a precondition violation when attempting to use this code (see title).
I have looked to see why I am getting this, but cannot immediately determine. Can someone assist?
Self Solved! Groovy! :-)
So, answer was a combination:
1. I needed to set the Concurrency to Eiffel Thread in the project settings.
2. I needed to change the pre-compile from base to base-mt
I then did a clean compile and ran. It got me to where I needed to be.
Cheers!!