interrupt.scm 13 KB

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