-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Segmentation fault occurs in funcall-ing a lambda-closure, when a function sets itself as the lambda-closure inside #463
Comments
I got this error when I try to add a new SMACH node in node callback function something like this. (defun smach-callback (userdata)
(send *sm* :add-node (instance state :init :name "new node" #'smach-callback))) |
I add test code and also tested with Apply#'spam : segmentation fault(defun spam1 (x)
(setq *spam-func* #'spam1)
(format t "spam1: ~A ~%" x)
(unix::sleep 1))
;; segmentation fault
(spam1 0)
(print *spam-func*)
(apply *spam-func* (list 1))
#'(lambda (x) (spam x)) : segmentation fault(defun spam2 (x)
(setq *spam-func* #'(lambda (xx) (spam2 xx)))
(format t "spam2: ~A ~%" x)
(unix::sleep 1))
;; segmentation fault
(spam2 0)
(print *spam-func*)
(apply *spam-func* (list 1))
#'spam-test: segmentation fault(defun spam3-test (xx)
(format t "spam3-test: ~A ~%" xx)
(unix::sleep 1))
(defun spam3 (x)
(setq *spam-func* #'spam3-test)
(format t "spam3: ~A ~%" x)
(unix::sleep 1))
;; segmentation fault
(spam3 0)
(print *spam-func*)
(apply *spam-func* (list 1))
#'(lambda (x) (spam-test x)): segmentation fault(defun spam4-test (xx)
(format t "spam3-test: ~A ~%" xx)
(unix::sleep 1))
(defun spam4 (x)
(setq *spam-func* #'(lambda (xx) (spam4-test xx)))
(format t "spam4: ~A ~%" x)
(unix::sleep 1))
;; segmentation fault
(spam4 0)
(print *spam-func*)
(apply *spam-func* (list 1))
#'(lambda (xx) (format t "spam5-lambda: ~A ~%" xx) : segmentation fault(defun spam5 (x)
(setq *spam-func*
#'(lambda (xx)
(format t "spam5-lambda: ~A ~%" xx)))
(format t "spam5: ~A ~%" x)
(unix::sleep 1))
;; segmentation fault
(spam5 0)
(print *spam-func*)
(apply *spam-func* (list 1))
`(lambda-closure nil 0 0 (xx) (format t "spam6-lambda: ~A ~%" xx)) : OK(defun spam6 (x)
(setq *spam-func*
`(lambda-closure nil 0 0 (xx)
(format t "spam6-lambda: ~A ~%" xx)))
(format t "spam6: ~A ~%" x)
(unix::sleep 1))
;; OK
(spam6 0)
(print *spam-func*)
(apply *spam-func* (list 1))
|
I tried with the same functions with no arguments, and it works. (defun noarg-spam1 ()
(setq *spam-func* #'noarg-spam1)
(format t "noarg-spam1: ~A ~%" nil)
(unix::sleep 1))
(defun noarg-spam2 ()
(setq *spam-func* #'(lambda () (noarg-spam2)))
(format t "noarg-spam2: ~A ~%" nil)
(unix::sleep 1))
(defun noarg-spam3-test (xx)
(format t "noarg-spam3-test: ~A ~%" xx)
(unix::sleep 1))
(defun noarg-spam3 ()
(setq *spam-func* #'noarg-spam3-test)
(format t "spam3: ~A ~%" nil)
(unix::sleep 1))
(defun noarg-spam4-test (xx)
(format t "noarg-spam4-test: ~A ~%" xx)
(unix::sleep 1))
(defun noarg-spam4 ()
(setq *spam-func* #'(lambda (xx) (noarg-spam4-test xx)))
(format t "noarg-spam4: ~A ~%" nil)
(unix::sleep 1))
(defun noarg-spam5 ()
(setq *spam-func*
#'(lambda (xx)
(format t "noarg-spam5-lambda: ~A ~%" xx)))
(format t "noarg-spam5: ~A ~%" nil)
(unix::sleep 1))
(defun noarg-spam6 ()
(setq *spam-func*
`(lambda-closure nil 0 0 (xx)
(format t "noarg-spam6-lambda: ~A ~%" xx)))
(format t "spam6: ~A ~%" nil)
(unix::sleep 1))
;; OK
(noarg-spam1)
(format t "*spam-func*: ~A~%" *spam-func*)
(funcall *spam-func*)
;; OK
(noarg-spam2)
(format t "*spam-func*: ~A~%" *spam-func*)
(funcall *spam-func*)
;; OK
(noarg-spam1)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* nil)
;; OK
(noarg-spam2)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* nil)
;; OK
(noarg-spam3)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* (list 1))
;; OK
(noarg-spam4)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* (list 1))
;; OK
(noarg-spam5)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* (list 1))
;; OK
(noarg-spam6)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* (list 1)) |
There are two conditions for lambda-closures to cause a segmentation fault in eus:
The problem has nothing to do with funcall or apply or lambda, but is rather originated from trying to access the (defun spam1 (x)
(if (zerop x) (setq *spam-func* #'spam1))
(format t "spam1: ~A ~%" x)
(unix::sleep 1))
;; ok
(spam1 0)
(print *spam-func*)
(funcall *spam-func* 1)
;; segmentation fault
(funcall *spam-func* 0) And yes, function arguments are similar to let and introduce new bind frames, so within functions with no arguments the bind frame is global (zero) and therefore segmentation faults does not occur. (defun foo () #'foo)
(defun bar (b) #'bar)
(foo)
;; (lambda-closure foo 0 0 nil #'foo)
(bar 1)
;; (lambda-closure bar 94419140493560 0 (b) #'bar) Segmentation also happens for fbind-frames similarly. There is no proper solution for this problem yet, but some workarounds are:
|
@Affonso-Gui thanks, it is quite unstable implementation of closure. (setq *a* 0)
(let ((a 0))
(setq *spam0-func*
#'(lambda ()
(setq a (+ a 1))
(print a))))
(let ((a 0))
(setq *spam1-func*
`(lambda-closure nil 0 0 ()
(setq a (+ a 1))
(print a))))
(let ((a 0))
(setq *spam2-func*
#'(lambda ()
(setq *a* (+ *a* 1))
(print *a*))))
; (print *spam0-func*)
; (funcall *spam0-func*)
; (print *spam1-func*)
; (funcall *spam1-func*)
; (print *spam2-func*)
; (funcall *spam2-func*) |
The core of the problem itself is not that complicated. We either need to find a way to validate pointer addresses before usage or keep them while being referenced. It is one project that has been in my list for quite some time but I was kind of reluctant to dig in the garbage collector. Maybe it's time to take a more serious try at the problem. |
So I took the time and was able to develop a proof-of-concept implementation of closures using cons cells instead of the current struct bindframe Affonso-Gui@7e34755 eus$ (let ((a 0)) (setq fn #'(lambda () (incf a))))
;; (lambda-closure nil ((a . 0)) 0 nil (incf a))
eus$ (funcall fn)
;; 1
eus$ (funcall fn)
;; 2
eus$ (funcall fn)
;; 3
eus$ (funcall fn)
;; 4
eus$ (funcall fn)
;; 5
eus$ fn
;; (lambda-closure nil ((a . 5)) 0 nil (incf a)) We probably should use some other custom class to make things a little bit more hidden from the end user, and definitely do the same for the fletframe (and maybe the specialbindframe?), but as I said this is only of a proof-on-concept implementation. The problem with the current method is that new frames are stacked on top of a vsp which is reset after each evaluation, making it impossible to keep track of bind frames simply by using their memory address. The core implementation itself is rather simple, but as you can see in the diff it is kinda tricky because I had to replace all references and all NULLs to NILs. |
when a function (
spam1
) sets itself as a lambda-closure inside(setq *spam-func* #'spam1)
,segmentation fault occurs in funcall
(funcall *spam-func* (list 1))
test code: https://gist.github.com/knorth55/5f0e02f8c0103323d51d04aea482f57a#file-spam-l
Funcall case
#'spam : segmentation fault
#'(lambda (x) (spam x)): OK
When we use
(setq *spam-func* #'(lambda (x) (spam2 x)))
, we can avoid the segmentation fault.Apply
#'spam : segmentation fault
#'(lambda (x) (spam x)) : segmentation fault
#'spam-test: segmentation fault
#'(lambda (x) (spam-test x)): segmentation fault
#'(lambda (xx) (format t "spam5-lambda: ~A ~%" xx) : segmentation fault
`(lambda-closure nil 0 0 (xx) (format t "spam6-lambda: ~A ~%" xx)) : OK
The text was updated successfully, but these errors were encountered: