sbcl.sbcl/tests/foreign.test.sh
Nikodemus Siivola f0da2f63aa redesign exiting SBCL
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.
2012-04-29 21:18:53 +03:00

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