File: with-timeout-raw.lsp

package info (click to toggle)
acl2 8.6%2Bdfsg-3
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 1,138,276 kB
  • sloc: lisp: 17,818,294; java: 125,359; python: 28,122; javascript: 23,458; cpp: 18,851; ansic: 11,569; perl: 7,678; xml: 5,591; sh: 3,978; makefile: 3,840; ruby: 2,633; yacc: 1,126; ml: 763; awk: 295; csh: 233; lex: 197; php: 178; tcl: 49; asm: 23; haskell: 17
file content (263 lines) | stat: -rw-r--r-- 9,813 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
;Author: Harsh Raju Chamarthi, Matt Kaufmann
;Acknowledgements: Thanks to Gary Byers, Gary Warren King, Bob Boyer,
;David Rager
;

(in-package "ACL2")


;; ;Taken from memoize-raw.lisp 
;; #+Clozure
;; (defun make-watchdog (duration)
;; ;   Thanks to Gary Byers for this!

;;    (let* ((done (ccl:make-semaphore))
;;           (current ccl:*current-process*))
;;       (ccl::process-run-function "watchdog"
;;         (lambda ()
;;           (or (ccl:timed-wait-on-semaphore done duration)
;;               (ccl:process-interrupt
;;                current #'timeout-hard-error 'with-timeout
;;                                  '"Time exceeded"
;;                                  'nil *the-live-state*))))
;;       done)) 

;; (defmacro with-timeout-raw (duration body) ;duration in seconds
;; #+Clozure
;; `(let* ((semaphore (make-watchdog ,duration)))
;;    (unwind-protect
;;        ,body
;;      (ccl:signal-semaphore semaphore)))

;; #+sb-thread ;Thanks to Gary Warren King for this!
;;   `(handler-case 
;;     (sb-ext:with-timeout ,duration ,body)
;;     (sb-ext::timeout (c)
;;                      (declare (ignore c))
;;                      (timeout-hard-error 'with-timeout
;;                              '"Time exceeded"
;;                              'nil *the-live-state*)))

;;)

;harshrc: Thanks to Matt for the following email snippet whose ideas
;I used to implement nested timeouts.

;; I think you know how to write a function (timer n) that causes an
;; error after n seconds.  Presumably you could write it so that instead
;; of (er hard ...), it does (throw 'timeout-tag *timeout-val*).

;i need `throw' to be be a function to be used in process-interrupt,
;so had to define the following. Does that screw the semantics of
;usage of macro throw?
(defun throw1 (tag form)
  (throw tag form))

#+Clozure
(defun make-watchdog (duration id)
;   Thanks to Gary Byers for this! --adapted from memoize-raw.lisp

   (let* ((done (ccl:make-semaphore))
          (current ccl:*current-process*))
      (ccl::process-run-function "watchdog"
        (lambda ()
          (or (ccl:timed-wait-on-semaphore done duration)
              (ccl:process-interrupt
               current #'throw1 id id))))
      done))

(defmacro with-timeout-raw (duration id body) ;duration

; Note: all args are expressions that are expected to be evaluated.

  ;; duration in seconds with
  ;; id which is a unique timeout identifier
  #+Clozure
  `(let ((semaphore (make-watchdog ,duration ',id))
;close the environment
         ;(closure #'(lambda () ,body))
         )
     (unwind-protect
         ;(funcall closure)
         ,body
       (ccl:signal-semaphore semaphore)))

  #+sb-thread ;Thanks to Gary Warren King for this!
  `(handler-case 
    (sb-ext:with-timeout ,duration ,body)
    (sb-ext::timeout (c)
                     (declare (ignore c))
                     (throw1 ',id ',id)))

  #-(or Clozure sb-thread)
  '(error "Unsupported host Lisp for CGEN (only CCL and multi-threaded SBCL ~
           are supported)."))

; For debugging
#||
(defmacro catch1 (tag arg)
  `(progn (format t "Set up catcher: ~s~%" '(,tag ,arg))
          (let ((vals (catch ,tag (multiple-value-list ,arg))))
            (format t "Catcher worked:~%~s~%" vals)
            vals)))
||#

(defmacro catch1 (tag arg)
  `(catch ,tag (multiple-value-list ,arg)))

#|

  Pete note on with-timeout-aux-raw.

  I added the (handler-case ... (error (c) ',timeout-id)) code.

  This change allows us to hide any errors that may be generated
  during the counterexample generation process. The idea of
  counterexample generation is to look for errors in the form of
  counterexamples and to report such errors to users of the theorem
  prover, but to otherwise introduce minimal disruptions in the
  theorem proving process.

  Why might counterexample generation disrupt the theorem proving
  process? Well, consider evaluating a function that is exponential
  time, say fib, on large numbers. We do not want this process to use
  significant resources, such as time and space. The use of timeouts
  helps to ameliorate this issue. But, it will not help if we exhaust
  memory or if common lisp throws an error. Here is an example from
  ACL2 built using SBCL. First, some trivial theorems.

  (defthm foo (implies (natp  x) (natp (expt 2 x))))
  (in-theory (disable natp))
  (defthm bar (implies (natp  x) (natp (expt 2 (expt 2 x)))))

  Now, since bar is a theorem, what we might expect is that evaluating
  the body of bar, after replacing the variables by any constant will
  evaluate to t. Let's try it.

  (let ((x 1000000)) (implies (natp  x) (natp (expt 2 (expt 2 x)))))

  That leads to an error. Here is what SBCL reports.

  ************ ABORTING from raw Lisp ***********
  ********** (see :DOC raw-lisp-error) **********
  Error:  can't represent result of left shift
  ***********************************************

  The issue is that SBCL, in the process of evaluating expt evaluates
  bignum-ashift-left, which checks that we are not shifting by a very
  large number, since that will lead to a number not representable
  with existing memory. That check leads to the above error. Note that
  the following thm leads to a similar error:

  (thm (natp (expt 2 (expt 2 1000000))))

  As does the following.

  (test? (natp (expt 2 (expt 2 1000000))))

  This is the kind of thing that can happen when we are generating
  counterexamples. 

  Note that we can prove the above and bypass evaluation, eg, with this:

  (thm (natp (expt 2 (expt 2 1000000)))
    :hints (("Goal" :use (:instance bar (x 1000000)))))

  Anyway, the current implementation will catch such errors, but it is
  still possible for fatal, non-recoverable errors to occur. An
  example is discussed below.

  Potential improvements include the following.

  1. Add support for tracking other resources such as space. To this
  end, the oracle-timelimit book in [books]/tools/ might be
  useful. The motivation for this is that some code can very quickly
  exhaust available memory, before timeouts kick in. I've seen cases
  where this just leads SBCL errors such as:

  fatal error encountered in SBCL pid 36112 pthread 0xaaf5600:
  Heap exhausted, game over.

  This error causes ACL2 to die, so it is not great. If you see this,
  limit the number of counterexamples, time for counterexamples,
  etc. using (acl2s-defaults :set num-witnesses/ cgen-local-timeout/
  cgen-timeout ...) forms. A better approach may be to use definec and
  defdata to define the right types. If that is not a viable option,
  one can use different enumerators, which can be attached
  dynamically. 

  2. Collect information and statistics on the types of errors that
  occur. Then analyze it and provide useful info to users.


|#

(defmacro with-timeout-aux-raw (&whole whole duration/timeout-form body)
  (case-match duration/timeout-form
    (('quote (duration timeout-form))
     (let ((timeout-id (acl2-gentemp "WITH-TIMEOUT$")))
       `(let ((vals (handler-case 
                        (catch1 ',timeout-id
                                (with-timeout-raw ,duration
                                                  ,timeout-id
                                                  ,body))
                      (error (c) ',timeout-id))))
          (cond ((eq vals ',timeout-id)
                 ,timeout-form)
                (t (values-list vals))))))
    (& (error "Illegal call in with-timeout-aux-raw:~%~s~%" whole))))

#|

 I left this here as an example of how to modify with-timeout-aux-raw
 so that debugging breaks are disabled. In one iteration of trying to
 get cgen to ignore break$, due to a request by Eric Smith, I explored
 doing that, in part, here. Currently, this is done at a higher level
 in test-checkpoint and test?-fn. That's better since it gets done
 less frequently but covers more of the cgen code. A version of this
 function may be independently useful so I left this code here.

(defmacro with-timeout-aux-raw (&whole whole duration/timeout-form body)
  (case-match duration/timeout-form
    (('quote (duration timeout-form))
     (let ((timeout-id (acl2-gentemp "WITH-TIMEOUT$"))
           (debug-enable (acl2::f-get-global 'acl2::debugger-enable
                                             acl2::*the-live-state*)))
       `(let ((vals
               (handler-case
                   (progn
                     (acl2::f-put-global 'acl2::debugger-enable
                                         :never
                                         acl2::*the-live-state*)
                     (catch1 ',timeout-id
                             (with-timeout-raw ,duration
                                               ,timeout-id
                                               ,body)))
                 (error (c) ',timeout-id))))
          (progn
            (acl2::f-put-global 'acl2::debugger-enable
                                ,debug-enable
                                acl2::*the-live-state*)
            (cond ((eq vals ',timeout-id)
                   ,timeout-form)
                  (t (values-list vals)))))))
    (& (error "Illegal call in with-timeout-aux-raw:~%~s~%" whole))))
|#

#|

Previous version.

(defmacro with-timeout-aux-raw (&whole whole duration/timeout-form body)
  (case-match duration/timeout-form
    (('quote (duration timeout-form))
     (let ((timeout-id (acl2-gentemp "WITH-TIMEOUT$")))
       `(let ((vals (catch1 ',timeout-id
                            (with-timeout-raw ,duration
                                              ,timeout-id
                                              ,body))))
          (cond ((eq vals ',timeout-id)
                 ,timeout-form)
                (t (values-list vals))))))
    (& (error "Illegal call in with-timeout-aux-raw:~%~s~%" whole))))
|#