Open knorth55 opened 3 years ago
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
, but it is more complicated...
test code: https://gist.github.com/knorth55/5f0e02f8c0103323d51d04aea482f57a#file-spam-l
(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))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam1: 0
(lambda-closure spam1 32879808 0 (x) (setq *spam-func* #'spam1) (format t "spam1: ~A ~%" x) (unix:sleep 1))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=372082800 x=162d8740 addr=1f5b4c0
Fatal:
(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))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam2: 0
(lambda-closure nil 9147584 0 (xx) (spam2 xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=-584781136 x=dd24f180 addr=8b94c0
Fatal:
(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))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam3: 0
(lambda-closure spam3-test 10745024 0 (xx) (format t "spam3-test: ~A ~%" xx) (unix:sleep 1))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=1339940976 x=4fdddf40 addr=a3f4c0
Fatal:
(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))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam4: 0
(lambda-closure nil 17814720 0 (xx) (spam4-test xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=693430448 x=2954e780 addr=10fd4c0
Fatal:
(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))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam5: 0
(lambda-closure nil 31184064 0 (xx) (format t "spam5-lambda: ~A ~%" xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=2103082992 x=7d5a7ec0 addr=1dbd4c0
Fatal:
(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))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam6: 0
(lambda-closure nil 0 0 (xx) (format t "spam6-lambda: ~A ~%" xx))
spam6-lambda: 1
1.eus$
I tried with the same functions with no arguments, and it works.
function argument seems similar to let
(probably?), so I assume that closure does not work properly.
(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:
(lambda-closure name bind-frame fbind-frame &rest body)
The problem has nothing to do with funcall or apply or lambda, but is rather originated from trying to access the *spam-func*
global variable from a non-zero bind frame.
(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. I'm not familiar with the closure, but I think we can disable closure function because euslisp is object-oriented lisp. Another solution is implement the closure properly, but it will take plenty of time. (BTW, whole structure of euslisp should be closure-based language, and rewrite class as one implementation of closure, i thought)
(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.
@knorth55
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 https://github.com/Affonso-Gui/EusLisp/commit/7e3475595d93d88073dfa1270c6bfa32fb7eb241
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 solution was to use proper lisp objects (in this case cons cells) rather than c structures + integer addresses. This way the objects are allocated more carefully and the gc can do its magic to keep referenced frames alive while getting rid of old ones.
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.
CC. @708yamaguchi @pazeshun
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