diff --git a/bordeaux-threads.asd b/bordeaux-threads.asd index e31c7e5..157d338 100644 --- a/bordeaux-threads.asd +++ b/bordeaux-threads.asd @@ -49,7 +49,7 @@ Distributed under the MIT license (see LICENSE file) #+(and thread-support scl) "impl-scl") #+(and thread-support lispworks (not lispworks6)) (:file "impl-lispworks-condition-variables") - #+(and thread-support (or armedbear digitool)) + #+(and thread-support digitool) (:file "condition-variables") (:file "default-implementations")))) :in-order-to ((asdf:test-op (asdf:load-op bordeaux-threads-test))) diff --git a/src/impl-abcl.lisp b/src/impl-abcl.lisp index 8a2495e..8369950 100644 --- a/src/impl-abcl.lisp +++ b/src/impl-abcl.lisp @@ -4,6 +4,10 @@ Copyright 2006, 2007 Greg Pfeil Distributed under the MIT license (see LICENSE file) + +Implementation based on java.util.concurrent.locks.ReentrantLock by +Mark Evenson + |# (in-package #:bordeaux-threads) @@ -12,7 +16,7 @@ Distributed under the MIT license (see LICENSE file) ;;; src/org/armedbear/lisp/LispThread.java (deftype thread () - 'ext:thread) + 'threads:thread) ;;; Thread Creation @@ -30,31 +34,105 @@ Distributed under the MIT license (see LICENSE file) ;;; Resource contention: locks and recursive locks +(defstruct mutex + name + lock + recursive) + +(eval-when (:compile-toplevel :load-toplevel) + (import '(java:jnew java:jcall java:jmethod))) + (defun make-lock (&optional name) - (declare (ignore name)) - (threads:make-thread-lock)) + (make-mutex + :name (or name "Anonymous lock") + :lock (jnew "java.util.concurrent.locks.ReentrantLock") + :recursive nil)) + +;; Making methods constants in this manner avoids the time expense of +;; introspection involved in JCALL with string arguments. +(defconstant +lock+ + (jmethod "java.util.concurrent.locks.ReentrantLock" "lock")) +(defconstant +try-lock+ + (jmethod "java.util.concurrent.locks.ReentrantLock" "tryLock")) +(defconstant +is-held-by-current-thread+ + (jmethod "java.util.concurrent.locks.ReentrantLock" "isHeldByCurrentThread")) +(defconstant +unlock+ + (jmethod "java.util.concurrent.locks.ReentrantLock" "unlock")) +(defconstant +get-hold-count+ + (jmethod "java.util.concurrent.locks.ReentrantLock" "getHoldCount")) (defun acquire-lock (lock &optional (wait-p t)) - (declare (ignore wait-p)) - (threads:thread-lock lock)) + (when (and (not (mutex-recursive lock)) + (jcall +is-held-by-current-thread+ (mutex-lock lock))) + (warn "Non-recursive lock being reacquired by owner.")) + (if wait-p + (progn + (jcall +lock+ (mutex-lock lock)) + t) + (jcall +try-lock+ (mutex-lock lock)))) (defun release-lock (lock) - (threads:thread-unlock lock)) + (unless (jcall +is-held-by-current-thread+ (mutex-lock lock)) + (error "Attempt to release lock not held by calling thread.")) + (when (and (not (mutex-recursive lock)) + (> (jcall +get-hold-count+ (mutex-lock lock)) 1)) + (do () + ((= (jcall +get-hold-count+ (mutex-lock lock)) 1)) + (jcall +unlock+ (mutex-lock lock)))) + (jcall +unlock+ (mutex-lock lock)) + (values)) (defmacro with-lock-held ((place) &body body) - `(threads:with-thread-lock (,place) ,@body)) + (let ((m (gensym))) + `(let ((,m ,place)) + (when (acquire-lock ,m) + (unwind-protect + (progn + ,@body) + (release-lock ,m)))))) + +(defun make-recursive-lock (&optional name) + (make-mutex + :name (or name "Anonymous recursive lock") + :lock (jnew "java.util.concurrent.locks.ReentrantLock") + :recursive t)) + +(defun acquire-recursive-lock (lock &optional (wait-p t)) + (acquire-lock lock wait-p)) + +(defun release-recursive-lock (lock) + (release-lock lock)) + +(defmacro with-recursive-lock-held ((place) &body body) + `(with-lock-held (,place) ,@body)) ;;; Resource contention: condition variables +(defstruct condition-variable + name) + +(defun condition-wait (condition lock) + (check-type condition condition-variable) + (release-lock lock) + (do () + ((acquire-lock lock nil)) + (threads:synchronized-on condition + (threads:object-wait condition)))) + +(defun condition-notify (condition) + (check-type condition condition-variable) + (threads:synchronized-on condition + (threads:object-notify-all condition))) + (defun thread-yield () - (sleep 0)) + (sleep 0.01)) ;;; Introspection/debugging (defun all-threads () (let ((threads ())) - (threads:mapcar-threads (lambda (th) - (push th threads))) + (threads:mapcar-threads (lambda (thread) + (push thread threads))) (reverse threads))) (defun interrupt-thread (thread function &rest args)