mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
Deprecate QUIT. It occupies an uncomfortable niche between processes
and threads, and doesn't actually do what it says on the tin unless
you call it from the main thread.
SIGTERM now uses EXIT, and doesn't depend on sessions.
WITH-DEADLINE (:SECONDS NIL :OVERRIDE T) can now be used to ignore
deadlines.
JOIN-THREAD on the main thread now blocks indefinitely instead of
claiming the thread did not exit normally.
New functions:
* SB-EXT:EXIT. Always exits the process. Takes keywords :CODE,
:ABORT, and :TIMEOUT. Code is the exit status. Abort controls if
the exit is clean (unwind, exit-hooks, terminate other threads) or
dirty. Timeout controls how long to wait for other threads to
finish.
* SB-THREAD:RETURN-FROM-THREAD. Normal termination for current
thread -- equivalent to return from the thread function with the
specified values. Takes keyword :ALLOW-EXIT, which determines if
returning from the main thread is an error, or equivalent to
calling EXIT :CODE 0.
* SB-THREAD:ABORT-THREAD. Abnormal termination for current thread --
equivalent to invoking the initial ABORT restart estabilished by
MAKE-THREAD (previously known as TERMINATE-THREAD, but ANSI
recommends there to always be an ABORT restart.) Takes keyword
:ALLOW-EXIT, which determines if aborting the main thread is an
error, or equivalent to calling EXIT :CODE 1.
* SB-THREAD:MAIN-THREAD-P. Let's you determine if a given thread is
the main thread of the process. This is important for some
functions on some operating systems -- and RETURN-FROM-THREAD and
ABORT-THREAD also need it.
* SB-THREAD:MAIN-THREAD. Returns the main thread object. Convenient
for when you need to eg. load a foreign library in the main
thread.
386 lines
12 KiB
Bash
386 lines
12 KiB
Bash
#!/bin/sh
|
|
|
|
# tests related to foreign function interface and loading of shared
|
|
# libraries
|
|
|
|
# This software is part of the SBCL system. See the README file for
|
|
# more information.
|
|
#
|
|
# While most of SBCL is derived from the CMU CL system, the test
|
|
# files (like this one) were written from scratch after the fork
|
|
# from CMU CL.
|
|
#
|
|
# This software is in the public domain and is provided with
|
|
# absolutely no warranty. See the COPYING and CREDITS files for
|
|
# more information.
|
|
|
|
. ./expect.sh
|
|
. ./subr.sh
|
|
|
|
use_test_subdirectory
|
|
|
|
echo //entering foreign.test.sh
|
|
|
|
# simple way to make sure we're not punting by accident:
|
|
# setting PUNT to anything other than 104 will make non-dlopen
|
|
# and non-linkage-table platforms fail this
|
|
PUNT=$EXIT_TEST_WIN
|
|
|
|
## Make some shared object files to test with.
|
|
|
|
build_so() (
|
|
echo building $1.so
|
|
/bin/sh ../run-compiler.sh -sbcl-pic -sbcl-shared "$1.c" -o "$1.so"
|
|
)
|
|
|
|
# We want to bail out in case any of these Unix programs fails.
|
|
set -e
|
|
|
|
cat > $TEST_FILESTEM.c <<EOF
|
|
int summish(int x, int y) { return 1 + x + y; }
|
|
|
|
int numberish = 42;
|
|
|
|
int nummish(int x) { return numberish + x; }
|
|
|
|
short negative_short() { return -1; }
|
|
int negative_int() { return -2; }
|
|
long negative_long() { return -3; }
|
|
|
|
long long powish(unsigned int x, unsigned int y) {
|
|
long long acc = 1;
|
|
long long xx = (long long) x;
|
|
for(; y != 1; y /= 2) {
|
|
if (y & 1) {
|
|
acc *= xx;
|
|
y -= 1;
|
|
}
|
|
xx *= xx;
|
|
}
|
|
return xx*acc;
|
|
}
|
|
|
|
float return9th(float f1, float f2, float f3, float f4, float f5,
|
|
float f6, float f7, float f8, float f9, float f10,
|
|
float f11, float f12) {
|
|
return f9;
|
|
}
|
|
|
|
double return9thd(double f1, double f2, double f3, double f4, double f5,
|
|
double f6, double f7, double f8, double f9, double f10,
|
|
double f11, double f12) {
|
|
return f9;
|
|
}
|
|
|
|
int long_test8(int a1, int a2, int a3, int a4, int a5,
|
|
int a6, int a7, long long l1) {
|
|
return (l1 == powish(2,34));
|
|
}
|
|
|
|
int long_test9(int a1, int a2, int a3, int a4, int a5,
|
|
int a6, int a7, long long l1, int a8) {
|
|
return (l1 == powish(2,35));
|
|
}
|
|
|
|
int long_test2(int i1, int i2, int i3, int i4, int i5, int i6,
|
|
int i7, int i8, int i9, long long l1, long long l2) {
|
|
return (l1 == (1 + powish(2,37)));
|
|
}
|
|
|
|
int long_sap_test1(int *p1, long long l1) {
|
|
return (l1 == (3 + powish(2,*p1)));
|
|
}
|
|
|
|
int long_sap_test2(int *p1, int i1, long long l1) {
|
|
return (l1 == (3 + powish(2,*p1)));
|
|
}
|
|
|
|
long long return_long_long() {
|
|
return powish(2,33);
|
|
}
|
|
EOF
|
|
|
|
build_so $TEST_FILESTEM
|
|
|
|
echo 'int foo = 13;' > $TEST_FILESTEM-b.c
|
|
echo 'int bar() { return 42; }' >> $TEST_FILESTEM-b.c
|
|
build_so $TEST_FILESTEM-b
|
|
|
|
echo 'int foo = 42;' > $TEST_FILESTEM-b2.c
|
|
echo 'int bar() { return 13; }' >> $TEST_FILESTEM-b2.c
|
|
build_so $TEST_FILESTEM-b2
|
|
|
|
echo 'int late_foo = 43;' > $TEST_FILESTEM-c.c
|
|
echo 'int late_bar() { return 14; }' >> $TEST_FILESTEM-c.c
|
|
build_so $TEST_FILESTEM-c
|
|
|
|
## Foreign definitions & load
|
|
|
|
cat > $TEST_FILESTEM.base.lisp <<EOF
|
|
(define-alien-variable environ (* c-string))
|
|
(defvar *environ* environ)
|
|
(eval-when (:compile-toplevel :load-toplevel :execute)
|
|
(handler-case
|
|
(progn
|
|
(load-shared-object (truename "$TEST_FILESTEM.so"))
|
|
(load-shared-object (truename "$TEST_FILESTEM-b.so")))
|
|
(sb-int:unsupported-operator ()
|
|
;; At least as of sbcl-0.7.0.5, LOAD-SHARED-OBJECT isn't
|
|
;; supported on every OS. In that case, there's nothing to test,
|
|
;; and we can just fall through to success.
|
|
(sb-ext:exit :code 22)))) ; catch that
|
|
(define-alien-routine summish int (x int) (y int))
|
|
(define-alien-variable numberish int)
|
|
(define-alien-routine nummish int (x int))
|
|
(define-alien-variable "foo" int)
|
|
(define-alien-routine "bar" int)
|
|
|
|
(define-alien-routine "negative_short" short)
|
|
(define-alien-routine "negative_int" int)
|
|
(define-alien-routine "negative_long" long)
|
|
|
|
(define-alien-routine return9th float (input1 float) (input2 float) (input3 float) (input4 float) (input5 float) (input6 float) (input7 float) (input8 float) (input9 float) (input10 float) (input11 float) (input12 float))
|
|
(define-alien-routine return9thd double (f1 double) (f2 double) (f3 double) (f4 double) (f5 double) (f6 double) (f7 double) (f8 double) (f9 double) (f10 double) (f11 double) (f12 double))
|
|
|
|
(define-alien-routine long-test8 int (int1 int) (int2 int) (int3 int) (int4 int) (int5 int) (int6 int) (int7 int) (long1 (integer 64)))
|
|
(define-alien-routine long-test9 int (int1 int) (int2 int) (int3 int) (int4 int) (int5 int) (int6 int) (int7 int) (long1 (integer 64)) (int8 int))
|
|
(define-alien-routine long-test2 int (int1 int) (int2 int) (int3 int) (int4 int) (int5 int) (int6 int) (int7 int) (int8 int) (int9 int) (long1 (integer 64)) (long2 (integer 64)))
|
|
(define-alien-routine long-sap-test1 int (ptr1 int :copy) (long1 (integer 64)))
|
|
(define-alien-routine long-sap-test2 int (ptr1 int :copy) (int1 int) (long1 (integer 64)))
|
|
(define-alien-routine return-long-long (integer 64))
|
|
|
|
;; compiling this gets us the FOP-FOREIGN-DATAREF-FIXUP on
|
|
;; linkage-table ports
|
|
(defvar *extern* (extern-alien "negative_short" short))
|
|
|
|
;; Test that loading an object file didn't screw up our records
|
|
;; of variables visible in runtime. (This was a bug until
|
|
;; Nikodemus Siivola's patch in sbcl-0.8.5.50.)
|
|
;;
|
|
;; This cannot be tested in a saved core, as there is no guarantee
|
|
;; that the location will be the same.
|
|
(assert (= (sb-sys:sap-int (alien-sap *environ*))
|
|
(sb-sys:sap-int (alien-sap environ))))
|
|
|
|
;; automagic restarts
|
|
(setf *invoke-debugger-hook*
|
|
(lambda (condition hook)
|
|
(declare (ignore hook))
|
|
(princ condition)
|
|
(let ((cont (find-restart 'continue condition)))
|
|
(when cont
|
|
(invoke-restart cont)))
|
|
(print :fell-through)
|
|
(invoke-debugger condition)))
|
|
EOF
|
|
|
|
echo "(declaim (optimize speed))" > $TEST_FILESTEM.fast.lisp
|
|
cat $TEST_FILESTEM.base.lisp >> $TEST_FILESTEM.fast.lisp
|
|
|
|
echo "(declaim (optimize space))" > $TEST_FILESTEM.small.lisp
|
|
cat $TEST_FILESTEM.base.lisp >> $TEST_FILESTEM.small.lisp
|
|
|
|
# Test code
|
|
cat > $TEST_FILESTEM.test.lisp <<EOF
|
|
;; FIXME: currently the start/small case fails on x86/Darwin. Moving
|
|
;; this NOTE definition to the base.lisp file fixes that, but obviously
|
|
;; it is better fo figure out what is going on instead of doing that...
|
|
;;
|
|
;; Other trivialish changes that mask the error include:
|
|
;; * loading the .lisp file instead of the .fasl at the save test
|
|
;; * --eval 'nil' before loading the .fasl at the save test
|
|
;;
|
|
;; HATE.
|
|
(defun note (x)
|
|
(write-line x *standard-output*)
|
|
(force-output *standard-output*))
|
|
(note "/initial assertions")
|
|
(assert (= 31 (summish 10 20)))
|
|
(assert (= 42 numberish))
|
|
(setf numberish 13)
|
|
(assert (= 13 numberish))
|
|
(assert (= 14 (nummish 1)))
|
|
|
|
(assert (= -1 (negative-short)))
|
|
(assert (= -2 (negative-int)))
|
|
(assert (= -3 (negative-long)))
|
|
|
|
(assert (= 9.0s0 (return9th 1.0s0 2.0s0 3.0s0 4.0s0 5.0s0 6.0s0 7.0s0 8.0s0 9.0s0 10.0s0 11.0s0 12.0s0)))
|
|
(assert (= 9.0d0 (return9thd 1.0d0 2.0d0 3.0d0 4.0d0 5.0d0 6.0d0 7.0d0 8.0d0 9.0d0 10.0d0 11.0d0 12.0d0)))
|
|
|
|
(assert (= 1 (long-test8 1 2 3 4 5 6 7 (ash 1 34))))
|
|
(assert (= 1 (long-test9 1 2 3 4 5 6 7 (ash 1 35) 8)))
|
|
(assert (= 1 (long-test2 1 2 3 4 5 6 7 8 9 (+ 1 (ash 1 37)) 15)))
|
|
(assert (= 1 (long-sap-test1 38 (+ 3 (ash 1 38)))))
|
|
(assert (= 1 (long-sap-test2 38 1 (+ 3 (ash 1 38)))))
|
|
(assert (= (ash 1 33) (return-long-long)))
|
|
|
|
(note "/initial assertions ok")
|
|
|
|
;; test reloading object file with new definitions
|
|
(assert (= 13 foo))
|
|
(assert (= 42 (bar)))
|
|
(note "/original definitions ok")
|
|
(rename-file "$TEST_FILESTEM-b.so" "$TEST_FILESTEM-b.bak")
|
|
(rename-file "$TEST_FILESTEM-b2.so" "$TEST_FILESTEM-b.so")
|
|
(load-shared-object (truename "$TEST_FILESTEM-b.so"))
|
|
(note "/reloading ok")
|
|
(assert (= 42 foo))
|
|
(assert (= 13 (bar)))
|
|
(note "/redefined versions ok")
|
|
(rename-file "$TEST_FILESTEM-b.so" "$TEST_FILESTEM-b2.so")
|
|
(rename-file "$TEST_FILESTEM-b.bak" "$TEST_FILESTEM-b.so")
|
|
(note "/renamed back to originals")
|
|
|
|
;; test late resolution
|
|
#+linkage-table
|
|
(progn
|
|
(note "/starting linkage table tests")
|
|
(define-alien-variable late-foo int)
|
|
(define-alien-routine late-bar int)
|
|
(multiple-value-bind (val err) (ignore-errors late-foo)
|
|
(assert (not val))
|
|
(assert (typep err 'undefined-alien-error)))
|
|
(multiple-value-bind (val err) (ignore-errors (late-bar))
|
|
(assert (not val))
|
|
(assert (typep err 'undefined-alien-error)))
|
|
(load-shared-object (truename "$TEST_FILESTEM-c.so"))
|
|
(assert (= 43 late-foo))
|
|
(assert (= 14 (late-bar)))
|
|
(unload-shared-object (truename "$TEST_FILESTEM-c.so"))
|
|
(multiple-value-bind (val err) (ignore-errors late-foo)
|
|
(assert (not val))
|
|
(assert (typep err 'undefined-alien-error)))
|
|
(multiple-value-bind (val err) (ignore-errors (late-bar))
|
|
(assert (not val))
|
|
(assert (typep err 'undefined-alien-error)))
|
|
(note "/linkage table ok"))
|
|
|
|
(sb-ext:exit :code $EXIT_LISP_WIN) ; success convention for Lisp program
|
|
EOF
|
|
|
|
# Files are now set up; toggle errexit off, since we use a custom exit
|
|
# convention.
|
|
set +e
|
|
|
|
test_compile() {
|
|
run_sbcl <<EOF
|
|
(progn (load (compile-file "$TEST_FILESTEM.$1.lisp"))
|
|
(sb-ext:exit :code $EXIT_LISP_WIN))
|
|
EOF
|
|
check_status_maybe_lose "compile $1" $?
|
|
}
|
|
|
|
test_compile fast
|
|
test_compile small
|
|
|
|
test_use() {
|
|
run_sbcl --load $TEST_FILESTEM.$1.fasl --load $TEST_FILESTEM.test.lisp
|
|
check_status_maybe_lose "use $1" $? 22 "(load-shared-object not supported)"
|
|
}
|
|
|
|
test_use small
|
|
test_use fast
|
|
|
|
test_save() {
|
|
echo testing save $1
|
|
run_sbcl --load $TEST_FILESTEM.$1.fasl <<EOF
|
|
#+linkage-table (save-lisp-and-die "$TEST_FILESTEM.$1.core")
|
|
#-linkage-table nil
|
|
(sb-ext:exit :code 22) ; catch this
|
|
EOF
|
|
check_status_maybe_lose "save $1" $? \
|
|
0 "(successful save)" 22 "(linkage table not available)"
|
|
}
|
|
|
|
test_save small
|
|
test_save fast
|
|
|
|
test_start() {
|
|
echo testing start $1
|
|
run_sbcl_with_core $TEST_FILESTEM.$1.core \
|
|
--no-sysinit --no-userinit --load $TEST_FILESTEM.test.lisp
|
|
check_status_maybe_lose "start $1" $?
|
|
}
|
|
|
|
test_start fast
|
|
test_start small
|
|
|
|
# missing object file
|
|
rm $TEST_FILESTEM-b.so $TEST_FILESTEM-b2.so
|
|
run_sbcl_with_core $TEST_FILESTEM.fast.core --no-sysinit --no-userinit <<EOF
|
|
(assert (= 22 (summish 10 11)))
|
|
(multiple-value-bind (val err) (ignore-errors (eval 'foo))
|
|
(assert (not val))
|
|
(assert (typep err 'undefined-alien-error)))
|
|
(multiple-value-bind (val err) (ignore-errors (eval '(bar)))
|
|
(assert (not val))
|
|
(assert (typep err 'undefined-alien-error)))
|
|
(exit :code $EXIT_LISP_WIN)
|
|
EOF
|
|
check_status_maybe_lose "missing-so" $?
|
|
|
|
# ADDR of a heap-allocated object
|
|
cat > $TEST_FILESTEM.addr.heap.c <<EOF
|
|
struct foo
|
|
{
|
|
int x, y;
|
|
} a, *b;
|
|
EOF
|
|
|
|
build_so $TEST_FILESTEM.addr.heap
|
|
|
|
run_sbcl <<EOF
|
|
(load-shared-object (truename "$TEST_FILESTEM.addr.heap.so"))
|
|
(define-alien-type foo (struct foo (x int) (y int)))
|
|
|
|
(define-alien-variable a foo)
|
|
(define-alien-variable b (* foo))
|
|
(funcall (compile nil '(lambda () (setq b (addr a)))))
|
|
(assert (sb-sys:sap= (alien-sap a) (alien-sap (deref b))))
|
|
(exit :code $EXIT_LISP_WIN)
|
|
EOF
|
|
check_status_maybe_lose "ADDR of a heap-allocated object" $?
|
|
|
|
run_sbcl <<EOF
|
|
(define-alien-type inner (struct inner (var (unsigned 32))))
|
|
(define-alien-type outer (struct outer (one inner) (two inner)))
|
|
|
|
(defvar *outer* (make-alien outer))
|
|
(defvar *inner* (make-alien inner))
|
|
(setf (slot *inner* 'var) 20)
|
|
(setf (slot *outer* 'one) *inner*)
|
|
(assert (= (slot (slot *outer* 'one) 'var) 20))
|
|
(setf (slot *inner* 'var) 40)
|
|
(setf (slot *outer* 'two) *inner*)
|
|
(assert (= (slot (slot *outer* 'two) 'var) 40))
|
|
(exit :code $EXIT_LISP_WIN)
|
|
EOF
|
|
check_status_maybe_lose "struct offsets" $?
|
|
|
|
cat > $TEST_FILESTEM.alien.enum.lisp <<EOF
|
|
(define-alien-type foo-flag
|
|
(enum foo-flag-
|
|
(:a 1)
|
|
(:b 2)))
|
|
|
|
(define-alien-type bar
|
|
(struct bar
|
|
(foo-flag foo-flag)))
|
|
|
|
(define-alien-type barp
|
|
(* bar))
|
|
|
|
(defun foo (x)
|
|
(declare (type (alien barp) x))
|
|
x)
|
|
|
|
(defun bar (x)
|
|
(declare (type (alien barp) x))
|
|
x)
|
|
EOF
|
|
expect_clean_compile $TEST_FILESTEM.alien.enum.lisp
|
|
|
|
# success convention for script
|
|
exit $EXIT_TEST_WIN
|