Active object: Difference between revisions

Content added Content deleted
m (omit from assembly)
(Add Common Lisp implementation)
Line 413: Line 413:
user> (test-integrator)
user> (test-integrator)
1.414065859052494E-5
1.414065859052494E-5
</lang>

=={{header|Common Lisp}}==
{{libheader|Bordeaux Threads}}

<lang lisp>
(defclass integrator ()
((input :initarg :input :writer input :reader %input)
(lock :initform (bt:make-lock) :reader lock)
(start-time :initform (get-internal-real-time) :reader start-time)
(interval :initarg :interval :reader interval)
(thread :reader thread :writer %set-thread)
(area :reader area :initform 0 :accessor %area)))

(defmethod shared-initialize
((integrator integrator) slot-names &key (interval nil interval-s-p) &allow-other-keys)
(declare (ignore interval))
(cond
;; Restart the thread if any unsynchronized slots are
;; being initialized
((or
(eql slot-names t)
(member 'thread slot-names)
(member 'interval slot-names)
(member 'start-time slot-names)
(member 'lock slot-names)
interval-s-p)
;; If the instance already has a thread, stop it and wait for it
;; to stop before initializing any slots
(when (slot-boundp integrator 'thread)
(input nil integrator)
(bt:join-thread (thread integrator)))
(call-next-method)
(let* ((now (get-internal-real-time))
(current-value (funcall (%input integrator) (- (start-time integrator) now))))
(%set-thread
(bt:make-thread
(lambda ()
(loop
;; Sleep for the amount required to reach the next interval;
;; mitigates drift from theoretical interval times
(sleep
(mod
(/ (- (start-time integrator) (get-internal-real-time))
internal-time-units-per-second)
(interval integrator)))
(let* ((input
(bt:with-lock-held ((lock integrator))
;; If input is nil, exit the thread
(or (%input integrator) (return))))
(previous-time (shiftf now (get-internal-real-time)))
(previous-value
(shiftf
current-value
(funcall input (/ (- now (start-time integrator)) internal-time-units-per-second)))))
(bt:with-lock-held ((lock integrator))
(incf (%area integrator)
(*
(/ (- now previous-time)
internal-time-units-per-second)
(/ (+ previous-value current-value)
2)))))))
:name "integrator-thread")
integrator)))
(t
;; If lock is not in SLOT-NAMES, it must already be initialized,
;; so it can be taken while slots synchronized to it are set
(bt:with-lock-held ((lock integrator))
(call-next-method)))))

(defmethod input :around (new-value (integrator integrator))
(bt:with-lock-held ((lock integrator))
(call-next-method)))

(defmethod area :around ((integrator integrator))
(bt:with-lock-held ((lock integrator))
(call-next-method)))

(let ((integrator
(make-instance 'integrator
:input (lambda (time) (sin (* 2 pi 0.5 time)))
:interval 1/1000)))
(unwind-protect
(progn
(sleep 2)
(input (constantly 0) integrator)
(sleep 0.5)
(format t "~F~%" (area integrator)))
(input nil integrator)))
</lang>
</lang>