catch/3 and call_with_time_limit/2 predicates in SWI-Prolog
Asked Answered
U

2

7

I want to use

catch(:Goal, +Catcher, :Recover)

where Goal is

call_with_time_limit(+Time, :Goal)

It's messed up and I can't find the right way to know when one of the above happened:

1) Goal stopped because of time out.

2) Goal failed (it's suppose to fail sometimes).

I tried:

(catch(call_with_time_limit(Time, Goal), Catcher, Recover) ->
(ground(Catcher), Catcher = time_limit_exceeded), **TIMEOUT ACTIONS**)
;
(**SUCCESS ACTIONS**))
;
**FAILURE ACTIONS**
)

* EDIT *

Pattern:

I use the following pattern:

((catch(call_with_time_limit(6,goal),
    Exception,
    true),(var(Exception);Exception=time_limit_exceeded))
->
    (var(Exception) -> writeln(succ) ; writeln(timeout))
;
    writeln(fail)
).

That pattern doesn't work for 4 or more seconds - it just ignores the request for timeout.

Uncrowned answered 27/5, 2014 at 23:18 Comment(4)
@false - seems you accidentally removed the swi-prolog tag. The question is specifically about an SWI built-in and even mentions SWI in the title.Amando
@jschimpf: As it turned out, it was not specific to SWI. That is: catching is not specific to SWI, nor timeouts.Crossbill
@false: there are lots of truly generic Prolog questions where I don't mind your aggressive tag removal. This is not such a case. Timeouts are not part of "the Prolog language". The question explicitly asks about SWI Prolog's call_with_time_limit/2. Have you considered that an SWI expert may only follow the swi-prolog tag, and you prevent her/him from seeing and answering the question?Amando
@jschimpf: Please use MSO for your observations or discussions.Crossbill
C
5

Your question concerns two different parts. First, the way how catch/3 can be used to handle such a situation. And then the timeout mechanism itself.

Catching errors and exceptions with catch/3

Generally speaking, the most idiomatic way to use catch/3 is like so:

   ...,
   catch((R = success, Goal), Pat, R = error(Pat)),
   ...

However, catching all errors/exceptions leads often to error-prone programs since a serious, unexpected error might be masked out.

In your particular case you want to catch a single pattern only, thus:

   ...,
   catch((R = success, call_with_time_limit(Time,Goal)),
          time_limit_exceeded,
          R = timeout ),
   ...

Please note that testing for uninstantiated variables using var(Pat) might be an easy-to-miss source of errors.

Handling timeouts

There are several interfaces offered in various systems. But the most fundamental question is what you actually want to achieve. Do you want to limit realtime, CPU time, or just resources?

time_out/3 in library(timeout) is probably the most advanced which was originally developed for SICStus Prolog about 1992. There are somewhat compatible implementations in SWI and YAP. However, SWI and YAP cannot handle nested cases. And SWI does not limit CPU-time. The interface is:

time_out(:Goal_0, +TimeMs, -Result)

call_with_time_limit/3 is a rather idiosyncratic built-in of SWI which does not conform to the common conventions for built-ins. Also, it calls its goal as once(Goal_0) only. I'd rather not.

call_with_inference_limit/3 this is currently present only in recent versions of SWI and uses similar conventions as time_out/3. It limits the number of inferences rather than CPU-time. It is thus well suited to detect programmer's loops but might not be suited for your task.

wait_for_input/3 might be an option for you should your timeouts be only related to reading data.

Crossbill answered 28/5, 2014 at 9:53 Comment(5)
First, thanks for the great informative answer. I want to limit realtime. When I use that pattern with a timeout of 0-3 seconds, it works well, but on 4 seconds or more it won't stop. I edited my post and showed the pattern I use. Any suggestions?Uncrowned
@Mockingbird: What kind of goal do you want to execute in between?Crossbill
It's a predicate that runs a satsolver which is written in C.Uncrowned
@Mockingbird: So you have a lot of options: Timeouts on the process level, etc. maybe wait_for_input/3. But definitely not time_out/3 nor call_with_time_limit/3, nor call_with_inference_limit/3.Crossbill
Pitty there is no widespread time_out/3, could be useful for combiner algorithms.Grocery
W
3

Try the pattern:

(   catch(call_with_time_limit(Time,Goal), Error, true) ->
    (   var(Error) ->
        % success actions
    ;   % error actions
    )
;   % failure actions
).
Witten answered 28/5, 2014 at 0:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.