PageRenderTime 27ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/quicklisp/dists/quicklisp/software/bordeaux-threads-v0.8.5/src/default-implementations.lisp

https://gitlab.com/dto/ecl-android-games-src
Lisp | 335 lines | 239 code | 50 blank | 46 comment | 27 complexity | bfa67c1f4fe55de8386a74e5f472a025 MD5 | raw file
  1. ;;;; -*- indent-tabs-mode: nil -*-
  2. (in-package #:bordeaux-threads)
  3. ;;; Helper macros
  4. (defmacro defdfun (name args doc &body body)
  5. `(eval-when (:compile-toplevel :load-toplevel :execute)
  6. (unless (fboundp ',name)
  7. (defun ,name ,args ,@body))
  8. (setf (documentation ',name 'function)
  9. (or (documentation ',name 'function) ,doc))))
  10. (defmacro defdmacro (name args doc &body body)
  11. `(eval-when (:compile-toplevel :load-toplevel :execute)
  12. (unless (fboundp ',name)
  13. (defmacro ,name ,args ,@body))
  14. (setf (documentation ',name 'function)
  15. (or (documentation ',name 'function) ,doc))))
  16. ;;; Thread Creation
  17. (defdfun start-multiprocessing ()
  18. "If the host implementation uses user-level threads, start the
  19. scheduler and multiprocessing, otherwise do nothing.
  20. It is safe to call repeatedly."
  21. nil)
  22. (defdfun make-thread (function &key name
  23. (initial-bindings *default-special-bindings*))
  24. "Creates and returns a thread named NAME, which will call the
  25. function FUNCTION with no arguments: when FUNCTION returns, the
  26. thread terminates. NAME defaults to \"Anonymous thread\" if unsupplied.
  27. On systems that do not support multi-threading, MAKE-THREAD will
  28. signal an error.
  29. The interaction between threads and dynamic variables is in some
  30. cases complex, and depends on whether the variable has only a global
  31. binding (as established by e.g. DEFVAR/DEFPARAMETER/top-level SETQ)
  32. or has been bound locally (e.g. with LET or LET*) in the calling
  33. thread.
  34. - Global bindings are shared between threads: the initial value of a
  35. global variable in the new thread will be the same as in the
  36. parent, and an assignment to such a variable in any thread will be
  37. visible to all threads in which the global binding is visible.
  38. - Local bindings, such as the ones introduced by INITIAL-BINDINGS,
  39. are local to the thread they are introduced in, except that
  40. - Local bindings in the the caller of MAKE-THREAD may or may not be
  41. shared with the new thread that it creates: this is
  42. implementation-defined. Portable code should not depend on
  43. particular behaviour in this case, nor should it assign to such
  44. variables without first rebinding them in the new thread."
  45. (%make-thread (binding-default-specials function initial-bindings)
  46. (or name "Anonymous thread")))
  47. (defdfun %make-thread (function name)
  48. "The actual implementation-dependent function that creates threads."
  49. (declare (ignore function name))
  50. (error (make-threading-support-error)))
  51. (defdfun current-thread ()
  52. "Returns the thread object for the calling
  53. thread. This is the same kind of object as would be returned by
  54. MAKE-THREAD."
  55. nil)
  56. (defdfun threadp (object)
  57. "Returns true if object is a thread, otherwise NIL."
  58. (declare (ignore object))
  59. nil)
  60. (defdfun thread-name (thread)
  61. "Returns the name of the thread, as supplied to MAKE-THREAD."
  62. (declare (ignore thread))
  63. "Main thread")
  64. ;;; Resource contention: locks and recursive locks
  65. (defdfun make-lock (&optional name)
  66. "Creates a lock (a mutex) whose name is NAME. If the system does not
  67. support multiple threads this will still return some object, but it
  68. may not be used for very much."
  69. ;; In CLIM-SYS this is a freshly consed list (NIL). I don't know if
  70. ;; there's some good reason it should be said structure or that it
  71. ;; be freshly consed - EQ comparison of locks?
  72. (declare (ignore name))
  73. (list nil))
  74. (defdfun acquire-lock (lock &optional wait-p)
  75. "Acquire the lock LOCK for the calling thread.
  76. WAIT-P governs what happens if the lock is not available: if WAIT-P
  77. is true, the calling thread will wait until the lock is available
  78. and then acquire it; if WAIT-P is NIL, ACQUIRE-LOCK will return
  79. immediately. ACQUIRE-LOCK returns true if the lock was acquired and
  80. NIL otherwise.
  81. This specification does not define what happens if a thread
  82. attempts to acquire a lock that it already holds. For applications
  83. that require locks to be safe when acquired recursively, see instead
  84. MAKE-RECURSIVE-LOCK and friends."
  85. (declare (ignore lock wait-p))
  86. t)
  87. (defdfun release-lock (lock)
  88. "Release LOCK. It is an error to call this unless
  89. the lock has previously been acquired (and not released) by the same
  90. thread. If other threads are waiting for the lock, the
  91. ACQUIRE-LOCK call in one of them will now be able to continue.
  92. This function has no interesting return value."
  93. (declare (ignore lock))
  94. (values))
  95. (defdmacro with-lock-held ((place) &body body)
  96. "Evaluates BODY with the lock named by PLACE, the value of which
  97. is a lock created by MAKE-LOCK. Before the forms in BODY are
  98. evaluated, the lock is acquired as if by using ACQUIRE-LOCK. After the
  99. forms in BODY have been evaluated, or if a non-local control transfer
  100. is caused (e.g. by THROW or SIGNAL), the lock is released as if by
  101. RELEASE-LOCK.
  102. Note that if the debugger is entered, it is unspecified whether the
  103. lock is released at debugger entry or at debugger exit when execution
  104. is restarted."
  105. `(when (acquire-lock ,place t)
  106. (unwind-protect
  107. (locally ,@body)
  108. (release-lock ,place))))
  109. (defdfun make-recursive-lock (&optional name)
  110. "Create and return a recursive lock whose name is NAME. A recursive
  111. lock differs from an ordinary lock in that a thread that already
  112. holds the recursive lock can acquire it again without blocking. The
  113. thread must then release the lock twice before it becomes available
  114. for another thread."
  115. (declare (ignore name))
  116. (list nil))
  117. (defdfun acquire-recursive-lock (lock)
  118. "As for ACQUIRE-LOCK, but for recursive locks."
  119. (declare (ignore lock))
  120. t)
  121. (defdfun release-recursive-lock (lock)
  122. "Release the recursive LOCK. The lock will only
  123. become free after as many Release operations as there have been
  124. Acquire operations. See RELEASE-LOCK for other information."
  125. (declare (ignore lock))
  126. (values))
  127. (defdmacro with-recursive-lock-held ((place &key timeout) &body body)
  128. "Evaluates BODY with the recursive lock named by PLACE, which is a
  129. reference to a recursive lock created by MAKE-RECURSIVE-LOCK. See
  130. WITH-LOCK-HELD etc etc"
  131. (declare (ignore timeout))
  132. `(when (acquire-recursive-lock ,place)
  133. (unwind-protect
  134. (locally ,@body)
  135. (release-recursive-lock ,place))))
  136. ;;; Resource contention: condition variables
  137. ;;; A condition variable provides a mechanism for threads to put
  138. ;;; themselves to sleep while waiting for the state of something to
  139. ;;; change, then to be subsequently woken by another thread which has
  140. ;;; changed the state.
  141. ;;;
  142. ;;; A condition variable must be used in conjunction with a lock to
  143. ;;; protect access to the state of the object of interest. The
  144. ;;; procedure is as follows:
  145. ;;;
  146. ;;; Suppose two threads A and B, and some kind of notional event
  147. ;;; channel C. A is consuming events in C, and B is producing them.
  148. ;;; CV is a condition-variable
  149. ;;;
  150. ;;; 1) A acquires the lock that safeguards access to C
  151. ;;; 2) A threads and removes all events that are available in C
  152. ;;; 3) When C is empty, A calls CONDITION-WAIT, which atomically
  153. ;;; releases the lock and puts A to sleep on CV
  154. ;;; 4) Wait to be notified; CONDITION-WAIT will acquire the lock again
  155. ;;; before returning
  156. ;;; 5) Loop back to step 2, for as long as threading should continue
  157. ;;;
  158. ;;; When B generates an event E, it
  159. ;;; 1) acquires the lock guarding C
  160. ;;; 2) adds E to the channel
  161. ;;; 3) calls CONDITION-NOTIFY on CV to wake any sleeping thread
  162. ;;; 4) releases the lock
  163. ;;;
  164. ;;; To avoid the "lost wakeup" problem, the implementation must
  165. ;;; guarantee that CONDITION-WAIT in thread A atomically releases the
  166. ;;; lock and sleeps. If this is not guaranteed there is the
  167. ;;; possibility that thread B can add an event and call
  168. ;;; CONDITION-NOTIFY between the lock release and the sleep - in this
  169. ;;; case the notify call would not see A, which would be left sleeping
  170. ;;; despite there being an event available.
  171. (defdfun thread-yield ()
  172. "Allows other threads to run. It may be necessary or desirable to
  173. call this periodically in some implementations; others may schedule
  174. threads automatically. On systems that do not support
  175. multi-threading, this does nothing."
  176. (values))
  177. (defdfun make-condition-variable (&key name)
  178. "Returns a new condition-variable object for use
  179. with CONDITION-WAIT and CONDITION-NOTIFY."
  180. (declare (ignore name))
  181. nil)
  182. (defdfun condition-wait (condition-variable lock &key timeout)
  183. "Atomically release LOCK and enqueue the calling
  184. thread waiting for CONDITION-VARIABLE. The thread will resume when
  185. another thread has notified it using CONDITION-NOTIFY; it may also
  186. resume if interrupted by some external event or in other
  187. implementation-dependent circumstances: the caller must always test
  188. on waking that there is threading to be done, instead of assuming
  189. that it can go ahead.
  190. It is an error to call function this unless from the thread that
  191. holds LOCK.
  192. If TIMEOUT is nil or not provided, the system always reacquires LOCK
  193. before returning to the caller. In this case T is returned.
  194. If TIMEOUT is non-nil, the call will return after at most TIMEOUT
  195. seconds (approximately), whether or not a notification has occurred.
  196. Either NIL or T will be returned. A return of NIL indicates that the
  197. lock is no longer held and that the timeout has expired. A return of
  198. T indicates that the lock is held, in which case the timeout may or
  199. may not have expired.
  200. **NOTE**: The behavior of CONDITION-WAIT with TIMEOUT diverges from
  201. the POSIX function pthread_cond_timedwait. The former may return
  202. without the lock being held while the latter always returns with the
  203. lock held.
  204. In an implementation that does not support multiple threads, this
  205. function signals an error."
  206. (declare (ignore condition-variable lock timeout))
  207. (error (make-threading-support-error)))
  208. (defdfun condition-notify (condition-variable)
  209. "Notify at least one of the threads waiting for
  210. CONDITION-VARIABLE. It is implementation-dependent whether one or
  211. more than one (and possibly all) threads are woken, but if the
  212. implementation is capable of waking only a single thread (not all
  213. are) this is probably preferable for efficiency reasons. The order
  214. of wakeup is unspecified and does not necessarily relate to the
  215. order that the threads went to sleep in.
  216. CONDITION-NOTIFY has no useful return value. In an implementation
  217. that does not support multiple threads, it has no effect."
  218. (declare (ignore condition-variable))
  219. (values))
  220. ;;; Timeouts
  221. (defdmacro with-timeout ((timeout) &body body)
  222. "Execute `BODY' and signal a condition of type TIMEOUT if the execution of
  223. BODY does not complete within `TIMEOUT' seconds. On implementations which do not
  224. support WITH-TIMEOUT natively and don't support threads either it has no effect."
  225. (declare (ignorable timeout))
  226. #+thread-support
  227. (let ((ok-tag (gensym "OK"))
  228. (timeout-tag (gensym "TIMEOUT"))
  229. (caller (gensym "CALLER"))
  230. (sleeper (gensym "SLEEPER")))
  231. (once-only (timeout)
  232. `(let (,sleeper)
  233. (multiple-value-prog1
  234. (catch ',ok-tag
  235. (catch ',timeout-tag
  236. (let ((,caller (current-thread)))
  237. (setf ,sleeper
  238. (make-thread #'(lambda ()
  239. (sleep ,timeout)
  240. (interrupt-thread ,caller
  241. #'(lambda ()
  242. (ignore-errors
  243. (throw ',timeout-tag nil)))))
  244. :name (format nil "WITH-TIMEOUT thread serving: ~S."
  245. (thread-name ,caller))))
  246. (throw ',ok-tag (progn ,@body))))
  247. (error 'timeout :length ,timeout))
  248. (when (thread-alive-p ,sleeper)
  249. (destroy-thread ,sleeper))))))
  250. #-thread-support
  251. `(progn
  252. ,@body))
  253. ;;; Introspection/debugging
  254. ;;; The following functions may be provided for debugging purposes,
  255. ;;; but are not advised to be called from normal user code.
  256. (defdfun all-threads ()
  257. "Returns a sequence of all of the threads. This may not
  258. be freshly-allocated, so the caller should not modify it."
  259. (error (make-threading-support-error)))
  260. (defdfun interrupt-thread (thread function)
  261. "Interrupt THREAD and cause it to evaluate FUNCTION
  262. before continuing with the interrupted path of execution. This may
  263. not be a good idea if THREAD is holding locks or doing anything
  264. important. On systems that do not support multiple threads, this
  265. function signals an error."
  266. (declare (ignore thread function))
  267. (error (make-threading-support-error)))
  268. (defdfun destroy-thread (thread)
  269. "Terminates the thread THREAD, which is an object
  270. as returned by MAKE-THREAD. This should be used with caution: it is
  271. implementation-defined whether the thread runs cleanup forms or
  272. releases its locks first.
  273. Destroying the calling thread is an error."
  274. (declare (ignore thread))
  275. (error (make-threading-support-error)))
  276. (defdfun thread-alive-p (thread)
  277. "Returns true if THREAD is alive, that is, if
  278. DESTROY-THREAD has not been called on it."
  279. (declare (ignore thread))
  280. (error (make-threading-support-error)))
  281. (defdfun join-thread (thread)
  282. "Wait until THREAD terminates. If THREAD
  283. has already terminated, return immediately."
  284. (declare (ignore thread))
  285. (error (make-threading-support-error)))