unify locks
[sbcl.git] / tests / threads.pure.lisp
index 983ace8..3d6d119 100644 (file)
       (release-mutex mutex))
     (assert (not (mutex-value mutex)))))
 
-(with-test (:name spinlock-owner)
-  ;; Make sure basics are sane on unithreaded ports as well
-  (let ((spinlock (sb-thread::make-spinlock)))
-    (sb-thread::get-spinlock spinlock)
-    (assert (eq *current-thread* (sb-thread::spinlock-value spinlock)))
-    (handler-bind ((warning #'error))
-      (sb-thread::release-spinlock spinlock))
-    (assert (not (sb-thread::spinlock-value spinlock)))))
-
 ;;; Terminating a thread that's waiting for the terminal.
 
 #+sb-thread
@@ -52,7 +43,6 @@
 ;;; Condition-wait should not be interruptible under WITHOUT-INTERRUPTS
 
 (with-test (:name without-interrupts+condition-wait
-            :fails-on :sb-lutex
             :skipped-on '(not :sb-thread))
   (let* ((lock (make-mutex))
          (queue (make-waitqueue))
 ;;; wich _appear_ to be caused by malloc() and free() not being thread safe: an
 ;;; interrupted malloc in one thread can apparently block a free in another. There
 ;;; are also some indications that pthread_mutex_lock is not re-entrant.
-(with-test (:name symbol-value-in-thread.3 :skipped-on '(not :sb-thread) :broken-on :darwin)
+(with-test (:name symbol-value-in-thread.3
+            :skipped-on '(not :sb-thread)
+            :broken-on :darwin)
   (let* ((parent *current-thread*)
          (semaphore (make-semaphore))
          (running t)
                     :ok)))
               :name "T1")))
     ;; Currently we don't consider it a deadlock
-    ;; if there is a timeout in the chain. No
-    ;; Timeouts on lutex builds, though.
-    (assert (eq #-sb-lutex :deadline
-                #+sb-lutex :deadlock
+    ;; if there is a timeout in the chain.
+    (assert (eq :deadline
                 (handler-case
                     (sb-thread:with-mutex (m2)
                       (sb-thread:signal-semaphore s2)
                     :deadlock))))
     (assert (eq :ok (join-thread t1)))))
 
-(with-test (:name deadlock-detection.4  :skipped-on '(not :sb-thread))
-  (loop
-    repeat 1000
-    do (flet ((test (ma mb sa sb)
-                (lambda ()
-                  (handler-case
-                      (sb-thread::with-spinlock (ma)
-                        (sb-thread:signal-semaphore sa)
-                        (sb-thread:wait-on-semaphore sb)
-                        (sb-thread::with-spinlock (mb)
-                          :ok))
-                    (sb-thread:thread-deadlock (e)
-                      (princ e)
-                      :deadlock)))))
-         (let* ((m1 (sb-thread::make-spinlock :name "M1"))
-                (m2 (sb-thread::make-spinlock :name "M2"))
-                (s1 (sb-thread:make-semaphore :name "S1"))
-                (s2 (sb-thread:make-semaphore :name "S2"))
-                (t1 (sb-thread:make-thread (test m1 m2 s1 s2) :name "T1"))
-                (t2 (sb-thread:make-thread (test m2 m1 s2 s1) :name "T2")))
-           ;; One will deadlock, and the other will then complete normally
-           ;; ...except sometimes, when we get unlucky, and both will do
-           ;; the deadlock detection in parallel and both signal.
-           (let ((res (list (sb-thread:join-thread t1)
-                            (sb-thread:join-thread t2))))
-             (assert (or (equal '(:deadlock :ok) res)
-                         (equal '(:ok :deadlock) res)
-                         (equal '(:deadlock :deadlock) res))))))))
+#+sb-thread
+(with-test (:name :pass-arguments-to-thread)
+  (assert (= 3 (join-thread (make-thread #'+ :arguments '(1 2))))))
 
-(with-test (:name deadlock-detection.5 :skipped-on '(not :sb-thread))
-  (let* ((m1 (sb-thread::make-spinlock :name "M1"))
-         (m2 (sb-thread::make-spinlock :name "M2"))
-         (s1 (sb-thread:make-semaphore :name "S1"))
-         (s2 (sb-thread:make-semaphore :name "S2"))
-         (t1 (sb-thread:make-thread
-              (lambda ()
-                (sb-thread::with-spinlock (m1)
-                  (sb-thread:signal-semaphore s1)
-                  (sb-thread:wait-on-semaphore s2)
-                  (sb-thread::with-spinlock (m2)
-                    :ok)))
-              :name "T1")))
-    (prog (err)
-     :retry
-       (handler-bind ((sb-thread:thread-deadlock
-                       (lambda (e)
-                         (unless err
-                           ;; Make sure we can print the condition
-                           ;; while it's active
-                           (let ((*print-circle* nil))
-                             (setf err (princ-to-string e)))
-                           (go :retry)))))
-         (when err
-           (sleep 1))
-         (assert (eq :ok (sb-thread::with-spinlock (m2)
-                           (unless err
-                             (sb-thread:signal-semaphore s2)
-                             (sb-thread:wait-on-semaphore s1)
-                             (sleep 1))
-                           (sb-thread::with-spinlock (m1)
-                             :ok)))))
-       (assert (stringp err)))
-    (assert (eq :ok (sb-thread:join-thread t1)))))
+#+sb-thread
+(with-test (:name :pass-atom-to-thread)
+  (assert (= 1/2 (join-thread (make-thread #'/ :arguments 2)))))
 
-(with-test (:name deadlock-detection.7 :skipped-on '(not :sb-thread))
-  (let* ((m1 (sb-thread::make-spinlock :name "M1"))
-         (m2 (sb-thread::make-spinlock :name "M2"))
-         (s1 (sb-thread:make-semaphore :name "S1"))
-         (s2 (sb-thread:make-semaphore :name "S2"))
-         (t1 (sb-thread:make-thread
-              (lambda ()
-                (sb-thread::with-spinlock (m1)
-                  (sb-thread:signal-semaphore s1)
-                  (sb-thread:wait-on-semaphore s2)
-                  (sb-thread::with-spinlock (m2)
-                    :ok)))
-              :name "T1")))
-    (assert (eq :deadlock
-                (handler-case
-                    (sb-thread::with-spinlock (m2)
-                      (sb-thread:signal-semaphore s2)
-                      (sb-thread:wait-on-semaphore s1)
-                      (sleep 1)
-                      (sb-sys:with-deadline (:seconds 0.1)
-                        (sb-thread::with-spinlock (m1)
-                          :ok)))
-                  (sb-sys:deadline-timeout ()
-                    :deadline)
-                  (sb-thread:thread-deadlock ()
-                    :deadlock))))
-    (assert (eq :ok (join-thread t1)))))
+#+sb-thread
+(with-test (:name :pass-nil-to-thread)
+  (assert (= 1 (join-thread (make-thread #'* :arguments '())))))
+
+#+sb-thread
+(with-test (:name :pass-nothing-to-thread)
+  (assert (= 1 (join-thread (make-thread #'*)))))
+
+#+sb-thread
+(with-test (:name :pass-improper-list-to-thread)
+  (multiple-value-bind (value error)
+      (ignore-errors (make-thread #'+ :arguments '(1 . 1)))
+    (when value
+      (join-thread value))
+    (assert (and (null value)
+                 error))))
+
+(with-test (:name (:wait-for :basics))
+  (assert (not (sb-ext:wait-for nil :timeout 0.1)))
+  (assert (eql 42 (sb-ext:wait-for 42)))
+  (let ((n 0))
+    (assert (eql 100 (sb-ext:wait-for (when (= 100 (incf n))
+                                        n))))))
+
+(with-test (:name (:wait-for :deadline))
+  (assert (eq :ok
+              (sb-sys:with-deadline (:seconds 10)
+                (assert (not (sb-ext:wait-for nil :timeout 0.1)))
+                :ok)))
+  (assert (eq :deadline
+              (handler-case
+                  (sb-sys:with-deadline (:seconds 0.1)
+                    (sb-ext:wait-for nil :timeout 10)
+                    (error "oops"))
+                (sb-sys:deadline-timeout () :deadline)))))
+
+(with-test (:name (:condition-wait :timeout :one-thread))
+  (let ((mutex (make-mutex))
+        (waitqueue (make-waitqueue)))
+    (assert (not (with-mutex (mutex)
+                   (condition-wait waitqueue mutex :timeout 0.01))))))
+
+(with-test (:name (:condition-wait :timeout :many-threads)
+            :skipped-on '(not :sb-thread))
+  (let* ((mutex (make-mutex))
+         (waitqueue (make-waitqueue))
+         (sem (make-semaphore))
+         (data nil)
+         (workers
+           (loop repeat 100
+                 collect (make-thread
+                          (lambda ()
+                            (wait-on-semaphore sem)
+                            (block thread
+                              (with-mutex (mutex)
+                                (loop until data
+                                      do (or (condition-wait waitqueue mutex :timeout 0.01)
+                                             (return-from thread nil)))
+                                (assert (eq t (pop data)))
+                                t)))))))
+    (loop repeat 50
+          do (with-mutex (mutex)
+               (push t data)
+               (condition-notify waitqueue)))
+    (signal-semaphore sem 100)
+    (let ((ok (count-if #'join-thread workers)))
+      (unless (eql 50 ok)
+        (error "Wanted 50, got ~S" ok)))))
+
+(with-test (:name (:wait-on-semaphore :timeout :one-thread))
+  (let ((sem (make-semaphore))
+        (n 0))
+    (signal-semaphore sem 10)
+    (loop repeat 100
+          do (when (wait-on-semaphore sem :timeout 0.001)
+               (incf n)))
+    (assert (= n 10))))
+
+(with-test (:name (:wait-on-semaphore :timeout :many-threads)
+            :skipped-on '(not :sb-thread))
+  (let* ((sem (make-semaphore))
+         (threads
+           (progn
+             (signal-semaphore sem 10)
+             (loop repeat 100
+                   collect (make-thread
+                            (lambda ()
+                              (sleep (random 0.02))
+                              (wait-on-semaphore sem :timeout 0.01)))))))
+    (loop repeat 5
+          do (signal-semaphore sem 2))
+    (let ((ok (count-if #'join-thread threads)))
+      (unless (eql 20 ok)
+        (error "Wanted 20, got ~S" ok)))))
+
+(with-test (:name (:join-thread :timeout)
+            :skipped-on '(not :sb-thread))
+  (assert (eq :error
+              (handler-case
+                  (join-thread (make-thread (lambda () (sleep 10))) :timeout 0.01)
+                (join-thread-error ()
+                  :error))))
+  (let ((cookie (cons t t)))
+    (assert (eq cookie
+                (join-thread (make-thread (lambda () (sleep 10)))
+                             :timeout 0.01
+                             :default cookie)))))