;; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-;; $Id: proxy.lisp,v 1.24 2006-02-04 12:15:32 espen Exp $
+;; $Id: proxy.lisp,v 1.31 2006-02-08 22:10:47 espen Exp $
(in-package "GLIB")
(slot-definition-type slotd))))
(funcall writer (foreign-location object) value)))))))))
- (initialize-internal-slot-gfs (slot-definition-name slotd)))
+ #-sbcl>=0.9.8(initialize-internal-slot-gfs (slot-definition-name slotd)))
;;;; Proxy cache
-(internal *instance-cache*)
(defvar *instance-cache* (make-hash-table :test #'eql))
(defun cache-instance (instance &optional (weak-ref t))
*instance-cache*)
instances))
+;; Instances that gets invalidated tend to be short lived, but created
+;; in large numbers. So we're keeping them in a hash table to be able
+;; to reuse them (and thus reduce consing)
+(defvar *invalidated-instance-cache* (make-hash-table :test #'eql))
+
+(defun cache-invalidated-instance (instance)
+ (push instance
+ (gethash (class-of instance) *invalidated-instance-cache*)))
+
+(defun find-invalidated-instance (class)
+ (when (gethash class *invalidated-instance-cache*)
+ (pop (gethash class *invalidated-instance-cache*))))
+
+(defun list-invalidated-instances ()
+ (let ((instances ()))
+ (maphash #'(lambda (location ref)
+ (declare (ignore location))
+ (push ref instances))
+ *invalidated-instance-cache*)
+ instances))
+
;;;; Proxy for alien instances
+;; TODO: add a ref-counted-proxy subclass
(defclass proxy ()
- ((location :allocation :special :reader foreign-location :type pointer))
+ ((location :allocation :special :type pointer))
(:metaclass virtual-slots-class))
(defgeneric instance-finalizer (object))
(defgeneric reference-foreign (class location))
(defgeneric unreference-foreign (class location))
+(defgeneric invalidate-instance (object))
+
+(defun foreign-location (instance)
+ (slot-value instance 'location))
+
+(defun (setf foreign-location) (location instance)
+ (setf (slot-value instance 'location) location))
+
+(defun proxy-valid-p (instance)
+ (slot-boundp instance 'location))
(defmethod reference-foreign ((name symbol) location)
(reference-foreign (find-class name) location))
(format stream "at 0x~X" (sap-int (foreign-location instance)))
(write-string "at \"unbound\"" stream))))
-(defmethod initialize-instance :around ((instance proxy) &key location)
- (if location
- (setf (slot-value instance 'location) location)
- (call-next-method))
- (cache-instance instance)
- (finalize instance (instance-finalizer instance))
- instance)
+(defmethod initialize-instance :around ((instance proxy) &rest initargs)
+ (declare (ignore initargs))
+ (prog1
+ (call-next-method)
+ (cache-instance instance)
+ (finalize instance (instance-finalizer instance))))
(defmethod instance-finalizer ((instance proxy))
(let ((location (foreign-location instance))
(remove-cached-instance location)
(unreference-foreign class location))))
+;; Any reference to the foreign object the instance may have held
+;; should be released before this method is invoked
+(defmethod invalidate-instance ((instance proxy))
+ (remove-cached-instance (foreign-location instance))
+ (slot-makunbound instance 'location)
+ (cancel-finalization instance)
+ (cache-invalidated-instance instance))
+
;;;; Metaclass used for subclasses of proxy
(defgeneric most-specific-proxy-superclass (class))
(defgeneric direct-proxy-superclass (class))
-(defgeneric compute-foreign-size (class))
(eval-when (:compile-toplevel :load-toplevel :execute)
(call-next-method))
- (defmethod compute-foreign-size ((class proxy-class))
- nil)
-
;; TODO: call some C code to detect this a compile time
(defconstant +struct-alignmen+ 4)
do (setf (slot-value slotd 'offset) offset))))
(call-next-method))
- (defmethod compute-slots :after ((class proxy-class))
- (when (and (class-finalized-p class) (not (slot-boundp class 'size)))
- (let ((size (compute-foreign-size class)))
- (when size
- (setf (slot-value class 'size) size)))))
-
(defmethod validate-superclass ((class proxy-class) (super standard-class))
(subtypep (class-name super) 'proxy))
(defmethod reader-function ((class proxy-class) &rest args)
(declare (ignore args))
- #'(lambda (location &optional (offset 0))
+ #'(lambda (location &optional (offset 0) weak-p)
+ (declare (ignore weak-p))
(let ((instance (sap-ref-sap location offset)))
(unless (null-pointer-p instance)
(ensure-proxy-instance class (reference-foreign class instance))))))
(declare (ignore args))
(values t nil))
-(defgeneric ensure-proxy-instance (class location)
- (:documentation "Returns a proxy object representing the foreign object at the give location."))
-
-(defmethod ensure-proxy-instance :around (class location)
+(defun ensure-proxy-instance (class location &rest initargs)
+ "Returns a proxy object representing the foreign object at the give
+location. If an existing object is not found in the cache
+MAKE-PROXY-INSTANCE is called to create one."
(unless (null-pointer-p location)
(or
- (find-cached-instance location)
- (call-next-method))))
-
-(defmethod ensure-proxy-instance ((class symbol) location)
- (ensure-proxy-instance (find-class class) location))
-
-(defmethod ensure-proxy-instance ((class proxy-class) location)
- (make-instance class :location location))
+ #-debug-ref-counting(find-cached-instance location)
+ #+debug-ref-counting
+ (let ((instance (find-cached-instance location)))
+ (when instance
+ (format t "Object found in cache: ~A~%" instance)
+ instance))
+ (let ((instance (apply #'make-proxy-instance class location initargs)))
+ (cache-instance instance)
+ instance))))
+
+(defgeneric make-proxy-instance (class location &key weak)
+ (:documentation "Creates a new proxy object representing the foreign
+object at the give location. If WEAK is non NIL the foreign memory
+will not be released when the proxy is garbage collected."))
+
+(defmethod make-proxy-instance ((class symbol) location &rest initargs)
+ (apply #'make-proxy-instance (find-class class) location initargs))
+
+(defmethod make-proxy-instance ((class proxy-class) location &key weak)
+ (let ((instance
+ (or
+ (find-invalidated-instance class)
+ (allocate-instance class))))
+ (setf (foreign-location instance) location)
+ (unless weak
+ (finalize instance (instance-finalizer instance)))
+ instance))
;;;; Superclasses for wrapping of C structures
(defmethod unreference-foreign ((class struct-class) location)
(deallocate-memory location))
-(defmethod compute-foreign-size ((class struct-class))
- (let ((size (loop
- for slotd in (class-slots class)
- when (eq (slot-definition-allocation slotd) :alien)
- maximize (+
- (slot-definition-offset slotd)
- (size-of (slot-definition-type slotd))))))
- (+ size (mod size +struct-alignmen+))))
+(defmethod compute-slots :around ((class struct-class))
+ (let ((slots (call-next-method)))
+ (when (and
+ #-sbcl>=0.9.8(class-finalized-p class) #+sbc098 t
+ (not (slot-boundp class 'size)))
+ (let ((size (loop
+ for slotd in slots
+ when (eq (slot-definition-allocation slotd) :alien)
+ maximize (+
+ (slot-definition-offset slotd)
+ (size-of (slot-definition-type slotd))))))
+ (setf (slot-value class 'size) (+ size (mod size +struct-alignmen+)))))
+ slots))
+
+(defmethod reader-function ((class struct-class) &rest args)
+ (declare (ignore args))
+ #'(lambda (location &optional (offset 0) weak-p)
+ (let ((instance (sap-ref-sap location offset)))
+ (unless (null-pointer-p instance)
+ (if weak-p
+ (ensure-proxy-instance class instance :weak t)
+ (ensure-proxy-instance class (reference-foreign class instance)))))))
(defclass static-struct-class (struct-class)
(declare (ignore class location))
nil)
+(defmethod reader-function ((class struct-class) &rest args)
+ (declare (ignore args))
+ #'(lambda (location &optional (offset 0) weak-p)
+ (declare (ignore weak-p))
+ (let ((instance (sap-ref-sap location offset)))
+ (unless (null-pointer-p instance)
+ (ensure-proxy-instance class instance :weak t)))))
+
;;; Pseudo type for structs which are inlined in other objects
(defmethod reader-function ((type (eql 'inlined)) &rest args)
(declare (ignore type))
(destructuring-bind (class) args
- #'(lambda (location &optional (offset 0))
+ #'(lambda (location &optional (offset 0) weak-p)
+ (declare (ignore weak-p))
(ensure-proxy-instance class
(reference-foreign class (sap+ location offset))))))
+(defmethod writer-function ((type (eql 'inlined)) &rest args)
+ (declare (ignore type))
+ (destructuring-bind (class) args
+ #'(lambda (instance location &optional (offset 0))
+ (copy-memory (foreign-location instance) (foreign-size class) (sap+ location offset)))))
+
(defmethod destroy-function ((type (eql 'inlined)) &rest args)
(declare (ignore args))
#'(lambda (location &optional (offset 0))