interrupt.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Code for handling interrupts.
  3. ; New interrupt handler vector in *val*
  4. (define-opcode set-interrupt-handlers!
  5. (cond ((or (not (vm-vector? *val*))
  6. (< (vm-vector-length *val*) interrupt-count))
  7. (raise-exception wrong-type-argument 0 *val*))
  8. (else
  9. (let ((temp (shared-ref *interrupt-handlers*)))
  10. (shared-set! *interrupt-handlers* *val*)
  11. (set! *val* temp)
  12. (goto continue 0)))))
  13. ; New interrupt mask as fixnum in *val*
  14. (define-opcode set-enabled-interrupts!
  15. (let ((old *enabled-interrupts*))
  16. (set-enabled-interrupts! (extract-fixnum *val*))
  17. (set! *val* (enter-fixnum old))
  18. (goto continue 0)))
  19. ; Save the current interpreter state and call an interrupt handler.
  20. (define (handle-interrupt)
  21. (push *val*)
  22. (receive (code pc)
  23. (current-code+pc)
  24. (push code)
  25. (push (enter-fixnum pc)))
  26. (push-interrupt-state)
  27. (push-adlib-continuation! (code+pc->code-pointer *interrupted-byte-opcode-return-code*
  28. return-code-pc))
  29. (goto find-and-call-interrupt-handler))
  30. (define (handle-native-interrupt protocol-skip)
  31. (push (enter-fixnum protocol-skip))
  32. (push *val*)
  33. (push-interrupt-state)
  34. (push-adlib-continuation! (code+pc->code-pointer *interrupted-native-call-return-code*
  35. return-code-pc))
  36. (goto find-and-call-interrupt-handler))
  37. (define (handle-native-poll template return-address)
  38. (push *val*)
  39. (push template)
  40. (push return-address)
  41. (push-interrupt-state)
  42. (push-adlib-continuation! (code+pc->code-pointer *native-poll-return-code*
  43. return-code-pc))
  44. (goto find-and-call-interrupt-handler))
  45. ; MG: This is an old comment, I don't want to remove it because I
  46. ; don't understand it:
  47. ; We now have three places interrupts are caught:
  48. ; - during a byte-code call
  49. ; - during a native-code call
  50. ; - during a native-code poll
  51. ; The two native calls can be done using the same method. Argh. We need
  52. ; to save the proposal and enabled interrupts and still end up with a template
  53. ; on top of the stack. Just make a return pointer with two extra pointer
  54. ; slots at the top. The native-dispatch code pops the code pointer and
  55. ; template, pushes the extra state, and then ... . No. The simple thing
  56. ; to do is have the native code make one continuation and then we push a
  57. ; second, byte-coded one on top. It's ugly no matter what.
  58. ; Ditto, except that we are going to return to the current continuation instead
  59. ; of continuating with the current template.
  60. (define interrupt-state-descriptors 2)
  61. (define (push-interrupt-state)
  62. (push (current-proposal))
  63. (set-current-proposal! false)
  64. (push (enter-fixnum *enabled-interrupts*)))
  65. (define (s48-pop-interrupt-state)
  66. (set-enabled-interrupts! (extract-fixnum (pop)))
  67. (set-current-proposal! (pop)))
  68. (define (find-and-call-interrupt-handler)
  69. (let* ((pending-interrupt (get-highest-priority-interrupt!))
  70. (handlers (shared-ref *interrupt-handlers*))
  71. (arg-count (push-interrupt-args pending-interrupt)))
  72. (if (not (vm-vector? handlers))
  73. (error "interrupt handler is not a vector"))
  74. (set! *val* (vm-vector-ref handlers pending-interrupt))
  75. (if (not (closure? *val*))
  76. (error "interrupt handler is not a closure" pending-interrupt))
  77. (set-enabled-interrupts! 0)
  78. (goto call-interrupt-handler arg-count pending-interrupt)))
  79. ; Push the correct arguments for each type of interrupt.
  80. ;
  81. ; For alarm interrupts the interrupted template is passed to the handler
  82. ; for use by code profilers.
  83. ; For gc interrupts we push the list of things to be finalized,
  84. ; the interrupt mask, and whether the GC is running out of space.
  85. ; For i/o-completion we push the channel and its status.
  86. ; For i/o-error we push the channel and the error code.
  87. ; For external-event, we push the event-type uid.
  88. (define (push-interrupt-args pending-interrupt)
  89. (cond ((eq? pending-interrupt (enum interrupt alarm))
  90. (push *interrupted-template*)
  91. (set! *interrupted-template* false)
  92. (push (enter-fixnum *enabled-interrupts*))
  93. 2)
  94. ((or (eq? pending-interrupt (enum interrupt post-major-gc))
  95. (eq? pending-interrupt (enum interrupt post-minor-gc)))
  96. (push *finalize-these*)
  97. (set! *finalize-these* null)
  98. (push (enter-fixnum *enabled-interrupts*))
  99. (push (enter-boolean *gc-in-trouble?*))
  100. 3)
  101. ((eq? pending-interrupt (enum interrupt i/o-completion))
  102. ;; we don't know which one it is for each individual channel
  103. (let ((channel (dequeue-channel!)))
  104. (if (not (channel-queue-empty?))
  105. (note-interrupt! (enum interrupt i/o-completion)))
  106. (push channel)
  107. (push (channel-error? channel))
  108. (push (channel-os-status channel))
  109. (push (enter-fixnum *enabled-interrupts*))
  110. 4))
  111. ((eq? pending-interrupt (enum interrupt os-signal))
  112. (push (enter-fixnum (os-signal-ring-remove!)))
  113. (if (os-signal-ring-ready?)
  114. (note-interrupt! (enum interrupt os-signal)))
  115. (push (enter-fixnum *enabled-interrupts*))
  116. 2)
  117. ((eq? pending-interrupt (enum interrupt external-event))
  118. (receive (uid still-ready?)
  119. (dequeue-external-event!)
  120. (push (enter-fixnum uid))
  121. (if still-ready?
  122. (note-interrupt! (enum interrupt external-event)))
  123. (push (enter-fixnum *enabled-interrupts*))
  124. 2))
  125. (else
  126. (push (enter-fixnum *enabled-interrupts*))
  127. 1)))
  128. ;;; Dealing with OS signals
  129. (define *os-signal-ring-length* 32)
  130. (define *os-signal-ring*
  131. (let ((v (make-vector *os-signal-ring-length* 0)))
  132. (if (null-pointer? v)
  133. (error "out of memory, unable to continue"))
  134. v))
  135. (define *os-signal-ring-start* 0) ; index of oldest signal
  136. (define *os-signal-ring-ready* 0) ; index of last signal for which an
  137. ; os-event has already been generated
  138. (define *os-signal-ring-end* 0) ; index of newest signal
  139. ;; ring-like incrementation
  140. (define-syntax os-signal-ring-inc!
  141. (syntax-rules ()
  142. ((os-signal-ring-inc! var)
  143. (set! var
  144. (if (= var
  145. (- *os-signal-ring-length* 1))
  146. 0
  147. (+ var 1))))))
  148. (define (os-signal-ring-ready?)
  149. (not (= *os-signal-ring-ready*
  150. *os-signal-ring-start*)))
  151. (define (os-signal-ring-add! sig)
  152. (os-signal-ring-inc! *os-signal-ring-end*)
  153. (if (= *os-signal-ring-start*
  154. *os-signal-ring-end*)
  155. (error "OS signal ring too small, report to Scheme 48 maintainers"))
  156. (vector-set! *os-signal-ring* *os-signal-ring-end* sig))
  157. (define (os-signal-ring-empty?)
  158. (= *os-signal-ring-start*
  159. *os-signal-ring-end*))
  160. (define (os-signal-ring-remove!)
  161. (if (os-signal-ring-empty?)
  162. (error "This cannot happen: OS signal ring empty"))
  163. (let ((sig (vector-ref *os-signal-ring* *os-signal-ring-start*)))
  164. (set! *os-signal-ring-start*
  165. (if (= *os-signal-ring-start*
  166. (- *os-signal-ring-length* 1))
  167. 0
  168. (+ *os-signal-ring-start* 1)))
  169. sig))
  170. ; Called from outside when an os-signal event is returned.
  171. (define (s48-add-os-signal sig)
  172. (os-signal-ring-add! sig))
  173. ; Called from outside to check whether an os-event has to be signalled
  174. (define (s48-os-signal-pending)
  175. (if (= *os-signal-ring-ready*
  176. *os-signal-ring-end*)
  177. #f
  178. (begin
  179. (os-signal-ring-inc! *os-signal-ring-ready*)
  180. #t)))
  181. ; Called from outside to initialize a new process.
  182. (define (s48-reset-interrupts!)
  183. (set! *os-signal-ring-start* 0)
  184. (set! *os-signal-ring-ready* 0)
  185. (set! *os-signal-ring-end* 0)
  186. (set! *enabled-interrupts* 0)
  187. (pending-interrupts-clear!)
  188. (set! s48-*pending-interrupt?* #f))
  189. (define-opcode poll
  190. (if (and (interrupt-flag-set?)
  191. (pending-interrupt?))
  192. (goto handle-interrupt)
  193. (goto continue 0)))
  194. (define-opcode resume-interrupted-opcode-to-byte-code
  195. (pop)
  196. (s48-pop-interrupt-state)
  197. (let ((pc (pop)))
  198. (set-code-pointer! (pop) (extract-fixnum pc)))
  199. (set! *val* (pop))
  200. (goto interpret *code-pointer*))
  201. (define-opcode resume-interrupted-call-to-native-code
  202. (pop)
  203. (s48-pop-interrupt-state)
  204. (set! *val* (pop))
  205. (let ((protocol-skip (extract-fixnum (pop))))
  206. (goto really-call-native-code protocol-skip)))
  207. (define-opcode resume-native-poll
  208. (pop) ; frame size
  209. (s48-pop-interrupt-state)
  210. (let* ((return-address (pop))
  211. (template (pop)))
  212. (set! *val* (pop))
  213. (goto post-native-dispatch (s48-jump-native return-address template))))
  214. ; Do nothing much until something happens. To avoid race conditions this
  215. ; opcode is called with all interrupts disabled, so it has to return if
  216. ; any interrupt occurs, even a disabled one.
  217. (define-primitive wait (fixnum-> boolean->)
  218. (lambda (max-wait minutes?)
  219. (if (and (not (pending-interrupt?))
  220. (pending-interrupts-empty?))
  221. (wait-for-event max-wait minutes?))
  222. (goto return-unspecific 0)))
  223. ; The players:
  224. ; pending-interrupts-X A bit mask of pending interrupts
  225. ; *enabled-interrupts* A bit mask of enabled interrupts
  226. ; s48-*pending-interrupt?* True if either an event or interrupt is pending
  227. ; s48-*pending-events?* True if an event is pending
  228. ;
  229. ; When an asynchronous event occurs the OS sets S48-*PENDING-EVENTS?* and
  230. ; S48-*PENDING-INTERRUPT?* to true.
  231. ;
  232. ; When S48-*PENDING-EVENTS?* is true the VM calls (CURRENT-EVENTS) to get the
  233. ; pending events.
  234. ;
  235. ; The goals of all this mucking about are:
  236. ; - no race conditions
  237. ; - the VM operates synchronously; only the OS is asynchronous
  238. ; - polling only requires testing S48-*PENDING-INTERRUPT?*
  239. (define s48-*pending-events?* #f)
  240. ; Called asynchronously by the OS
  241. (define (s48-note-event)
  242. (set! s48-*pending-events?* #t) ; order required by non-atomicity
  243. (set-interrupt-flag!))
  244. ; Called when the interrupt flag is set, so either an event or interrupt is
  245. ; waiting (or both). We process any events and then see if is an interrupt.
  246. (define (pending-interrupt?)
  247. (if s48-*pending-events?*
  248. (begin
  249. (set! s48-*pending-events?* #f)
  250. (process-events)))
  251. (real-pending-interrupt?))
  252. ; Check for a pending interrupt, clearing the interrupt flag if there is
  253. ; none. This and S48-NOTE-EVENT cooperate to avoid clearing the interrupt
  254. ; flag while an event is pending.
  255. (define (real-pending-interrupt?)
  256. (cond ((= 0 (bitwise-and (pending-interrupts-mask)
  257. *enabled-interrupts*))
  258. (clear-interrupt-flag!)
  259. (if s48-*pending-events?*
  260. (set-interrupt-flag!))
  261. #f)
  262. (else
  263. #t)))
  264. (define (update-pending-interrupts)
  265. (if (real-pending-interrupt?)
  266. (set-interrupt-flag!)))
  267. ; Add INTERRUPT to the set of pending interrupts, then check to see if it
  268. ; is currently pending.
  269. (define (note-interrupt! interrupt)
  270. (pending-interrupts-add! (interrupt-bit interrupt))
  271. (update-pending-interrupts))
  272. ; Remove INTERRUPT from the set of pending interrupts, then recheck for pending
  273. ; interrupts; INTERRUPT may have been the only one.
  274. (define (clear-interrupt! interrupt)
  275. (pending-interrupts-remove! (interrupt-bit interrupt))
  276. (update-pending-interrupts))
  277. ; Install a new set of enabled interrupts. As usual we have to recheck for
  278. ; enabled interrupts.
  279. (define (set-enabled-interrupts! enabled)
  280. (set! *enabled-interrupts* enabled)
  281. (update-pending-interrupts))
  282. ; Disable all interrupts.
  283. (define (disable-interrupts!)
  284. (set! s48-*pending-interrupt?* #f)
  285. (set! *enabled-interrupts* 0))
  286. ; Enable all interrupts.
  287. (define (enable-interrupts!)
  288. (set-enabled-interrupts! -1))
  289. ; We don't need to mess with S48-*PENDING-INTERRUPT?* because all interrupts
  290. ; are about to be disabled.
  291. (define (get-highest-priority-interrupt!)
  292. (let ((n (bitwise-and (pending-interrupts-mask) *enabled-interrupts*)))
  293. (let loop ((i 0) (m 1))
  294. (cond ((= 0 (bitwise-and n m))
  295. (loop (+ i 1) (* m 2)))
  296. (else
  297. (pending-interrupts-remove! m)
  298. i)))))
  299. ; Process any pending OS events. PROCESS-EVENT returns a mask of any interrupts
  300. ; that have just occured.
  301. (define (process-events)
  302. (let loop ()
  303. (receive (type channel status)
  304. (get-next-event)
  305. (pending-interrupts-add! (process-event type channel status))
  306. (if (not (eq? type (enum events no-event)))
  307. (loop)))))
  308. ; Do whatever processing the event requires.
  309. (define (process-event event id status)
  310. (cond ((eq? event (enum events alarm-event))
  311. ;; Save the interrupted template for use by profilers.
  312. ;; Except that we have no more templates and no more profiler.
  313. ;(if (false? *interrupted-template*)
  314. ; (set! *interrupted-template* *template*))
  315. (interrupt-bit (enum interrupt alarm)))
  316. ((eq? event (enum events keyboard-interrupt-event))
  317. (interrupt-bit (enum interrupt keyboard)))
  318. ((eq? event (enum events io-completion-event))
  319. (enqueue-channel! id status false)
  320. (interrupt-bit (enum interrupt i/o-completion)))
  321. ((eq? event (enum events io-error-event))
  322. (enqueue-channel! id status true)
  323. (interrupt-bit (enum interrupt i/o-completion)))
  324. ((eq? event (enum events os-signal-event))
  325. (interrupt-bit (enum interrupt os-signal)))
  326. ((eq? event (enum events external-event))
  327. (interrupt-bit (enum interrupt external-event)))
  328. ((eq? event (enum events no-event))
  329. 0)
  330. ((eq? event (enum events error-event))
  331. (error-message "OS error while getting event")
  332. (error-message (error-string status))
  333. 0)
  334. (else
  335. (error-message "unknown type of event")
  336. 0)))
  337. ; Return a bitmask for INTERRUPT.
  338. (define (interrupt-bit interrupt)
  339. (shift-left 1 interrupt))