articleSimple timer class

patrickr's picture

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

Comments

To see something, you might add a loop

Jocelyn Fiat's picture

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!!

Syndicate content