REBOL [ Title: "Timers" Purpose: { Defines the functions DO-AT, DO-EVERY and DO-AFTER. } Author: "Gabriele Santilli" EMail: giesse@rebol.it File: %timers.r License: { Copyright (c) 2003, Gabriele Santilli All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of Gabriele Santilli may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. } Date: 8-Feb-2005 Version: 1.9.0 ; majorv.minorv.status ; status: 0: unfinished; 1: testing; 2: stable History: [ 5-Apr-2004 1.1.0 "History start" 6-Apr-2004 1.2.0 "Little bug fix (actions were not called if defined after calling do-events)" 7-Apr-2004 1.3.0 "Now using /precise in DO-AFTER and DO-EVERY too" 25-Nov-2004 1.4.0 "Corrected documentation" 25-Nov-2004 1.5.0 "Added REMOVE-TIMER" 29-Nov-2004 1.6.0 "Minor fix to REMOVE-TIMER" 29-Nov-2004 1.7.0 "Fixed a typo" 15-Dec-2004 1.8.0 "DO-XXX, REMOVE-TIMER and CLEAR-TIMERS are now safe to use inside action" 8-Feb-2005 1.9.0 "Now avoids to insert a negative value in the wait list" ] ] ; comment the following line if you are using autodoc.r ;#do [document: func [text] [none]] #do [document { ===Global timers (timers.r) This script defines a set of functions to execute actions at a given time or at repeated intervals. Timers are only handled while inside DO-EVENTS. }] context [ #do [document { Internally it mantains a queue of actions to execute, with the time when they should be executed, and if they should be repeated. Then a TIME! value (the difference between now and the time of the next action) is added to SYSTEM/PORTS/WAIT-LIST. To avoid problems with changes to the queue happening during processing of the queue itself, while executing actions changes to the queue are delayed. }] changes: [ ] timers: [ ] enqueue-action: enqueue-action1: func [when [date!] every [number! time! none!] action [block!] /local pos] [ pos: timers if number? every [every: to time! every] while [all [ not tail? pos when >= pos/1 ]] [pos: skip pos 3] insert/only insert insert pos when every action setup-wait difference timers/1 now/precise ] enqueue-action2: func [when [date!] every [number! time! none!] action [block!]] [ insert/only insert insert insert tail changes 'enqueue-action1 when every action ] #do [document { The REMOVE-TIMER function can be used to cancel a previously scheduled action. Return value is undefined. }] remove-timer: remove-timer1: func [action [block!] /local pos] [ pos: timers until [ any [none? pos: find/only next pos action same? pos/1 action] ] if pos [ remove/part skip pos -2 3 either empty? timers [ clear-wait ] [ setup-wait difference timers/1 now/precise ] ] ] remove-timer2: func [action [block!]] [ insert/only insert tail changes 'remove-timer1 action ] setup-wait: func [amount [number! time!] /local pos] [ either pos: find system/ports/wait-list time! [ change pos max 0:00 to time! amount ] [ insert tail system/ports/wait-list max 0:00 to time! amount ] ] clear-wait: does [ if pos: find system/ports/wait-list time! [remove pos] ] #do [document { When this time has passed, the queue is processed for actions that should be executed, because their execution time is less or equal to now; if the action is repetitive, it is readded to the queue after bumping the execution time. }] process-timers: does [ ;print ">process-timers" before-action while [all [ not empty? timers now/precise >= timers/1 ]] [ ;print mold timers/3 if all [do timers/3 timers/2] [ enqueue-action timers/1 + timers/2 timers/2 timers/3 ] remove/part timers 3 ] after-action ;print "