1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2002-2023, University of Amsterdam 7 VU University Amsterdam 8 CWI, Amsterdam 9 SWI-Prolog Solutions b.v. 10 All rights reserved. 11 12 Redistribution and use in source and binary forms, with or without 13 modification, are permitted provided that the following conditions 14 are met: 15 16 1. Redistributions of source code must retain the above copyright 17 notice, this list of conditions and the following disclaimer. 18 19 2. Redistributions in binary form must reproduce the above copyright 20 notice, this list of conditions and the following disclaimer in 21 the documentation and/or other materials provided with the 22 distribution. 23 24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 POSSIBILITY OF SUCH DAMAGE. 36*/ 37 38:- module(time, 39 [ alarm/3, % +Time, :Callable, -Id 40 alarm/4, % +Time, :Callable, -Id, +Options 41 alarm_at/3, % +Time, :Callable, -Id 42 alarm_at/4, % +Time, :Callable, -Id, +Options 43 remove_alarm/1, % +Id 44 install_alarm/1, % +Id 45 install_alarm/2, % +Id, +Time 46 uninstall_alarm/1, % +Id 47 current_alarm/4, % ?At, ?:Goal, ?Id, ?Status 48 call_with_time_limit/2, % +Time, :Callable 49 call_with_time_limit/3 % +Time, :Callable, +Context 50 ]). 51:- autoload(library(lists),[member/2]). 52 53:- set_prolog_flag(generate_debug_info, false). 54 55:- meta_predicate 56 call_with_time_limit( , ), 57 call_with_time_limit( , , ), 58 alarm( , , ), 59 alarm( , , , ), 60 alarm_at( , , , ), 61 current_alarm( , , , ). 62 63:- predicate_options(alarm/4, 4, 64 [ remove(boolean), 65 install(boolean) 66 ]). 67 68 69/** <module> Time and alarm library 70 71The library(time) provides timing and alarm functions. Alarms are 72thread-specific, i.e., creating an alarm causes the alarm goal to be 73called in the thread that created it. The predicate current_alarm/4 only 74reports alarms that are related to the calling thread. If a thread 75terminates, all remaining alarms are silently removed. Most applications 76use call_with_time_limit/2. 77*/ 78 79%! alarm(+Time, :Callable, -Id) is det. 80%! alarm(+Time, :Callable, -Id, +Options) is det. 81% 82% Set up an alarm to be signaled Time seconds from now. If the 83% alarm expires, Callable is called asynchronously. Callable can 84% be used to raise an exception using throw/1 to abort some 85% execution. 86% 87% Options is a list of Name(Value) options. Currently defined 88% options are: 89% 90% * remove(Bool) 91% If =true= (default =false=), remove the alarm-event (as 92% remove_alarm/1) after it has been fired. 93% * install(Bool) 94% If =false= (default =true=) do not install the alarm. 95% It must be installed separately using install_alarm/1. 96 97%! alarm_at(+Time, :Callable, -Id) is det. 98%! alarm_at(+Time, :Callable, -Id, +Options) is det. 99% 100% As alarm/3 and alarm/4, but schedule the alarm at an absolute 101% point in time. 102% 103% @see date_time_stamp/2. 104 105%! install_alarm(+Id) is det. 106%! install_alarm(+Id, +RelTime) is det. 107% 108% Install an alarm allocated using alarm/4 with the install(false) 109% option or de-activated using uninstall_alarm/1. With a given 110% RelTime, the alarm is scheduled at the RelTime from now. 111% Otherwise it is scheduled on the same (absolute) time on which 112% is was created. 113 114%! uninstall_alarm(+Id) is det. 115% 116% De-activate an alarm. This does _not_ invalidate Id, but ensures 117% that the alarm will not fire. The alarm can be rescheduled to 118% the original time using install_alarm/1 or to a new time using 119% install_alarm/2. 120 121%! remove_alarm(+Id) is det. 122% 123% Remove an alarm. If it has not yet been fired, it never will. 124 125%! current_alarm(?Time, :Goal, ?Id, ?Status) is nondet. 126% 127% Enumerate the alarms in the schedule. Time is the absolute time 128% the event is scheduled for (see also get_time/1). Goal is the 129% goal to execute, Id is the identifier and Status is the 130% scheduling status. It takes the value =done= if the alarm has 131% been fired, =next= if the event is the next to be executed and 132% =scheduled= otherwise. 133 134:- use_foreign_library(foreign(time)). 135:- public time_debug/1. % set debugging 136 137%! call_with_time_limit(+Time, :Goal) is det. 138%! call_with_time_limit(+Time, :Goal, +Context) is det. 139% 140% Call Goal, while watching out for a (wall-time) limit. If this limit 141% is exceeded, the exception `time_limit_exceeded` is raised. 142% call_with_time_limit/3 throws time_limit_exceeded(Context). Goal is 143% called as in once/1. 144% 145% @throws `time_limit_exceeded` (call_with_time_limit/2) or 146% time_limit_exceeded(Context) (call_with_time_limit/3). 147 148call_with_time_limit(Time, Goal) :- 149 call_with_time_limit(Time, Goal, '$no_ctx'). 150 151call_with_time_limit(Time, Goal, Ctx) :- 152 Time > 0, 153 !, 154 setup_call_cleanup(alarm(Time, time_limit_exceeded(Time, Ctx), 155 Id, [install(false)]), 156 run_alarm_goal(Id, Goal), 157 remove_alarm_notrace(Id)). 158call_with_time_limit(_Time, _Goal, _Ctx) :- 159 throw(time_limit_exceeded). 160 161run_alarm_goal(AlarmID, Goal) :- 162 install_alarm(AlarmID), 163 , 164 !. 165 166time_limit_exceeded(_Time, Ctx) :- 167 ( Ctx == '$no_ctx' 168 -> throw(time_limit_exceeded) 169 ; throw(time_limit_exceeded(Ctx)) 170 ). 171 172current_alarm(Time, Goal, Id, Status) :- 173 current_alarms(Time, Goal, Id, Status, List), 174 member(alarm(Time, Goal, Id, Status), List). 175 176 /******************************* 177 * HANDLE MESSAGES * 178 *******************************/ 179 180:- multifile 181 prolog:message/3. 182 183prologmessage(time_limit_exceeded) --> 184 [ 'Time limit exceeded' ]. 185prologmessage(time_limit_exceeded(Context)) --> 186 [ 'Time limit exceeded: ~p'-[Context] ]. 187 188 /******************************* 189 * ALARM * 190 *******************************/ 191 192:- multifile sandbox:safe_meta_predicate/1. 193 194sandbox:safe_meta_predicate(time:call_with_time_limit/2)