1 ;;; threads.el --- tests for threads.
3 ;; Copyright (C) 2012-2018 Free Software Foundation, Inc.
5 ;; This file is part of GNU Emacs.
7 ;; GNU Emacs is free software: you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation, either version 3 of the License, or
10 ;; (at your option) any later version.
12 ;; GNU Emacs is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
22 ;; Declare the functions in case Emacs has been configured --without-threads.
23 (declare-function all-threads
"thread.c" ())
24 (declare-function condition-mutex
"thread.c" (cond))
25 (declare-function condition-name
"thread.c" (cond))
26 (declare-function condition-notify
"thread.c" (cond &optional all
))
27 (declare-function condition-wait
"thread.c" (cond))
28 (declare-function current-thread
"thread.c" ())
29 (declare-function make-condition-variable
"thread.c" (mutex &optional name
))
30 (declare-function make-mutex
"thread.c" (&optional name
))
31 (declare-function make-thread
"thread.c" (function &optional name
))
32 (declare-function mutex-lock
"thread.c" (mutex))
33 (declare-function mutex-unlock
"thread.c" (mutex))
34 (declare-function thread--blocker
"thread.c" (thread))
35 (declare-function thread-alive-p
"thread.c" (thread))
36 (declare-function thread-join
"thread.c" (thread))
37 (declare-function thread-last-error
"thread.c" (&optional cleanup
))
38 (declare-function thread-name
"thread.c" (thread))
39 (declare-function thread-signal
"thread.c" (thread error-symbol data
))
40 (declare-function thread-yield
"thread.c" ())
43 (ert-deftest threads-is-one
()
44 "Test for existence of a thread."
45 (skip-unless (featurep 'threads
))
46 (should (current-thread)))
48 (ert-deftest threads-threadp
()
50 (skip-unless (featurep 'threads
))
51 (should (threadp (current-thread))))
53 (ert-deftest threads-type
()
54 "Test of thread type."
55 (skip-unless (featurep 'threads
))
56 (should (eq (type-of (current-thread)) 'thread
)))
58 (ert-deftest threads-name
()
59 "Test for name of a thread."
60 (skip-unless (featurep 'threads
))
62 (string= "hi bob" (thread-name (make-thread #'ignore
"hi bob")))))
64 (ert-deftest threads-alive
()
65 "Test for thread liveness."
66 (skip-unless (featurep 'threads
))
68 (thread-alive-p (make-thread #'ignore
))))
70 (ert-deftest threads-all-threads
()
71 "Simple test for all-threads."
72 (skip-unless (featurep 'threads
))
73 (should (listp (all-threads))))
75 (ert-deftest threads-main-thread
()
76 "Simple test for all-threads."
77 (skip-unless (featurep 'threads
))
78 (should (eq main-thread
(car (all-threads)))))
80 (defvar threads-test-global nil
)
82 (defun threads-test-thread1 ()
83 (setq threads-test-global
23))
85 (ert-deftest threads-basic
()
87 (skip-unless (featurep 'threads
))
90 (setq threads-test-global nil
)
91 (make-thread #'threads-test-thread1
)
92 (while (not threads-test-global
)
94 threads-test-global
)))
96 (ert-deftest threads-join
()
97 "Test of `thread-join'."
98 (skip-unless (featurep 'threads
))
101 (setq threads-test-global nil
)
102 (let ((thread (make-thread #'threads-test-thread1
)))
104 (and threads-test-global
105 (not (thread-alive-p thread
)))))))
107 (ert-deftest threads-join-self
()
108 "Cannot `thread-join' the current thread."
109 (skip-unless (featurep 'threads
))
110 (should-error (thread-join (current-thread))))
112 (defvar threads-test-binding nil
)
114 (defun threads-test-thread2 ()
115 (let ((threads-test-binding 23))
117 (setq threads-test-global
23))
119 (ert-deftest threads-let-binding
()
120 "Simple test of threads and let bindings."
121 (skip-unless (featurep 'threads
))
124 (setq threads-test-global nil
)
125 (make-thread #'threads-test-thread2
)
126 (while (not threads-test-global
)
128 (and (not threads-test-binding
)
129 threads-test-global
))))
131 (ert-deftest threads-mutexp
()
132 "Simple test of `mutexp'."
133 (skip-unless (featurep 'threads
))
134 (should-not (mutexp 'hi
)))
136 (ert-deftest threads-mutexp-2
()
137 "Another simple test of `mutexp'."
138 (skip-unless (featurep 'threads
))
139 (should (mutexp (make-mutex))))
141 (ert-deftest threads-mutex-type
()
143 (skip-unless (featurep 'threads
))
144 (should (eq (type-of (make-mutex)) 'mutex
)))
146 (ert-deftest threads-mutex-lock-unlock
()
147 "Test mutex-lock and unlock."
148 (skip-unless (featurep 'threads
))
150 (let ((mx (make-mutex)))
155 (ert-deftest threads-mutex-recursive
()
156 "Test mutex recursion."
157 (skip-unless (featurep 'threads
))
159 (let ((mx (make-mutex)))
166 (defvar threads-mutex nil
)
167 (defvar threads-mutex-key nil
)
169 (defun threads-test-mlock ()
170 (mutex-lock threads-mutex
)
171 (setq threads-mutex-key
23)
172 (while threads-mutex-key
174 (mutex-unlock threads-mutex
))
176 (ert-deftest threads-mutex-contention
()
177 "Test of mutex contention."
178 (skip-unless (featurep 'threads
))
181 (setq threads-mutex
(make-mutex))
182 (setq threads-mutex-key nil
)
183 (make-thread #'threads-test-mlock
)
184 ;; Wait for other thread to get the lock.
185 (while (not threads-mutex-key
)
188 (setq threads-mutex-key nil
)
189 (mutex-lock threads-mutex
)
190 (mutex-unlock threads-mutex
)
193 (defun threads-test-mlock2 ()
194 (setq threads-mutex-key
23)
195 (mutex-lock threads-mutex
))
197 (ert-deftest threads-mutex-signal
()
198 "Test signaling a blocked thread."
199 (skip-unless (featurep 'threads
))
202 (setq threads-mutex
(make-mutex))
203 (setq threads-mutex-key nil
)
204 (mutex-lock threads-mutex
)
205 (let ((thr (make-thread #'threads-test-mlock2
)))
206 (while (not threads-mutex-key
)
208 (thread-signal thr
'quit nil
)
212 (defun threads-test-io-switch ()
213 (setq threads-test-global
23))
215 (ert-deftest threads-io-switch
()
216 "Test that `accept-process-output' causes thread switch."
217 (skip-unless (featurep 'threads
))
220 (setq threads-test-global nil
)
221 (make-thread #'threads-test-io-switch
)
222 (while (not threads-test-global
)
223 (accept-process-output nil
1))
224 threads-test-global
)))
226 (ert-deftest threads-condvarp
()
227 "Simple test of `condition-variable-p'."
228 (skip-unless (featurep 'threads
))
229 (should-not (condition-variable-p 'hi
)))
231 (ert-deftest threads-condvarp-2
()
232 "Another simple test of `condition-variable-p'."
233 (skip-unless (featurep 'threads
))
234 (should (condition-variable-p (make-condition-variable (make-mutex)))))
236 (ert-deftest threads-condvar-type
()
238 (skip-unless (featurep 'threads
))
239 (should (eq (type-of (make-condition-variable (make-mutex)))
240 'condition-variable
)))
242 (ert-deftest threads-condvar-mutex
()
243 "Simple test of `condition-mutex'."
244 (skip-unless (featurep 'threads
))
246 (let ((m (make-mutex)))
247 (eq m
(condition-mutex (make-condition-variable m
))))))
249 (ert-deftest threads-condvar-name
()
250 "Simple test of `condition-name'."
251 (skip-unless (featurep 'threads
))
253 (eq nil
(condition-name (make-condition-variable (make-mutex))))))
255 (ert-deftest threads-condvar-name-2
()
256 "Another simple test of `condition-name'."
257 (skip-unless (featurep 'threads
))
260 (condition-name (make-condition-variable (make-mutex)
263 (defun threads-call-error ()
265 (error "Error is called"))
267 ;; This signals an error internally; the error should be caught.
268 (defun threads-custom ()
269 (defcustom threads-custom-face
'highlight
270 "Face used for thread customizations."
272 :group
'widget-faces
))
274 (ert-deftest threads-errors
()
275 "Test what happens when a thread signals an error."
276 (skip-unless (featurep 'threads
))
278 (setq th1
(make-thread #'threads-call-error
"call-error"))
279 (should (threadp th1
))
280 (while (thread-alive-p th1
)
282 (should (equal (thread-last-error)
283 '(error "Error is called")))
284 (should (equal (thread-last-error 'cleanup
)
285 '(error "Error is called")))
286 (should-not (thread-last-error))
287 (setq th2
(make-thread #'threads-custom
"threads-custom"))
288 (should (threadp th2
))))
290 (ert-deftest threads-sticky-point
()
291 "Test bug #25165 with point movement in cloned buffer."
292 (skip-unless (featurep 'threads
))
294 (insert "Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
295 (goto-char (point-min))
296 (clone-indirect-buffer nil nil
)
299 (should (= (point) 21))))
301 (ert-deftest threads-signal-early
()
302 "Test signaling a thread as soon as it is started by the OS."
303 (skip-unless (featurep 'threads
))
305 (make-thread #'(lambda ()
306 (while t
(thread-yield))))))
307 (thread-signal thread
'error nil
)
309 (should-not (thread-alive-p thread
))
310 (should (equal (thread-last-error) '(error)))))
312 (defvar threads-condvar nil
)
314 (defun threads-test-condvar-wait ()
315 ;; Wait for condvar to be notified.
316 (with-mutex (condition-mutex threads-condvar
)
317 (condition-wait threads-condvar
))
318 ;; Wait again, it will be signaled.
319 (with-mutex (condition-mutex threads-condvar
)
320 (condition-wait threads-condvar
)))
322 (ert-deftest threads-condvar-wait
()
323 "Test waiting on conditional variable."
324 (skip-unless (featurep 'threads
))
325 (let ((cv-mutex (make-mutex))
327 ;; We could have spurious threads from the previous tests still
328 ;; running; wait for them to die.
329 (while (> (length (all-threads)) 1)
331 (setq threads-condvar
(make-condition-variable cv-mutex
))
332 (setq new-thread
(make-thread #'threads-test-condvar-wait
))
334 ;; Make sure new-thread is alive.
335 (should (thread-alive-p new-thread
))
336 (should (= (length (all-threads)) 2))
337 ;; Wait for new-thread to become blocked on the condvar.
338 (while (not (eq (thread--blocker new-thread
) threads-condvar
))
341 ;; Notify the waiting thread.
343 (condition-notify threads-condvar t
))
344 ;; Allow new-thread to process the notification.
346 ;; Make sure the thread is still there. This used to fail due to
347 ;; a bug in thread.c:condition_wait_callback.
348 (should (thread-alive-p new-thread
))
349 (should (= (length (all-threads)) 2))
350 (should (eq (thread--blocker new-thread
) threads-condvar
))
352 ;; Signal the thread.
353 (thread-signal new-thread
'error
'("Die, die, die!"))
355 ;; Make sure the thread died.
356 (should (= (length (all-threads)) 1))
357 (should (equal (thread-last-error) '(error "Die, die, die!")))))
359 ;;; threads.el ends here