thread.scm 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. ; Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Threads.
  3. ; This was inspired by Haynes et al's engines.
  4. ;
  5. ; The fundamental operation is (RUN <thread> <time>), which runs the thread
  6. ; for the given amount of time.
  7. ;
  8. ; Each thread has:
  9. ; dynamic environment
  10. ; dynamic point
  11. ; current proposal
  12. ; saved continuation (if not currently runnning)
  13. ; state
  14. ; scheduler, which is the thread that RUNs this one
  15. ; remaining time in clock ticks ('waiting = waiting for events)
  16. ; queue that is holding this thread, if any
  17. ; arguments waiting to be passed to the thread when it is next run
  18. ; whatever data the scheduler wants
  19. ; Schedulers also have:
  20. ; list of pending events
  21. ; thread that this scheduler is currently running
  22. ;
  23. ; A `scheduler' is any thread that has called RUN. All threads are organized
  24. ; into a tree by the THREAD-SCHEDULER field, with the pointers pointing from
  25. ; the leaves to the root.
  26. ;
  27. ; There is a doubly linked list of running threads linked by the
  28. ; the thread-scheduler and thread-current-task fields.
  29. ; e1 <-> e2 <-> ... <-> eN-1 <-> eN
  30. ; e1 is the top thread and eN is the thread whose code is actually running.
  31. ; All except eN are in the middle of a call to RUN. The scheduler pointers
  32. ; point to the left and the current-task pointers point to the right.
  33. ;
  34. ; When an thread stops, its scheduler is run and the portion of the list from
  35. ; the stopped thread down is saved for when the stopped thread is resumed. For
  36. ; example, suppose e3 runs out of time. Then the list is shortened to
  37. ; e1 <-> e2 and the e3 <-...-> eN portion is saved. When e3 is resumed,
  38. ; the list is spliced back together and eN's continuation is resumed.
  39. (define-record-type thread :thread
  40. (really-make-thread dynamic-env dynamic-point proposal
  41. continuation scheduler
  42. cell arguments
  43. events current-task uid name)
  44. thread?
  45. ; These first three fields hold dynamic data used by various VM opcodes.
  46. (dynamic-env thread-dynamic-env set-thread-dynamic-env!)
  47. ;Must be first! (See fluid.scm)
  48. (dynamic-point thread-dynamic-point set-thread-dynamic-point!)
  49. ;Must be second! (See fluid.scm)
  50. (proposal thread-proposal) ;Must be third! (See fluid.scm)
  51. ; The time remaining for a thread to run.
  52. (time thread-time set-thread-time!)
  53. ; The saved state of a non-running thread
  54. (continuation thread-continuation set-thread-continuation!)
  55. (arguments thread-arguments set-thread-arguments!)
  56. ; Used by the engine algorithm
  57. (scheduler thread-scheduler set-thread-scheduler!)
  58. (current-task thread-current-task set-thread-current-task!)
  59. (events thread-events set-thread-events!)
  60. ; Used by schedulers
  61. (data thread-data set-thread-data!)
  62. (cell thread-cell set-thread-cell!)
  63. ; For debugging
  64. (uid thread-uid) ; (also used as a cheap weak pointer)
  65. (name thread-name))
  66. (define-record-discloser :thread
  67. (lambda (thread)
  68. (cons 'thread
  69. (cons (thread-uid thread)
  70. (let ((name (thread-name thread)))
  71. (if name
  72. (list name)
  73. '()))))))
  74. (define *thread-uid* 0)
  75. (define (make-thread thunk name)
  76. (let ((thread (really-make-thread #f ; dynamic-env
  77. #f ; dynamic-point root
  78. #f ; proposal
  79. (thunk->continuation
  80. (thread-top-level thunk))
  81. #f ; scheduler
  82. #f ; cell
  83. '() ; arguments
  84. #f ; events
  85. #f ; current-task
  86. *thread-uid*
  87. name)))
  88. (set! *thread-uid* (+ *thread-uid* 1))
  89. thread))
  90. ;----------------
  91. ; Call THUNK and then suspend. The LET is just to give the thunk a name
  92. ; in the debugger. This thunk shows up at the bottom of every ,preview.
  93. (define (thread-top-level thunk)
  94. (let ((thread-start (lambda ()
  95. (call-with-values
  96. thunk
  97. (lambda values
  98. (suspend (enum event-type completed) values))))))
  99. thread-start))
  100. ; Find the thread with the indicated uid. This is expensive. It is used
  101. ; by rts/channel-port.scm to when forcibly unlocking one of the REPL's ports.
  102. (define (thread-uid->thread uid)
  103. (let ((threads (all-threads)))
  104. (let loop ((i 0))
  105. (cond ((= i (vector-length threads))
  106. #f)
  107. ((= uid (thread-uid (vector-ref threads i)))
  108. (vector-ref threads i))
  109. (else
  110. (loop (+ i 1)))))))
  111. (define (all-threads)
  112. (find-all-records :thread))
  113. ; Add EVENT to THREAD's event queue.
  114. ; Called with interrupts disabled.
  115. (define (add-event! thread event)
  116. (enqueue! (or (thread-events thread)
  117. (let ((q (make-queue)))
  118. (set-thread-events! thread q)
  119. q))
  120. event))
  121. (define (next-event! thread)
  122. (let ((queue (thread-events thread)))
  123. (if (and queue
  124. (not (queue-empty? queue)))
  125. (dequeue! queue)
  126. #f)))
  127. ; A bit of magic courtesy of JAR. We need to use PRIMITIVE-CWCC to save
  128. ; thread continuations because CALL-WITH-CURRENT-CONTINUATION saves the
  129. ; dynamic state, including the current thread. PRIMITIVE-CWCC's continuations
  130. ; are not procedures, so we need this thing to convert a thread's initial
  131. ; thunk into a continuation.
  132. ; (Alternatively, we could make a version of CWCC that didn't save the
  133. ; dynamic state. That would slow down context switching, which is
  134. ; presumably more frequent than thread creation.)
  135. (define (thunk->continuation thunk)
  136. (compose-continuation thunk #f))
  137. ; Return a continuation that will call PROC with continuation CONT.
  138. ; Synopsis: we grab the current continuation, install the continuation
  139. ; we want to create, and then at the last minute save the new continuation
  140. ; and return it to the one we grabbed on entry.
  141. (define (compose-continuation proc cont)
  142. (primitive-cwcc ; grab the current continuation so that
  143. (lambda (k) ; we can return
  144. (with-continuation ; install CONT or an empty continuation
  145. (or cont (loophole :escape #f))
  146. (lambda ()
  147. (call-with-values ; install PROC as a continuation
  148. (lambda ()
  149. (primitive-cwcc ; grab a continuation that will call PROC and
  150. (lambda (k2) ; then return to the installed continuation
  151. (with-continuation ; return the PROC-calling continuation to
  152. k ; the continuation we grabbed on entry
  153. (lambda () k2)))))
  154. proc))))))
  155. ;----------------
  156. ; Removing threads from queues and cells.
  157. ; These are utility procedures for users. They are not used here.
  158. (define (maybe-dequeue-thread! queue)
  159. (let loop ()
  160. (let ((cell (maybe-dequeue! queue)))
  161. (if cell
  162. (or (provisional-cell-ref cell)
  163. (loop))
  164. #f))))
  165. ; Look for a non-empty cell.
  166. (define (thread-queue-empty? queue)
  167. (ensure-atomicity
  168. (let loop ()
  169. (cond ((queue-empty? queue)
  170. #t)
  171. ((provisional-cell-ref (queue-head queue))
  172. #f)
  173. (else
  174. (dequeue! queue)
  175. (loop))))))
  176. ;----------------
  177. ; Return values for RUN.
  178. (define-enumeration event-type
  179. ;; Reason Additional return values
  180. (
  181. ;; events relating to the running thread
  182. out-of-time
  183. completed ; . <results>
  184. blocked
  185. killed
  186. upcall ; <args> unknown upcalls are passed up
  187. ;; asynchronous events
  188. runnable ; <thread> <args> <thread> is now runnable
  189. spawned ; <thunk> <id> ... spawn <thunk> as a new thread
  190. interrupt ; <type> . <stuff> an interrupt has occured
  191. deadlock ; no one can run
  192. no-event ; there are no pending events
  193. ))
  194. ; DEADLOCK is used by the REPL to gain control when the thread system deadlocks.
  195. ; (RUN <thread> <time>) -> <time-left> <event-type> . <stuff>
  196. ;
  197. ; Run <thread> for no more than <time>. The call returns when the thread
  198. ; stops, returning the remaining time, the reason the thread stopped, and
  199. ; any addition information relating to the reason. Times are in milliseconds.
  200. ;
  201. ; What this does:
  202. ; 1. Check that THREAD is runnable, that it belongs to the current thread,
  203. ; and that it can accept any values being returned.
  204. ; 2. Return immediately if an event is pending.
  205. ; 3. Otherwise suspend the current thread, make THREAD its task, and then
  206. ; run THREAD (or the thread that it is running or ...)
  207. (define (run thread time)
  208. (disable-interrupts!)
  209. (let ((scheduler (current-thread)))
  210. (cond ((not (thread-continuation thread))
  211. (enable-interrupts!)
  212. (error "RUN called with a completed thread" thread))
  213. ((not (eq? (thread-scheduler thread) scheduler))
  214. (enable-interrupts!)
  215. (error "thread run by wrong scheduler" thread scheduler))
  216. ((thread-cell thread)
  217. (enable-interrupts!)
  218. (error "thread run while still blocked" thread))
  219. ((and (thread-current-task thread)
  220. (not (null? (thread-arguments thread))))
  221. (enable-interrupts!)
  222. (error "returning values to running thread"
  223. thread
  224. (thread-arguments thread)))
  225. ((event-pending?)
  226. (enable-interrupts!)
  227. (apply values time (dequeue! (thread-events (current-thread)))))
  228. (else
  229. (set-thread-current-task! scheduler thread)
  230. (find-and-run-next-thread thread time)))))
  231. ; The next thread to run is the scheduler of the highest thread in the chain
  232. ; with no time left or, if there is no such thread, the bottom thread in the
  233. ; chain. The time limit is the minimum of the remaining times of threads
  234. ; above the thread to be run.
  235. ;
  236. ; We first go down from the user-provided thread, looking for a thread with
  237. ; no time left or a thread that has an event queued. We then continue either
  238. ; with that thread's scheduler or with the bottom thread of the chain.
  239. ; This could be modified to add the current time to NEW-THREAD and any threads
  240. ; below. Then the old time limit could be reused if none of the new threads
  241. ; got less time than SCHEDULER and above. This is slower and simpler.
  242. (define (find-and-run-next-thread new-thread time)
  243. (set-thread-time! new-thread time) ; in case we don't run it now
  244. (let loop ((thread new-thread) (time time))
  245. (let ((next (thread-current-task thread)))
  246. (cond ((or (not next)
  247. (< 0 (thread-time next)))
  248. (debit-thread-times-and-run! thread time #f))
  249. ((next-event! thread)
  250. => (lambda (event)
  251. (set-thread-current-task! thread #f)
  252. (set-thread-arguments! thread (cons (thread-time next)
  253. event))
  254. (debit-thread-times-and-run! thread time #f)))
  255. (else
  256. (loop next (min time (thread-time next))))))))
  257. ; Debit the times of all threads from the current-thread on up. If we find a
  258. ; thread with no time left, then that thread's scheduler becomes the potential
  259. ; next thread.
  260. ;
  261. ; Only the root thread can end up with a time-limit of #f, as all other threads
  262. ; have schedulers.
  263. (define (debit-thread-times-and-run! next-to-run time-limit time-out?)
  264. (let ((elapsed (interrupt-timer-time)))
  265. (let loop ((thread (current-thread))
  266. (time-limit time-limit)
  267. (next-to-run next-to-run))
  268. (let ((scheduler (thread-scheduler thread)))
  269. (cond (scheduler
  270. (let ((time-left (max2 (- (thread-time thread)
  271. elapsed)
  272. 0)))
  273. (set-thread-time! thread time-left)
  274. (if (<= time-left 0)
  275. (loop scheduler #f scheduler)
  276. (loop scheduler
  277. (if time-limit
  278. (min time-limit time-left)
  279. time-left)
  280. next-to-run))))
  281. (next-to-run
  282. ; (debug-message (if time-out?
  283. ; "[quantum ran out]"
  284. ; "[run called]"))
  285. (run-next-thread next-to-run time-limit))
  286. (else
  287. (schedule-interrupt! time-limit)))))))
  288. ; Debit the times of all running threads and run whomever is next.
  289. (define (handle-timer-interrupt interrupted-template ei)
  290. (if (thread-scheduler (current-thread))
  291. (debit-thread-times-and-run! #f #f #t)))
  292. ; Run the next thread, first scheduling an interrupt if the thread is not the
  293. ; root thread. If the next thread has a current task, then the return values
  294. ; are the normal out-of-time values. The bottom thread of the chain gets
  295. ; whatever return values have been stashed earlier.
  296. (define (run-next-thread thread time-limit)
  297. (if time-limit (schedule-interrupt! time-limit))
  298. (let ((arguments (if (thread-current-task thread)
  299. (list 0 (enum event-type out-of-time))
  300. (thread-arguments thread))))
  301. (set-thread-arguments! thread '())
  302. (switch-to-thread thread arguments)))
  303. ; Fast binary version of MIN
  304. (define (min x y)
  305. (if (< x y) x y))
  306. ;----------------
  307. ; Save the current thread and start running NEW-THREAD.
  308. (define (switch-to-thread thread return-values)
  309. (primitive-cwcc
  310. (lambda (cont)
  311. (set-thread-continuation! (current-thread) cont)
  312. (run-thread thread return-values))))
  313. ; Start running THREAD. This assumes that THREAD has already been linked into
  314. ; the list of running threads.
  315. (define (run-thread thread return-values)
  316. ; (debug-message "[Running " (thread-name thread)
  317. ; " " (thread-uid thread) " on "
  318. ; (length return-values) " values]")
  319. (set-current-thread! thread)
  320. (set-thread-current-task! thread #f)
  321. (with-continuation (thread-continuation thread)
  322. (lambda ()
  323. (set-thread-continuation! thread #f) ; HCC: for GC
  324. (enable-interrupts!)
  325. (apply values return-values))))
  326. ;----------------
  327. ; (SUSPEND <reason> <stuff>) stops the current thread and returns from
  328. ; the call to RUN that invoked it. The arguments passed to SUSPEND become
  329. ; the return values of the call to RUN. SUSPEND itself returns the arguments
  330. ; passed to RUN when the stopped thread is restarted.
  331. (define (suspend reason stuff)
  332. (disable-interrupts!)
  333. (suspend-to (thread-scheduler (current-thread))
  334. (cons reason stuff)))
  335. ; Stop running the current thread and return from the RUN call in
  336. ; SCHEDULER with the given reason. We need to debit the time of every
  337. ; thread between the current one and SCHEDULER.
  338. (define (suspend-to scheduler event) ; called with interrupts disabled
  339. ; (debug-message "[suspending for "
  340. ; (enumerand->name (car event) event-type)
  341. ; "]")
  342. (debit-down! (thread-current-task scheduler))
  343. (switch-to-thread scheduler
  344. (cons (thread-time (thread-current-task scheduler))
  345. event)))
  346. (define (debit-down! thread)
  347. (let ((elapsed (interrupt-timer-time)))
  348. (let loop ((thread thread))
  349. (if thread
  350. (begin
  351. (set-thread-time! thread
  352. (max2 (- (thread-time thread)
  353. elapsed)
  354. 0))
  355. (loop (thread-current-task thread)))))))
  356. ; Same thing, except that we don't save the current continuation and
  357. ; we don't need to debit the thread's time. This is used for completed
  358. ; and killed threads and is not exported.
  359. (define (exit reason stuff)
  360. (disable-interrupts!)
  361. ; (debug-message "[thread exiting]")
  362. (let ((thread (current-thread)))
  363. (set-thread-continuation! thread #f)
  364. (run-thread (thread-scheduler thread)
  365. (cons (- (thread-time thread)
  366. (interrupt-timer-time))
  367. (cons reason stuff)))))
  368. ; Wait for something to happen. If an event is pending we return immediately.
  369. ; Another same thing, except that we have to be careful because we need to
  370. ; set the current thread's time field to a non-integer.
  371. (define (wait)
  372. (let ((interrupts (set-enabled-interrupts! no-interrupts)))
  373. (let ((thread (current-thread)))
  374. (if (not (and (thread-events thread)
  375. (not (queue-empty? (thread-events thread)))))
  376. (let ((time-left (- (thread-time thread)
  377. (interrupt-timer-time))))
  378. ; (debug-message "[thread waiting]")
  379. (set-thread-time! thread 'waiting)
  380. (switch-to-thread (thread-scheduler thread)
  381. (list time-left (enum event-type blocked)))))
  382. (set-enabled-interrupts! interrupts))))
  383. ; Is THREAD waiting for something to happen.
  384. (define (waiting? thread)
  385. (eq? (thread-time thread) 'waiting))
  386. ; Various calls to SUSPEND.
  387. (define (relinquish-timeslice)
  388. (suspend (enum event-type out-of-time) '()))
  389. (define (block)
  390. (suspend (enum event-type blocked) '()))
  391. ; Block if the current proposal succeeds. Returns true if successful and false
  392. ; if the commit fails. The cell becomes the thread's cell. It will be cleared
  393. ; if the thread is killed.
  394. (define (maybe-commit-and-block cell)
  395. (disable-interrupts!)
  396. (cond ((maybe-commit)
  397. (set-thread-cell! (current-thread) cell)
  398. (suspend-to (thread-scheduler (current-thread))
  399. (list (enum event-type blocked)))
  400. #t)
  401. (else
  402. (enable-interrupts!)
  403. #f)))
  404. ; Utility procedure for the common case of blocking on a queue.
  405. (define (maybe-commit-and-block-on-queue queue)
  406. (let ((cell (make-cell (current-thread))))
  407. (enqueue! queue cell)
  408. (maybe-commit-and-block cell)))
  409. ; Send the upcall to the current scheduler and check the return value(s)
  410. ; to see if it was handled properly.
  411. (define (upcall token . args)
  412. (propogate-upcall (current-thread) token args))
  413. ; Send the upcall to the current thread's handler, if it has one.
  414. (define (propogate-upcall thread token args)
  415. (if (thread-scheduler (current-thread))
  416. (suspend (enum event-type upcall) (list thread token args))
  417. (begin
  418. (interrupt-thread thread
  419. (lambda ()
  420. (apply error "unhandled upcall" token args)))
  421. (values))))
  422. (define (kill-thread! thread) ; dangerous!
  423. (interrupt-thread thread
  424. (lambda ignored
  425. (exit (enum event-type killed) '()))))
  426. ; Also ends the thread, but lets it run any pending dynamic-winds.
  427. (define (terminate-thread! thread)
  428. (let ((interrupts (set-enabled-interrupts! no-interrupts)))
  429. (clear-thread-cell! thread)
  430. (interrupt-thread thread (lambda _ (terminate-current-thread)))))
  431. ;----------------
  432. ; Make THREAD execute PROC the next time it is run. The thread's own
  433. ; continuation is passed whatever PROC returns.
  434. (define (interrupt-thread thread proc)
  435. (let ((interrupts (set-enabled-interrupts! no-interrupts)))
  436. (cond ((and (thread? thread)
  437. (not (running? thread)))
  438. (set-thread-continuation!
  439. thread
  440. (compose-continuation proc (thread-continuation thread)))
  441. (set-enabled-interrupts! interrupts))
  442. (else
  443. (set-enabled-interrupts! interrupts)
  444. (call-error "invalid argument" interrupt-thread thread)))))
  445. ;----------------
  446. ; Dealing with event queues
  447. ; Returns the next event scheduled for the current thread.
  448. (define (get-next-event!)
  449. (let* ((interrupts (disable-interrupts!))
  450. (event (next-event! (current-thread))))
  451. (set-enabled-interrupts! interrupts)
  452. (if event
  453. (apply values event)
  454. (enum event-type no-event))))
  455. (define (event-pending?)
  456. (let ((events (thread-events (current-thread))))
  457. (and events (not (queue-empty? events)))))
  458. ; Schedule the given event for THREAD and then check to see if any of the
  459. ; current thread's ancestors have received an event.
  460. (define (schedule-event thread . event)
  461. (let ((interrupts (disable-interrupts!)))
  462. (really-schedule-event thread event)
  463. (maybe-suspend)
  464. (set-enabled-interrupts! interrupts)
  465. (unspecific)))
  466. ; Resume running the highest thread which has both a current task and a
  467. ; waiting event. If there are none such the current thread can continue.
  468. (define (maybe-suspend)
  469. (let loop ((thread (root-scheduler)))
  470. (let ((next (thread-current-task thread)))
  471. (if next
  472. (let ((event (next-event! thread)))
  473. (if event
  474. (suspend-to thread event)
  475. (loop next)))))))
  476. ; Add EVENT to THREAD's event queue. If THREAD is already running some other
  477. ; thread but is not currently runnning, then we arrange for it's call to RUN
  478. ; to return as soon as it is resumed.
  479. (define (really-schedule-event thread event)
  480. ; (debug-message "[event "
  481. ; (enumerand->name (car event) event-type)
  482. ; " for "
  483. ; (thread-uid thread)
  484. ; "]")
  485. (cond ((and (thread-current-task thread)
  486. (not (running? thread)))
  487. (set-thread-arguments! thread
  488. (cons (thread-time (thread-current-task thread))
  489. event))
  490. (set-thread-current-task! thread #f))
  491. (else
  492. (add-event! thread event)))
  493. (if (waiting? thread)
  494. (schedule-wakeup thread)))
  495. ; Make THREAD's scheduler aware of the fact that THREAD is runnable.
  496. ; Called with interrupts disabled.
  497. (define (schedule-wakeup thread)
  498. (let ((scheduler (thread-scheduler thread)))
  499. (set-thread-time! thread 0) ; clear WAITING flag
  500. (if scheduler
  501. (really-schedule-event scheduler
  502. (list (enum event-type runnable)
  503. thread)))))
  504. ; Debugging routine
  505. (define (show-running)
  506. (apply debug-message "Running:" (do ((e (current-thread) (thread-scheduler e))
  507. (l '() (cons (thread-name e)
  508. (cons " " l))))
  509. ((not e)
  510. (reverse l)))))
  511. ; Walk up the scheduler pointers from the current thread to see if THREAD is
  512. ; running.
  513. (define (running? thread)
  514. (let loop ((running (current-thread)))
  515. (cond ((not running)
  516. #f)
  517. ((eq? running thread)
  518. #t)
  519. (else
  520. (loop (thread-scheduler running))))))
  521. ; Spawn a new thread either on the current thread's scheduler or on a specific
  522. ; scheduler.
  523. (define (spawn thunk . id)
  524. (apply spawn-on-scheduler
  525. (thread-scheduler (current-thread))
  526. thunk
  527. id))
  528. (define (spawn-on-root thunk . id)
  529. (if (root-scheduler)
  530. (apply spawn-on-scheduler
  531. (root-scheduler)
  532. thunk
  533. id)
  534. (thunk)))
  535. (define (spawn-on-scheduler scheduler thunk . id)
  536. (let ((thread (make-thread thunk
  537. (if (null? id) #f (car id)))))
  538. (schedule-event scheduler
  539. (enum event-type spawned)
  540. thread)
  541. thread))
  542. ; Enqueue a RUNNABLE event for THREAD's scheduler.
  543. (define (make-ready thread . args)
  544. (if (thread-cell thread)
  545. (begin
  546. (clear-thread-cell! thread)
  547. (set-thread-arguments! thread args)
  548. (if (thread-scheduler thread)
  549. (schedule-event (thread-scheduler thread)
  550. (enum event-type runnable)
  551. thread)
  552. (error "MAKE-READY thread has no scheduler" thread)))))
  553. (define (clear-thread-cell! thread)
  554. (let ((cell (thread-cell thread)))
  555. (if cell
  556. (begin
  557. (set-thread-cell! thread #f)
  558. (cell-set! cell #f)))))
  559. ; Same as MAKE-READY, except that we only schedule the thread if the current
  560. ; proposal succeeds.
  561. (define (maybe-commit-and-make-ready thread-or-queue . args)
  562. (let ((ints (set-enabled-interrupts! 0)))
  563. (cond ((maybe-commit)
  564. (if (queue? thread-or-queue)
  565. (apply make-threads-ready thread-or-queue args)
  566. (apply make-ready thread-or-queue args))
  567. (set-enabled-interrupts! ints)
  568. #t)
  569. (else
  570. (set-enabled-interrupts! ints)
  571. #f))))
  572. ;; Common pattern
  573. (define (maybe-commit-no-interrupts thunk)
  574. (let ((ints (disable-interrupts!)))
  575. (cond ((maybe-commit)
  576. (thunk)
  577. (set-enabled-interrupts! ints)
  578. #t)
  579. (else
  580. (set-enabled-interrupts! ints)
  581. #f))))
  582. ; Make all of the threads on QUEUE ready (and don't run any of them until
  583. ; all have been processed).
  584. (define (make-threads-ready queue . args)
  585. (let loop ()
  586. (if (queue-empty? queue)
  587. (maybe-suspend)
  588. (let ((thread (cell-ref (dequeue! queue))))
  589. (if thread
  590. (apply make-ready thread args))
  591. (loop)))))
  592. ;----------------
  593. (define (schedule-interrupt! time)
  594. (schedule-interrupt time))
  595. (define (cancel-timer-interrupt!)
  596. (schedule-interrupt 0))
  597. ; Use CHEAP-TIME to save the cost of a system call on every context switch.
  598. ; This clock is reset whenever we request an interrupt.
  599. (define (interrupt-timer-time)
  600. (time (enum time-option cheap-time) #f))
  601. ; Used for waking sleepers
  602. (define (real-time)
  603. (time (enum time-option real-time) #f))
  604. ; Install our own handler for timer interrupts and then start running threads.
  605. (define (with-threads thunk)
  606. (with-interrupts-inhibited
  607. (lambda ()
  608. (dynamic-wind
  609. (lambda ()
  610. (set-interrupt-handler! (enum interrupt alarm)
  611. handle-timer-interrupt))
  612. (lambda ()
  613. (start-multitasking thunk))
  614. (lambda ()
  615. (cancel-timer-interrupt!))))))
  616. ; Add handler for deadlock and termination and morph ourselves into the
  617. ; initial thread.
  618. (define (start-multitasking thunk)
  619. (call-with-current-continuation
  620. (lambda (exit-multitasking)
  621. (with-handler
  622. (lambda (c punt)
  623. (if (deadlock? c)
  624. (exit-multitasking 0)
  625. (punt)))
  626. (lambda ()
  627. (call-with-current-continuation
  628. (lambda (terminate)
  629. (with-handler (lambda (c punt)
  630. (if (terminate? c)
  631. (terminate 0)
  632. (punt)))
  633. (lambda ()
  634. (set! *thread-uid* 0)
  635. (let ((thread (make-thread #f ; thunk
  636. 'initial-thread)))
  637. (set-thread-dynamic-env! thread (get-dynamic-env))
  638. (set-thread-time! thread #f)
  639. (set-thread-dynamic-point! thread (get-dynamic-point))
  640. (set-current-thread! thread)
  641. (session-data-set! root-scheduler-slot thread))
  642. ;; End the LET to get THREAD out of the continuation to THUNK
  643. ;; Interrupts were turned off by START-THREADS
  644. (enable-interrupts!)
  645. ;; We throw out after THUNK to avoid the EXIT below, as we
  646. ;; have no scheduler to exit to.
  647. (exit-multitasking (thunk))))))
  648. ;; land here when terminating a thread
  649. (exit (enum event-type completed) '()))))))
  650. ; Raised when there is nothing to run.
  651. (define-condition-type 'deadlock '())
  652. (define deadlock? (condition-predicate 'deadlock))
  653. ; Raised when the current thread has been killed.
  654. (define-condition-type 'terminate '())
  655. (define terminate? (condition-predicate 'terminate))
  656. ; Kill the current thread. DEBUG-MESSAGE is used to try and make sure that some
  657. ; record exists when an error occured. The system may be too broken for ERROR
  658. ; to work properly.
  659. (define (terminate-current-thread)
  660. (signal 'terminate)
  661. (debug-message "Can't terminate current thread "
  662. (thread-uid (current-thread))
  663. " "
  664. (thread-name (current-thread)))
  665. (error "can't terminate current thread")
  666. 0) ; suppress bogus compiler warning
  667. ;----------------
  668. ; A slot in the session data to hold the root thread.
  669. (define root-scheduler-slot (make-session-data-slot! #f))
  670. (define (root-scheduler)
  671. (session-data-ref root-scheduler-slot))
  672. ;----------------
  673. ; Fast binary version of max.
  674. (define (max2 x y)
  675. (if (< x y)
  676. y
  677. x))