root-scheduler.scm 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber,
  3. ; Suresh Jagannathan, Henry Ceijtin
  4. ; The root scheduler.
  5. ;
  6. ; This uses RUN-THREADS-WITH-HOUSEKEEPER from the round-robin scheduler.
  7. ; The housekeeping thread flushes output buffers and wakes any sleeping
  8. ; threads whose time has come.
  9. (define (root-scheduler thunk quantum housekeeping-quantum)
  10. (let ((*result* 111))
  11. (call-with-current-continuation
  12. (lambda (abort)
  13. (initialize-channel-i/o!)
  14. (run-threads-with-housekeeper (make-root-event-handler
  15. (lambda () (set! *result* (thunk)))
  16. quantum
  17. abort)
  18. (lambda ()
  19. (zap-i/o-orphans!)
  20. (zap-external-event-orphans!)
  21. (spawn-output-forcers #t)
  22. (wake-some-threads))
  23. housekeeping-quantum)
  24. *result*))))
  25. ; Returns a handler and a procedure for adding new threads. No events
  26. ; are handled specially. The only upcall is for aborting execution.
  27. (define (make-root-event-handler thunk quantum abort)
  28. (let ((runnable (make-queue))
  29. (thread-count (make-counter))
  30. (safe-dynamic-env (with-handler root-handler get-dynamic-env))
  31. (thread (make-thread thunk
  32. 'scheduler-initial-thread)))
  33. (set-thread-scheduler! thread (current-thread))
  34. (set-thread-dynamic-env! thread (get-dynamic-env))
  35. (increment-counter! thread-count)
  36. (enqueue! runnable thread)
  37. (round-robin-event-handler
  38. runnable quantum safe-dynamic-env thread-count
  39. (lambda args #f) ; we handle no events
  40. (lambda (thread token args) ; upcall handler
  41. (if (eq? token abort-token)
  42. (abort (car args))
  43. (propogate-upcall thread token args)))
  44. root-wait)))
  45. ; Let the user know if anything goes wrong while running a root thread.
  46. ; Errors kill the offending thread, warnings allow it to proceed.
  47. (define (root-handler condition next-handler)
  48. (let ((out (current-error-port)))
  49. (cond ((serious-condition? condition)
  50. (display "Serious problem while running root thread, thread killed: " out)
  51. (display (current-thread) out)
  52. (newline out)
  53. (cheap-display-condition condition out)
  54. (terminate-current-thread))
  55. ((warning? condition)
  56. (cheap-display-condition condition out)
  57. (unspecific)) ;proceed
  58. (else
  59. (next-handler)))))
  60. (define (cheap-display-condition condition out)
  61. (call-with-values
  62. (lambda () (decode-condition condition))
  63. (lambda (type who message stuff)
  64. (display (case type
  65. ((error) "Error")
  66. ((assertion-violation) "Assertion violation")
  67. ((serious) "Serious problem")
  68. ((vm-exception) "VM Exception")
  69. ((warning) "Warning")
  70. (else type))
  71. out)
  72. (display ": " out)
  73. (display " [" out)
  74. (display who out)
  75. (display "]" out)
  76. (display message out)
  77. (newline out)
  78. (for-each (lambda (irritant)
  79. (display " " out)
  80. (display irritant out)
  81. (newline out))
  82. stuff))))
  83. ; Upcall token
  84. (define abort-token (list 'abort-token))
  85. (define scheme-exit-now
  86. (lambda (status)
  87. (upcall abort-token status)))
  88. ; Getting around to calling the VM's WAIT procedure. We disable interrupts
  89. ; to keep things from happening behind our back, and then see if there is
  90. ; any thread to run or any event pending, or if work may appear in the future.
  91. (define (root-wait)
  92. (set-enabled-interrupts! 0)
  93. (let ((forcers? (spawn-output-forcers #f)))
  94. (call-with-values
  95. wake-some-threads
  96. (lambda (woke-some? time-until-wakeup)
  97. (cond ((or forcers? woke-some? (event-pending?))
  98. (set-enabled-interrupts! all-interrupts)
  99. #t)
  100. ((or time-until-wakeup
  101. (> threads-not-deadlocked-count 0))
  102. (do-some-waiting time-until-wakeup)
  103. (set-enabled-interrupts! all-interrupts)
  104. (root-wait))
  105. ((session-data-ref deadlock-handler)
  106. => (lambda (handler)
  107. (handler)
  108. (set-enabled-interrupts! all-interrupts)
  109. #t))
  110. (else
  111. (set-enabled-interrupts! all-interrupts)
  112. #f))))))
  113. ; A mess because a fixnum's worth of milliseconds is only a few days.
  114. ; The VM's WAIT procedure takes its maximum-wait argument in either
  115. ; milliseconds or minutes.
  116. (define (do-some-waiting time-until-wakeup)
  117. (call-with-values
  118. (lambda ()
  119. (cond ((not time-until-wakeup)
  120. (values -1 #f))
  121. ((< time-until-wakeup one-day-of-milliseconds)
  122. (values time-until-wakeup #f))
  123. (else
  124. (values (min (quotient time-until-wakeup
  125. one-minute-of-milliseconds)
  126. one-year-of-minutes) ; stick with fixnums
  127. #t))))
  128. wait))
  129. (define one-minute-of-milliseconds (* 1000 60))
  130. (define one-day-of-milliseconds (* one-minute-of-milliseconds
  131. 60 ; minutes in an hour
  132. 24)) ; hours in a day
  133. (define one-year-of-minutes (* 60 24 365))
  134. (define deadlock-handler (make-session-data-slot! #f))
  135. (define (call-when-deadlocked! thunk)
  136. (session-data-set! deadlock-handler thunk))
  137. ; Find any ports that need to be flushed. We get both a thunk to flush the
  138. ; port and the port itself; the port is only used for reporting problems.
  139. (define (spawn-output-forcers others-waiting?)
  140. (let ((thunks (output-port-forcers others-waiting?)))
  141. (cond ((null? thunks)
  142. #f)
  143. (else
  144. (for-each spawn-on-root thunks)
  145. #t))))