Fork of https://github.com/google/lisp-koans so that I could go through them. THIS CONTAINS ANSWERS.

threads.lsp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. ;; Copyright 2013 Google Inc.
  2. ;;
  3. ;; Licensed under the Apache License, Version 2.0 (the "License");
  4. ;; you may not use this file except in compliance with the License.
  5. ;; You may obtain a copy of the License at
  6. ;;
  7. ;; http://www.apache.org/licenses/LICENSE-2.0
  8. ;;
  9. ;; Unless required by applicable law or agreed to in writing, software
  10. ;; distributed under the License is distributed on an "AS IS" BASIS,
  11. ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. ;; See the License for the specific language governing permissions and
  13. ;; limitations under the License.
  14. ;; NOTE: This koan group uses language features specific to sbcl, that are
  15. ;; not part of the Common Lisp specification. If you are not using sbcl,
  16. ;; feel free to skip this group by removing it from '.koans'
  17. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  18. ;; Making threads with sb-thread:make-thread ;;
  19. ;; Joining threads with sb-thread:join-thread ;;
  20. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  21. ;; sb-thread takes a -function- as a parameter.
  22. ;; This function will be executed in a separate thread.
  23. ;; Since the execution order of separate threads is not guaranteed,
  24. ;; we must -join- the threads in order to make our assertions.
  25. (defvar *greeting* "no greeting")
  26. (defun sets-socal-greeting ()
  27. (setf *greeting* "Sup, dudes"))
  28. (define-test test-hello-world-thread
  29. "Create a thread which returns 'hello world', then ends.
  30. using a lambda as the supplied function to execute."
  31. (assert-equal *greeting* "no greeting")
  32. (let ((greeting-thread
  33. (sb-thread:make-thread
  34. (lambda ()
  35. (setf *greeting* "hello world")))))
  36. (sb-thread:join-thread greeting-thread)
  37. (assert-equal *greeting* "hello world")
  38. (setf greeting-thread (sb-thread:make-thread #'sets-socal-greeting))
  39. (sb-thread:join-thread greeting-thread)
  40. (assert-equal *greeting* ____)))
  41. (define-test test-join-thread-return-value
  42. "the return value of the thread is passed in sb-thread:join-thread"
  43. (let ((my-thread (sb-thread:make-thread
  44. (lambda () (* 11 99)))))
  45. (assert-equal ____ (sb-thread:join-thread my-thread))))
  46. (define-test test-threads-can-have-names
  47. "Threads can have names. Names can be useful in diagnosing problems
  48. or reporting."
  49. (let ((empty-plus-thread
  50. (sb-thread:make-thread #'+
  51. :name "what is the sum of no things adding?")))
  52. (assert-equal (sb-thread:thread-name empty-plus-thread)
  53. ____)))
  54. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  55. ;; Sending arguments to the thread function: ;;
  56. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  57. (defun returns-hello-name (name)
  58. (format nil "Hello, ~a" name))
  59. (defun double-wrap-list (x y z)
  60. (list (list x y z)))
  61. ;; Create a thread which will print out "Hello -Name-" using
  62. ;; the named write-hello-name function. Arguments are handed
  63. ;; to threads as a list, unless there is just a single argument
  64. ;; then it does not need to be wrapped in a list.
  65. (define-test test-sending-arguments-to-thread
  66. (assert-equal "Hello, Buster"
  67. (sb-thread:join-thread
  68. (sb-thread:make-thread 'returns-hello-name
  69. :arguments "Buster")))
  70. (assert-equal ____
  71. (sb-thread:join-thread
  72. (sb-thread:make-thread 'double-wrap-list
  73. :arguments '(3 4 5)))))
  74. ;; ----
  75. (defvar *accum* 0)
  76. (defun accum-after-time (time arg1)
  77. "sleeps for time seconds and then adds arg1 to *accum*"
  78. (sleep time)
  79. (incf *accum* arg1))
  80. (defvar *before-time-millisec* 0)
  81. (defvar *after-time-millisec* 0)
  82. ;; cheap and dirty time measuring function
  83. (defun duration-ms ()
  84. (- *after-time-millisec* *before-time-millisec*))
  85. (define-test test-run-in-series
  86. "get internal real time returns a time stamp in milliseconds"
  87. (setf *accum* 0)
  88. (setf *before-time-millisec* (get-internal-real-time))
  89. (accum-after-time 0.3 1)
  90. (accum-after-time 0.2 2)
  91. (accum-after-time 0.1 4)
  92. (setf *after-time-millisec* (get-internal-real-time))
  93. (true-or-false? ___ (> (duration-ms) 500))
  94. (true-or-false? ___ (< (duration-ms) 700))
  95. (assert-equal *accum* ___))
  96. (define-test test-run-in-parallel
  97. "same program as above, executed in threads. Sleeps are simultaneous"
  98. (setf *accum* 0)
  99. (setf *before-time-millisec* (get-internal-real-time))
  100. (let ((thread-1 (sb-thread:make-thread 'accum-after-time :arguments '(0.3 1)))
  101. (thread-2 (sb-thread:make-thread 'accum-after-time :arguments '(0.2 2)))
  102. (thread-3 (sb-thread:make-thread 'accum-after-time :arguments '(0.1 4))))
  103. (sb-thread:join-thread thread-1)
  104. (sb-thread:join-thread thread-2)
  105. (sb-thread:join-thread thread-3))
  106. (setf *after-time-millisec* (get-internal-real-time))
  107. (true-or-false? ___ (> (duration-ms) 200))
  108. (true-or-false? ___ (< (duration-ms) 400))
  109. (assert-equal *accum* ___))
  110. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  111. ;; killing renegade threads ;;
  112. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  113. (defun spawn-looping-thread (name)
  114. "create a never-ending looping thread with a given name"
  115. (sb-thread:make-thread (lambda () (loop)) :name name))
  116. (defvar *top-thread* sb-thread:*current-thread*)
  117. (defun main-thread-p (thread) (eq thread *top-thread*))
  118. (defun kill-thread-if-not-main (thread)
  119. " kills a given thread, unless the thread is the main thread.
  120. returns nil if thread is main.
  121. returns a 'terminated~' string otherwise"
  122. (unless (main-thread-p thread)
  123. (sb-thread:terminate-thread thread)
  124. (concatenate 'string "terminated " (sb-thread:thread-name thread))))
  125. (defun kill-spawned-threads ()
  126. "kill all lisp threads except the main thread."
  127. (map 'list 'kill-thread-if-not-main (sb-thread:list-all-threads)))
  128. (defun spawn-three-loopers ()
  129. "Spawn three run-aways."
  130. (progn
  131. (spawn-looping-thread "looper one")
  132. (spawn-looping-thread "looper two")
  133. (spawn-looping-thread "looper three")))
  134. (define-test test-counting-and-killing-threads
  135. "list-all-threads makes a list of all running threads in this lisp. The sleep
  136. calls are necessary, as killed threads are not instantly removed from the
  137. list of all running threads."
  138. (assert-equal ___ (length (sb-thread:list-all-threads)))
  139. (kill-thread-if-not-main (spawn-looping-thread "NEVER CATCH ME~! NYA NYA!"))
  140. (sleep 0.01)
  141. (assert-equal ___ (length (sb-thread:list-all-threads)))
  142. (spawn-three-loopers)
  143. (assert-equal ___ (length (sb-thread:list-all-threads)))
  144. (kill-spawned-threads)
  145. (sleep 0.01)
  146. (assert-equal ___ (length (sb-thread:list-all-threads))))
  147. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  148. ;; bindings are not inherited across threads ;;
  149. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  150. (defvar *v* 0)
  151. (defun returns-v ()
  152. *v*)
  153. (define-test test-threads-dont-get-bindings
  154. "bindings are not inherited across threads"
  155. (let ((thread-ret-val (sb-thread:join-thread
  156. (sb-thread:make-thread 'returns-v))))
  157. (assert-equal thread-ret-val ____))
  158. (let ((*v* "LEXICAL BOUND VALUE"))
  159. (assert-equal *v* ____)
  160. (let ((thread-ret-val (sb-thread:join-thread
  161. (sb-thread:make-thread 'returns-v))))
  162. (assert-equal thread-ret-val ____))))
  163. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  164. ;; global state (special vars) are ;;
  165. ;; shared across threads ;;
  166. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  167. (defvar *g* 0)
  168. (defun waits-and-increments-g (&optional (n 0.2))
  169. "sets *g* to 1 + the value of *g* n seconds ago"
  170. (let ((my-remembered-g *g*))
  171. (sleep n)
  172. (setq *g* (+ 1 my-remembered-g))))
  173. (define-test test-serial-wait-and-increment
  174. "incrementing *g* three times and expecting
  175. the final value to be three works."
  176. (setf *g* 0)
  177. (waits-and-increments-g)
  178. (waits-and-increments-g)
  179. (waits-and-increments-g)
  180. (assert-equal *g* ___))
  181. (define-test test-parallel-wait-and-increment
  182. (setf *g* 0)
  183. (let ((thread-1 (sb-thread:make-thread 'waits-and-increments-g))
  184. (thread-2 (sb-thread:make-thread 'waits-and-increments-g))
  185. (thread-3 (sb-thread:make-thread 'waits-and-increments-g)))
  186. (sb-thread:join-thread thread-1)
  187. (sb-thread:join-thread thread-2)
  188. (sb-thread:join-thread thread-3)
  189. (assert-equal *g* ___)))
  190. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  191. ;; Global state can be protected ;;
  192. ;; with a mutex. ;;
  193. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  194. (setf *g* 0)
  195. (defvar *gs-mutex* (sb-thread:make-mutex :name "g's lock"))
  196. (defun protected-increments-g (&optional (n 0.1))
  197. "Surround all references to *g* within the with-mutex form."
  198. (sb-thread:with-mutex (*gs-mutex*)
  199. (let ((my-remembered-g *g*))
  200. (sleep n)
  201. (setq *g* (+ 1 my-remembered-g)))))
  202. (define-test test-parallel-wait-and-increment-with-mutex
  203. (setf *g* 0)
  204. (let ((thread-1 (sb-thread:make-thread 'protected-increments-g))
  205. (thread-2 (sb-thread:make-thread 'protected-increments-g))
  206. (thread-3 (sb-thread:make-thread 'protected-increments-g)))
  207. (sb-thread:join-thread thread-1)
  208. (sb-thread:join-thread thread-2)
  209. (sb-thread:join-thread thread-3)
  210. (assert-equal *g* ___)))
  211. ;;;;;;;;;;;;;;;;
  212. ;; Semaphores ;;
  213. ;;;;;;;;;;;;;;;;
  214. ;; Incrementing a semaphore is an atomic operation.
  215. (defvar *g-semaphore* (sb-thread:make-semaphore :name "g" :count 0))
  216. (defun semaphore-increments-g ()
  217. (sb-thread:signal-semaphore *g-semaphore*))
  218. (define-test test-increment-semaphore
  219. (assert-equal 0 (sb-thread:semaphore-count *g-semaphore*))
  220. (sb-thread:join-thread (sb-thread:make-thread 'semaphore-increments-g :name "S incrementor 1"))
  221. (sb-thread:join-thread (sb-thread:make-thread 'semaphore-increments-g :name "S incrementor 2"))
  222. (sb-thread:join-thread (sb-thread:make-thread 'semaphore-increments-g :name "S incrementor 3"))
  223. (assert-equal ___ (sb-thread:semaphore-count *g-semaphore*)))
  224. ;; Semaphores can be used to manage resource allocation, and to trigger
  225. ;; threads to run when the semaphore value is above zero.
  226. (defvar *apples* (sb-thread:make-semaphore :name "how many apples" :count 0))
  227. (defvar *orchard-log* (make-array 10))
  228. (defvar *next-log-idx* 0)
  229. (defvar *orchard-log-mutex* (sb-thread:make-mutex :name "orchard log mutex"))
  230. (defun add-to-log (item)
  231. (sb-thread:with-mutex (*orchard-log-mutex*)
  232. (setf (aref *orchard-log* *next-log-idx*) item)
  233. (incf *next-log-idx*)))
  234. (defun apple-eater ()
  235. (sb-thread:wait-on-semaphore *apples*)
  236. (add-to-log "apple eaten."))
  237. (defun apple-grower ()
  238. (sleep 0.1)
  239. (add-to-log "apple grown.")
  240. (sb-thread:signal-semaphore *apples*))
  241. (defun num-apples ()
  242. (sb-thread:semaphore-count *apples*))
  243. (define-test test-orchard-simulation
  244. (assert-equal (num-apples) ___)
  245. (let ((eater-thread (sb-thread:make-thread 'apple-eater :name "apple eater thread")))
  246. (let ((grower-thread (sb-thread:make-thread 'apple-grower :name "apple grower thread")))
  247. (sb-thread:join-thread eater-thread)))
  248. (assert-equal (aref *orchard-log* 0) ____)
  249. (assert-equal (aref *orchard-log* 1) ____))