summaryrefslogtreecommitdiff
path: root/testing/embedding
diff options
context:
space:
mode:
authorKevin Cheng <kevcheng@google.com>2019-04-18 11:31:16 -0700
committerKevin Cheng <kevcheng@google.com>2019-05-02 13:59:40 -0700
commit757c264bc10ebc71074ee3f5fb66d670667a09bc (patch)
tree26c7f7b74c752db99d9b0ac1f94fc592aca1e53a /testing/embedding
parent99013222844839c42437f16eace25f4e6a8a8b20 (diff)
downloadcffi-757c264bc10ebc71074ee3f5fb66d670667a09bc.tar.gz
Add in cffi 1.12.2 (e0c7666)
Since this is a mercurial repo, d/led zip of src: https://bitbucket.org/cffi/cffi/get/v1.12.2.zip Also add in misc METADATA/NOTICE/Android.bp/etc files. Bug: 122778810 Test: None Change-Id: I36c58ed07a2cdd4d9d11831908175a5c988f33c1
Diffstat (limited to 'testing/embedding')
-rw-r--r--testing/embedding/__init__.py0
-rw-r--r--testing/embedding/add1-test.c21
-rw-r--r--testing/embedding/add1.py33
-rw-r--r--testing/embedding/add2-test.c14
-rw-r--r--testing/embedding/add2.py29
-rw-r--r--testing/embedding/add3.py24
-rw-r--r--testing/embedding/add_recursive-test.c27
-rw-r--r--testing/embedding/add_recursive.py33
-rw-r--r--testing/embedding/empty.py10
-rw-r--r--testing/embedding/initerror.py18
-rw-r--r--testing/embedding/perf-test.c90
-rw-r--r--testing/embedding/perf.py21
-rw-r--r--testing/embedding/test_basic.py207
-rw-r--r--testing/embedding/test_performance.py52
-rw-r--r--testing/embedding/test_recursive.py15
-rw-r--r--testing/embedding/test_thread.py61
-rw-r--r--testing/embedding/test_tlocal.py10
-rw-r--r--testing/embedding/thread-test.h96
-rw-r--r--testing/embedding/thread1-test.c43
-rw-r--r--testing/embedding/thread2-test.c57
-rw-r--r--testing/embedding/thread3-test.c56
-rw-r--r--testing/embedding/tlocal-test.c47
-rw-r--r--testing/embedding/tlocal.py33
23 files changed, 997 insertions, 0 deletions
diff --git a/testing/embedding/__init__.py b/testing/embedding/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/embedding/__init__.py
diff --git a/testing/embedding/add1-test.c b/testing/embedding/add1-test.c
new file mode 100644
index 0000000..b9ede18
--- /dev/null
+++ b/testing/embedding/add1-test.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#include <windows.h>
+#endif
+
+extern int add1(int, int);
+
+
+int main(void)
+{
+ int x, y;
+ x = add1(40, 2);
+ y = add1(100, -5);
+ printf("got: %d %d\n", x, y);
+#ifdef _MSC_VER
+ if (x == 0 && y == 0)
+ Sleep(2000);
+#endif
+ return 0;
+}
diff --git a/testing/embedding/add1.py b/testing/embedding/add1.py
new file mode 100644
index 0000000..e5b3de1
--- /dev/null
+++ b/testing/embedding/add1.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ import sys, time
+ sys.stdout.write("preparing")
+ for i in range(3):
+ sys.stdout.flush()
+ time.sleep(0.2)
+ sys.stdout.write(".")
+ sys.stdout.write("\n")
+
+ from _add1_cffi import ffi
+
+ int(ord("A")) # check that built-ins are there
+
+ @ffi.def_extern()
+ def add1(x, y):
+ sys.stdout.write("adding %d and %d\n" % (x, y))
+ sys.stdout.flush()
+ return x + y
+""")
+
+ffi.set_source("_add1_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add2-test.c b/testing/embedding/add2-test.c
new file mode 100644
index 0000000..9620843
--- /dev/null
+++ b/testing/embedding/add2-test.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+extern int add1(int, int);
+extern int add2(int, int, int);
+
+
+int main(void)
+{
+ int x, y;
+ x = add1(40, 2);
+ y = add2(100, -5, -20);
+ printf("got: %d %d\n", x, y);
+ return 0;
+}
diff --git a/testing/embedding/add2.py b/testing/embedding/add2.py
new file mode 100644
index 0000000..311a464
--- /dev/null
+++ b/testing/embedding/add2.py
@@ -0,0 +1,29 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add2(int, int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ import sys
+ sys.stdout.write("prepADD2\n")
+
+ assert '_add2_cffi' in sys.modules
+ m = sys.modules['_add2_cffi']
+ import _add2_cffi
+ ffi = _add2_cffi.ffi
+
+ @ffi.def_extern()
+ def add2(x, y, z):
+ sys.stdout.write("adding %d and %d and %d\n" % (x, y, z))
+ sys.stdout.flush()
+ return x + y + z
+""")
+
+ffi.set_source("_add2_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add3.py b/testing/embedding/add3.py
new file mode 100644
index 0000000..1361912
--- /dev/null
+++ b/testing/embedding/add3.py
@@ -0,0 +1,24 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add3(int, int, int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ from _add3_cffi import ffi
+ import sys
+
+ @ffi.def_extern()
+ def add3(x, y, z, t):
+ sys.stdout.write("adding %d, %d, %d, %d\n" % (x, y, z, t))
+ sys.stdout.flush()
+ return x + y + z + t
+""")
+
+ffi.set_source("_add3_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add_recursive-test.c b/testing/embedding/add_recursive-test.c
new file mode 100644
index 0000000..cd29b79
--- /dev/null
+++ b/testing/embedding/add_recursive-test.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+
+#ifdef _MSC_VER
+# define DLLIMPORT __declspec(dllimport)
+#else
+# define DLLIMPORT extern
+#endif
+
+DLLIMPORT int add_rec(int, int);
+DLLIMPORT int (*my_callback)(int);
+
+static int some_callback(int x)
+{
+ printf("some_callback(%d)\n", x);
+ fflush(stdout);
+ return add_rec(x, 9);
+}
+
+int main(void)
+{
+ int x, y;
+ my_callback = some_callback;
+ x = add_rec(40, 2);
+ y = add_rec(100, -5);
+ printf("got: %d %d\n", x, y);
+ return 0;
+}
diff --git a/testing/embedding/add_recursive.py b/testing/embedding/add_recursive.py
new file mode 100644
index 0000000..9fa463d
--- /dev/null
+++ b/testing/embedding/add_recursive.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int (*my_callback)(int);
+ int add_rec(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ from _add_recursive_cffi import ffi, lib
+ import sys
+ print("preparing REC")
+ sys.stdout.flush()
+
+ @ffi.def_extern()
+ def add_rec(x, y):
+ print("adding %d and %d" % (x, y))
+ sys.stdout.flush()
+ return x + y
+
+ x = lib.my_callback(400)
+ print('<<< %d >>>' % (x,))
+""")
+
+ffi.set_source("_add_recursive_cffi", """
+/* use CFFI_DLLEXPORT: on windows, it expands to __declspec(dllexport),
+ which is needed to export a variable from a dll */
+CFFI_DLLEXPORT int (*my_callback)(int);
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/empty.py b/testing/embedding/empty.py
new file mode 100644
index 0000000..aa8d830
--- /dev/null
+++ b/testing/embedding/empty.py
@@ -0,0 +1,10 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("")
+
+ffi.set_source("_empty_cffi", "")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/initerror.py b/testing/embedding/initerror.py
new file mode 100644
index 0000000..775cf56
--- /dev/null
+++ b/testing/embedding/initerror.py
@@ -0,0 +1,18 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ raise KeyError
+""")
+
+ffi.set_source("_initerror_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
+
diff --git a/testing/embedding/perf-test.c b/testing/embedding/perf-test.c
new file mode 100644
index 0000000..2195bf5
--- /dev/null
+++ b/testing/embedding/perf-test.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <assert.h>
+#include <sys/time.h>
+#ifdef PTEST_USE_THREAD
+# include <pthread.h>
+static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
+static int remaining;
+#endif
+
+
+extern int add1(int, int);
+
+
+static double time_delta(struct timeval *stop, struct timeval *start)
+{
+ return (stop->tv_sec - start->tv_sec) +
+ 1e-6 * (stop->tv_usec - start->tv_usec);
+}
+
+static double measure(void)
+{
+ long long i, iterations;
+ int result;
+ struct timeval start, stop;
+ double elapsed;
+
+ add1(0, 0); /* prepare off-line */
+
+ i = 0;
+ iterations = 1000;
+ result = gettimeofday(&start, NULL);
+ assert(result == 0);
+
+ while (1) {
+ for (; i < iterations; i++) {
+ add1(((int)i) & 0xaaaaaa, ((int)i) & 0x555555);
+ }
+ result = gettimeofday(&stop, NULL);
+ assert(result == 0);
+
+ elapsed = time_delta(&stop, &start);
+ assert(elapsed >= 0.0);
+ if (elapsed > 2.5)
+ break;
+ iterations = iterations * 3 / 2;
+ }
+
+ return elapsed / (double)iterations;
+}
+
+static void *start_routine(void *arg)
+{
+ double t = measure();
+ printf("time per call: %.3g\n", t);
+
+#ifdef PTEST_USE_THREAD
+ pthread_mutex_lock(&mutex1);
+ remaining -= 1;
+ if (!remaining)
+ pthread_cond_signal(&cond1);
+ pthread_mutex_unlock(&mutex1);
+#endif
+
+ return arg;
+}
+
+
+int main(void)
+{
+#ifndef PTEST_USE_THREAD
+ start_routine(0);
+#else
+ pthread_t th;
+ int i, status;
+
+ add1(0, 0); /* this is the main thread */
+
+ remaining = PTEST_USE_THREAD;
+ for (i = 0; i < PTEST_USE_THREAD; i++) {
+ status = pthread_create(&th, NULL, start_routine, NULL);
+ assert(status == 0);
+ }
+ pthread_mutex_lock(&mutex1);
+ while (remaining)
+ pthread_cond_wait(&cond1, &mutex1);
+ pthread_mutex_unlock(&mutex1);
+#endif
+ return 0;
+}
diff --git a/testing/embedding/perf.py b/testing/embedding/perf.py
new file mode 100644
index 0000000..a8d20f4
--- /dev/null
+++ b/testing/embedding/perf.py
@@ -0,0 +1,21 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ from _perf_cffi import ffi
+
+ @ffi.def_extern()
+ def add1(x, y):
+ return x + y
+""")
+
+ffi.set_source("_perf_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py
new file mode 100644
index 0000000..8463c3f
--- /dev/null
+++ b/testing/embedding/test_basic.py
@@ -0,0 +1,207 @@
+import py
+import sys, os, re
+import shutil, subprocess, time
+from testing.udir import udir
+import cffi
+
+
+local_dir = os.path.dirname(os.path.abspath(__file__))
+_link_error = '?'
+
+def check_lib_python_found(tmpdir):
+ global _link_error
+ if _link_error == '?':
+ ffi = cffi.FFI()
+ kwds = {}
+ ffi._apply_embedding_fix(kwds)
+ ffi.set_source("_test_lib_python_found", "", **kwds)
+ try:
+ ffi.compile(tmpdir=tmpdir, verbose=True)
+ except cffi.VerificationError as e:
+ _link_error = e
+ else:
+ _link_error = None
+ if _link_error:
+ py.test.skip(str(_link_error))
+
+
+def prefix_pythonpath():
+ cffi_base = os.path.dirname(os.path.dirname(local_dir))
+ pythonpath = org_env.get('PYTHONPATH', '').split(os.pathsep)
+ if cffi_base not in pythonpath:
+ pythonpath.insert(0, cffi_base)
+ return os.pathsep.join(pythonpath)
+
+def copy_away_env():
+ global org_env
+ try:
+ org_env
+ except NameError:
+ org_env = os.environ.copy()
+
+
+class EmbeddingTests:
+ _compiled_modules = {}
+
+ def setup_method(self, meth):
+ check_lib_python_found(str(udir.ensure('embedding', dir=1)))
+ self._path = udir.join('embedding', meth.__name__)
+ if sys.platform == "win32" or sys.platform == "darwin":
+ self._compiled_modules.clear() # workaround
+
+ def get_path(self):
+ return str(self._path.ensure(dir=1))
+
+ def _run_base(self, args, **kwds):
+ print('RUNNING:', args, kwds)
+ return subprocess.Popen(args, **kwds)
+
+ def _run(self, args):
+ popen = self._run_base(args, cwd=self.get_path(),
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+ output = popen.stdout.read()
+ err = popen.wait()
+ if err:
+ raise OSError("popen failed with exit code %r: %r" % (
+ err, args))
+ print(output.rstrip())
+ return output
+
+ def prepare_module(self, name):
+ self.patch_environment()
+ if name not in self._compiled_modules:
+ path = self.get_path()
+ filename = '%s.py' % name
+ # NOTE: if you have an .egg globally installed with an older
+ # version of cffi, this will not work, because sys.path ends
+ # up with the .egg before the PYTHONPATH entries. I didn't
+ # find a solution to that: we could hack sys.path inside the
+ # script run here, but we can't hack it in the same way in
+ # execute().
+ pathname = os.path.join(path, filename)
+ with open(pathname, 'w') as g:
+ g.write('''
+# https://bugs.python.org/issue23246
+import sys
+if sys.platform == 'win32':
+ try:
+ import setuptools
+ except ImportError:
+ pass
+''')
+ with open(os.path.join(local_dir, filename), 'r') as f:
+ g.write(f.read())
+
+ output = self._run([sys.executable, pathname])
+ match = re.compile(r"\bFILENAME: (.+)").search(output)
+ assert match
+ dynamic_lib_name = match.group(1)
+ if sys.platform == 'win32':
+ assert dynamic_lib_name.endswith('_cffi.dll')
+ elif sys.platform == 'darwin':
+ assert dynamic_lib_name.endswith('_cffi.dylib')
+ else:
+ assert dynamic_lib_name.endswith('_cffi.so')
+ self._compiled_modules[name] = dynamic_lib_name
+ return self._compiled_modules[name]
+
+ def compile(self, name, modules, opt=False, threads=False, defines={}):
+ path = self.get_path()
+ filename = '%s.c' % name
+ shutil.copy(os.path.join(local_dir, filename), path)
+ shutil.copy(os.path.join(local_dir, 'thread-test.h'), path)
+ import distutils.ccompiler
+ curdir = os.getcwd()
+ try:
+ os.chdir(self.get_path())
+ c = distutils.ccompiler.new_compiler()
+ print('compiling %s with %r' % (name, modules))
+ extra_preargs = []
+ debug = True
+ if sys.platform == 'win32':
+ libfiles = []
+ for m in modules:
+ m = os.path.basename(m)
+ assert m.endswith('.dll')
+ libfiles.append('Release\\%s.lib' % m[:-4])
+ modules = libfiles
+ extra_preargs.append('/MANIFEST')
+ debug = False # you need to install extra stuff
+ # for this to work
+ elif threads:
+ extra_preargs.append('-pthread')
+ objects = c.compile([filename], macros=sorted(defines.items()),
+ debug=debug)
+ c.link_executable(objects + modules, name, extra_preargs=extra_preargs)
+ finally:
+ os.chdir(curdir)
+
+ def patch_environment(self):
+ copy_away_env()
+ path = self.get_path()
+ # for libpypy-c.dll or Python27.dll
+ path = os.path.split(sys.executable)[0] + os.path.pathsep + path
+ env_extra = {'PYTHONPATH': prefix_pythonpath()}
+ if sys.platform == 'win32':
+ envname = 'PATH'
+ else:
+ envname = 'LD_LIBRARY_PATH'
+ libpath = org_env.get(envname)
+ if libpath:
+ libpath = path + os.path.pathsep + libpath
+ else:
+ libpath = path
+ env_extra[envname] = libpath
+ for key, value in sorted(env_extra.items()):
+ if os.environ.get(key) != value:
+ print('* setting env var %r to %r' % (key, value))
+ os.environ[key] = value
+
+ def execute(self, name):
+ path = self.get_path()
+ print('running %r in %r' % (name, path))
+ executable_name = name
+ if sys.platform == 'win32':
+ executable_name = os.path.join(path, executable_name + '.exe')
+ else:
+ executable_name = os.path.join('.', executable_name)
+ popen = self._run_base([executable_name], cwd=path,
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+ result = popen.stdout.read()
+ err = popen.wait()
+ if err:
+ raise OSError("%r failed with exit code %r" % (name, err))
+ return result
+
+
+class TestBasic(EmbeddingTests):
+ def test_empty(self):
+ empty_cffi = self.prepare_module('empty')
+
+ def test_basic(self):
+ add1_cffi = self.prepare_module('add1')
+ self.compile('add1-test', [add1_cffi])
+ output = self.execute('add1-test')
+ assert output == ("preparing...\n"
+ "adding 40 and 2\n"
+ "adding 100 and -5\n"
+ "got: 42 95\n")
+
+ def test_two_modules(self):
+ add1_cffi = self.prepare_module('add1')
+ add2_cffi = self.prepare_module('add2')
+ self.compile('add2-test', [add1_cffi, add2_cffi])
+ output = self.execute('add2-test')
+ assert output == ("preparing...\n"
+ "adding 40 and 2\n"
+ "prepADD2\n"
+ "adding 100 and -5 and -20\n"
+ "got: 42 75\n")
+
+ def test_init_time_error(self):
+ initerror_cffi = self.prepare_module('initerror')
+ self.compile('add1-test', [initerror_cffi])
+ output = self.execute('add1-test')
+ assert output == "got: 0 0\n" # plus lots of info to stderr
diff --git a/testing/embedding/test_performance.py b/testing/embedding/test_performance.py
new file mode 100644
index 0000000..f9f2605
--- /dev/null
+++ b/testing/embedding/test_performance.py
@@ -0,0 +1,52 @@
+import sys
+from testing.embedding.test_basic import EmbeddingTests
+
+if sys.platform == 'win32':
+ import py
+ py.test.skip("written with POSIX functions")
+
+
+class TestPerformance(EmbeddingTests):
+ def test_perf_single_threaded(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True)
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
+
+ def test_perf_in_1_thread(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+ defines={'PTEST_USE_THREAD': '1'})
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
+
+ def test_perf_in_2_threads(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+ defines={'PTEST_USE_THREAD': '2'})
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
+
+ def test_perf_in_4_threads(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+ defines={'PTEST_USE_THREAD': '4'})
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
+
+ def test_perf_in_8_threads(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+ defines={'PTEST_USE_THREAD': '8'})
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
diff --git a/testing/embedding/test_recursive.py b/testing/embedding/test_recursive.py
new file mode 100644
index 0000000..b85e7ed
--- /dev/null
+++ b/testing/embedding/test_recursive.py
@@ -0,0 +1,15 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestRecursive(EmbeddingTests):
+ def test_recursive(self):
+ add_recursive_cffi = self.prepare_module('add_recursive')
+ self.compile('add_recursive-test', [add_recursive_cffi])
+ output = self.execute('add_recursive-test')
+ assert output == ("preparing REC\n"
+ "some_callback(400)\n"
+ "adding 400 and 9\n"
+ "<<< 409 >>>\n"
+ "adding 40 and 2\n"
+ "adding 100 and -5\n"
+ "got: 42 95\n")
diff --git a/testing/embedding/test_thread.py b/testing/embedding/test_thread.py
new file mode 100644
index 0000000..1895076
--- /dev/null
+++ b/testing/embedding/test_thread.py
@@ -0,0 +1,61 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestThread(EmbeddingTests):
+ def test_first_calls_in_parallel(self):
+ add1_cffi = self.prepare_module('add1')
+ self.compile('thread1-test', [add1_cffi], threads=True)
+ for i in range(20):
+ output = self.execute('thread1-test')
+ assert output == ("starting\n"
+ "preparing...\n" +
+ "adding 40 and 2\n" * 10 +
+ "done\n")
+
+ def _take_out(self, text, content):
+ assert content in text
+ i = text.index(content)
+ return text[:i] + text[i+len(content):]
+
+ def test_init_different_modules_in_different_threads(self):
+ add1_cffi = self.prepare_module('add1')
+ add2_cffi = self.prepare_module('add2')
+ self.compile('thread2-test', [add1_cffi, add2_cffi], threads=True)
+ output = self.execute('thread2-test')
+ output = self._take_out(output, "preparing")
+ output = self._take_out(output, ".")
+ output = self._take_out(output, ".")
+ # at least the 3rd dot should be after everything from ADD2
+ assert output == ("starting\n"
+ "prepADD2\n"
+ "adding 1000 and 200 and 30\n"
+ ".\n"
+ "adding 40 and 2\n"
+ "done\n")
+
+ def test_alt_issue(self):
+ add1_cffi = self.prepare_module('add1')
+ add2_cffi = self.prepare_module('add2')
+ self.compile('thread2-test', [add1_cffi, add2_cffi],
+ threads=True, defines={'T2TEST_AGAIN_ADD1': '1'})
+ output = self.execute('thread2-test')
+ output = self._take_out(output, "adding 40 and 2\n")
+ assert output == ("starting\n"
+ "preparing...\n"
+ "adding -1 and -1\n"
+ "prepADD2\n"
+ "adding 1000 and 200 and 30\n"
+ "done\n")
+
+ def test_load_in_parallel_more(self):
+ add2_cffi = self.prepare_module('add2')
+ add3_cffi = self.prepare_module('add3')
+ self.compile('thread3-test', [add2_cffi, add3_cffi], threads=True)
+ for i in range(150):
+ output = self.execute('thread3-test')
+ for j in range(10):
+ output = self._take_out(output, "adding 40 and 2 and 100\n")
+ output = self._take_out(output, "adding 1000, 200, 30, 4\n")
+ assert output == ("starting\n"
+ "prepADD2\n"
+ "done\n")
diff --git a/testing/embedding/test_tlocal.py b/testing/embedding/test_tlocal.py
new file mode 100644
index 0000000..6e7c5af
--- /dev/null
+++ b/testing/embedding/test_tlocal.py
@@ -0,0 +1,10 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestThreadLocal(EmbeddingTests):
+ def test_thread_local(self):
+ tlocal_cffi = self.prepare_module('tlocal')
+ self.compile('tlocal-test', [tlocal_cffi], threads=True)
+ for i in range(10):
+ output = self.execute('tlocal-test')
+ assert output == "done\n"
diff --git a/testing/embedding/thread-test.h b/testing/embedding/thread-test.h
new file mode 100644
index 0000000..f66cf70
--- /dev/null
+++ b/testing/embedding/thread-test.h
@@ -0,0 +1,96 @@
+/************************************************************/
+#ifndef _MSC_VER
+/************************************************************/
+
+
+#include <pthread.h>
+
+/* don't include <semaphore.h>, it is not available on OS/X */
+
+typedef struct {
+ pthread_mutex_t mutex1;
+ pthread_cond_t cond1;
+ unsigned int value;
+} sem_t;
+
+static int sem_init(sem_t *sem, int pshared, unsigned int value)
+{
+ assert(pshared == 0);
+ sem->value = value;
+ return (pthread_mutex_init(&sem->mutex1, NULL) ||
+ pthread_cond_init(&sem->cond1, NULL));
+}
+
+static int sem_post(sem_t *sem)
+{
+ pthread_mutex_lock(&sem->mutex1);
+ sem->value += 1;
+ pthread_cond_signal(&sem->cond1);
+ pthread_mutex_unlock(&sem->mutex1);
+ return 0;
+}
+
+static int sem_wait(sem_t *sem)
+{
+ pthread_mutex_lock(&sem->mutex1);
+ while (sem->value == 0)
+ pthread_cond_wait(&sem->cond1, &sem->mutex1);
+ sem->value -= 1;
+ pthread_mutex_unlock(&sem->mutex1);
+ return 0;
+}
+
+
+/************************************************************/
+#else
+/************************************************************/
+
+
+/* Very quick and dirty, just what I need for these tests.
+ Don't use directly in any real code!
+*/
+
+#include <Windows.h>
+#include <assert.h>
+
+typedef HANDLE sem_t;
+typedef HANDLE pthread_t;
+
+static int sem_init(sem_t *sem, int pshared, unsigned int value)
+{
+ assert(pshared == 0);
+ assert(value == 0);
+ *sem = CreateSemaphore(NULL, 0, 999, NULL);
+ return *sem ? 0 : -1;
+}
+
+static int sem_post(sem_t *sem)
+{
+ return ReleaseSemaphore(*sem, 1, NULL) ? 0 : -1;
+}
+
+static int sem_wait(sem_t *sem)
+{
+ WaitForSingleObject(*sem, INFINITE);
+ return 0;
+}
+
+static DWORD WINAPI myThreadProc(LPVOID lpParameter)
+{
+ void *(* start_routine)(void *) = (void *(*)(void *))lpParameter;
+ start_routine(NULL);
+ return 0;
+}
+
+static int pthread_create(pthread_t *thread, void *attr,
+ void *start_routine(void *), void *arg)
+{
+ assert(arg == NULL);
+ *thread = CreateThread(NULL, 0, myThreadProc, start_routine, 0, NULL);
+ return *thread ? 0 : -1;
+}
+
+
+/************************************************************/
+#endif
+/************************************************************/
diff --git a/testing/embedding/thread1-test.c b/testing/embedding/thread1-test.c
new file mode 100644
index 0000000..70bb861
--- /dev/null
+++ b/testing/embedding/thread1-test.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+#define NTHREADS 10
+
+
+extern int add1(int, int);
+
+static sem_t done;
+
+
+static void *start_routine(void *arg)
+{
+ int x, status;
+ x = add1(40, 2);
+ assert(x == 42);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+int main(void)
+{
+ pthread_t th;
+ int i, status = sem_init(&done, 0, 0);
+ assert(status == 0);
+
+ printf("starting\n");
+ fflush(stdout);
+ for (i = 0; i < NTHREADS; i++) {
+ status = pthread_create(&th, NULL, start_routine, NULL);
+ assert(status == 0);
+ }
+ for (i = 0; i < NTHREADS; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ }
+ printf("done\n");
+ return 0;
+}
diff --git a/testing/embedding/thread2-test.c b/testing/embedding/thread2-test.c
new file mode 100644
index 0000000..62f5ec8
--- /dev/null
+++ b/testing/embedding/thread2-test.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+extern int add1(int, int);
+extern int add2(int, int, int);
+
+static sem_t done;
+
+
+static void *start_routine_1(void *arg)
+{
+ int x, status;
+ x = add1(40, 2);
+ assert(x == 42);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+static void *start_routine_2(void *arg)
+{
+ int x, status;
+#ifdef T2TEST_AGAIN_ADD1
+ add1(-1, -1);
+#endif
+ x = add2(1000, 200, 30);
+ assert(x == 1230);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+int main(void)
+{
+ pthread_t th;
+ int i, status = sem_init(&done, 0, 0);
+ assert(status == 0);
+
+ printf("starting\n");
+ fflush(stdout);
+ status = pthread_create(&th, NULL, start_routine_1, NULL);
+ assert(status == 0);
+ status = pthread_create(&th, NULL, start_routine_2, NULL);
+ assert(status == 0);
+
+ for (i = 0; i < 2; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ }
+ printf("done\n");
+ return 0;
+}
diff --git a/testing/embedding/thread3-test.c b/testing/embedding/thread3-test.c
new file mode 100644
index 0000000..69ada27
--- /dev/null
+++ b/testing/embedding/thread3-test.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+extern int add2(int, int, int);
+extern int add3(int, int, int, int);
+
+static sem_t done;
+
+
+static void *start_routine_2(void *arg)
+{
+ int x, status;
+ x = add2(40, 2, 100);
+ assert(x == 142);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+static void *start_routine_3(void *arg)
+{
+ int x, status;
+ x = add3(1000, 200, 30, 4);
+ assert(x == 1234);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+int main(void)
+{
+ pthread_t th;
+ int i, status = sem_init(&done, 0, 0);
+ assert(status == 0);
+
+ printf("starting\n");
+ fflush(stdout);
+ for (i = 0; i < 10; i++) {
+ status = pthread_create(&th, NULL, start_routine_2, NULL);
+ assert(status == 0);
+ status = pthread_create(&th, NULL, start_routine_3, NULL);
+ assert(status == 0);
+ }
+ for (i = 0; i < 20; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ }
+ printf("done\n");
+ fflush(stdout); /* this is occasionally needed on Windows */
+ return 0;
+}
diff --git a/testing/embedding/tlocal-test.c b/testing/embedding/tlocal-test.c
new file mode 100644
index 0000000..b78a03d
--- /dev/null
+++ b/testing/embedding/tlocal-test.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+#define NTHREADS 10
+
+
+extern int add1(int, int);
+
+static sem_t done;
+
+
+static void *start_routine(void *arg)
+{
+ int i, x, expected, status;
+
+ expected = add1(40, 2);
+ assert((expected % 1000) == 42);
+
+ for (i=0; i<10; i++) {
+ x = add1(50, i);
+ assert(x == expected + 8 + i);
+ }
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+int main(void)
+{
+ pthread_t th;
+ int i, status = sem_init(&done, 0, 0);
+ assert(status == 0);
+
+ for (i = 0; i < NTHREADS; i++) {
+ status = pthread_create(&th, NULL, start_routine, NULL);
+ assert(status == 0);
+ }
+ for (i = 0; i < NTHREADS; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ }
+ printf("done\n");
+ return 0;
+}
diff --git a/testing/embedding/tlocal.py b/testing/embedding/tlocal.py
new file mode 100644
index 0000000..7800dff
--- /dev/null
+++ b/testing/embedding/tlocal.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ from _tlocal_cffi import ffi
+ import itertools
+ try:
+ import thread
+ g_seen = itertools.count().next
+ except ImportError:
+ import _thread as thread # py3
+ g_seen = itertools.count().__next__
+ tloc = thread._local()
+
+ @ffi.def_extern()
+ def add1(x, y):
+ try:
+ num = tloc.num
+ except AttributeError:
+ num = tloc.num = g_seen() * 1000
+ return x + y + num
+""")
+
+ffi.set_source("_tlocal_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))