Skip to content

Commit f5712af

Browse files
committed
more recount testing
1 parent e32ef26 commit f5712af

24 files changed

+541
-84
lines changed

.ycm_extra_conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
# translate the call to c++ instead o cc and youcompleteme add -x c++
2222
# instead of -x c
2323
env = {'CC': 'clang++'}
24-
2524
build = subprocess.Popen(['bear', 'python3', 'setup.py', 'build'], env=env)
25+
2626
build.wait()
2727
shutil.rmtree(os.path.join(ROOT_DIR, 'build'))
2828

Makefile

Lines changed: 0 additions & 22 deletions
This file was deleted.

README

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
mix.py
2+
------
3+
4+
mix.py is a coroutine library. Coroutines might also be called Fibers
5+
(Windows), Green Threads (Java), or Lightweight Threads.
6+
7+
vs. Greenlet
8+
--------
9+
10+
The main difference between mix.py and greenlet is the stack handling, while
11+
greenlet relies heavily on the use of copying to exchange stacks mix.py uses a
12+
fixed size stack.
13+
14+
That said, greenlet has the advantage that it's stacks can grow, while mix.py
15+
cannot (at least not without spagetthi stacks, and that is not tested for the
16+
moment), and mix.py sacrifices that benefit for the improved performance of
17+
_not_ copying stacks to and from the heap.
18+
19+
compiling
20+
---------
21+
22+
mix.py is coded in C++ with boost, it requires a least a C++11 compatible
23+
compiler and boost 1.60.0, and obviously Python's headers are required.
24+
25+
Use the package setup.py to compile the package with `python setup.py bdist`

debug.sh

Lines changed: 0 additions & 34 deletions
This file was deleted.

mix/Makefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# PYTHON_VERSION = 3.5m
2+
PYTHON_VERSION = 2.7
3+
PYTHON_INCLUDE = /usr/include/python$(PYTHON_VERSION)
4+
5+
BOOST_INC = /usr/include
6+
BOOST_LIB = /usr/lib
7+
8+
# CXX = clang
9+
CXX = gcc
10+
CXXFLAGS = -ggdb3 -Wall -Wextra -Werror -Wno-long-long -Wno-variadic-macros -fexceptions -DNDEBUG -std=c++14 -pipe -fstack-protector-strong --param=ssp-buffer-size=4
11+
LDFLAGS = -Wl,-O1,--sort-common,--as-needed,-z,relro,--export-dynamic
12+
INCLUDES = -I . -I ${BOOST_INC} -I $(PYTHON_INCLUDE)
13+
# LIBS = -L$(BOOST_LIB) -L/usr/lib/python$(PYTHON_VERSION)/config -lpython3 -lboost_python3 -lboost_context
14+
LIBS = -L$(BOOST_LIB) -L/usr/lib/python$(PYTHON_VERSION)/config -lpython2.7 -lboost_python -lboost_context
15+
16+
TARGET = fiber
17+
18+
$(TARGET).so: $(TARGET).o
19+
$(CXX) $(CXXFLAGS) $(INCLUDES) $(LIBS) -shared $(LDFLAGS) $(TARGET).o -o $(TARGET).so
20+
21+
$(TARGET).o: $(TARGET).cpp
22+
$(CXX) $(CXXFLAGS) $(INCLUDES) -fPIC -c $(TARGET).cpp
23+
24+
# python setup.py clean leaves most of it behind (dist, the build dir, and libraries from develop command)
25+
clean:
26+
rm *.so *.o || true
27+
# rm compile_commands.json || true
28+
# rm -r build mix.py.egg-info .tox || true

mix/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# -*- coding: utf-8 -*-
2+
# vim: set sts=4 sw=4 ts=4 et:
3+
4+
from mix.fiber import context_switch, Fiber # noqa
File renamed without changes.

mix.cpp renamed to mix/fiber.cpp

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// vim: set sts=4 sw=4 ts=4 et:
22

33
// Python.h includes pyconfig.h that unconditionally defines the
4-
// _POSIX_C_SOURCE and other constants, since the extension is compile with
4+
// _POSIX_C_SOURCE and other constants, since the extension is compiled with
55
// -Werror the compilation fail if Python.h is included after any system level
66
// header
77
#include <Python.h>
@@ -16,8 +16,7 @@ namespace ctx = boost::context;
1616
/**
1717
* This is a implementation of cooperatively scheduled M:1 fibers for python,
1818
* the motivation for this project is to have the equivalent semantics as
19-
* greenlets but with no copying of the stack data and an implementation that
20-
* does not have assumptions about the stack.
19+
* greenlets but with no copying of the stack data.
2120
*
2221
* The initial ideia was to use UNIX's makecontext() family of functions, this
2322
* idea was dropped because the signal mask requires SYSCALLs to update the
@@ -34,8 +33,8 @@ namespace ctx = boost::context;
3433
/**
3534
* Some notes:
3635
*
37-
* mix does not keep track of a hierarchy of executaiton the same way that
38-
* greenlet does, this logic should be emulated in python.
36+
* mix does not keep track of a hierarchy of execution like the greenlet
37+
* library, this logic should be emulated in python.
3938
*
4039
* Currently there is no support for spagetthi/segmented stacks.
4140
*
@@ -129,9 +128,27 @@ struct Fiber
129128
**/
130129
void first_call(intptr_t arguments_ptr) {
131130
callback_t *callback;
131+
PyObject* fiber;
132132

133133
callback = (callback_t*)arguments_ptr;
134-
PyObject_Call(callback->callback, callback->args, callback->kwds);
134+
135+
// PyObject_Call call will create a new frame object that will keep the
136+
// variables alive, we don't need to increase the refcount
137+
fiber = PyObject_Call(callback->callback, callback->args, callback->kwds);
138+
// If we get here either the function returned or a exception was raised
139+
// and not handled
140+
141+
// The fiber needs to exit to guarantee proper cleanup, otherwise callback
142+
// could have references and keep objects alive.
143+
if (fiber) {
144+
// TODO: check the type of fiber and switch context
145+
}
146+
147+
// Unlike greenlet we don't keep in the compiled code a chain of parent and
148+
// child threads of execution, that means we don't have another stack to
149+
// switch to and continue running, also we cannot raise an exception
150+
// SystemExit exception because there is no stack to unwind, so just
151+
// finalize the running interpreter.
135152

136153
#if PY_MAJOR_VERSION >= 3
137154
PyObject *pystdout, *pystderr;
@@ -156,18 +173,25 @@ void first_call(intptr_t arguments_ptr) {
156173
_PyObject_CallMethodId(pystdout, &PyId_flush, "");
157174
#endif
158175

159-
printf("mix: Fiber got to the end of stack\n");
160-
161-
// At this point we cannot raise an SystemExit exception because there is
162-
// no stack to unwind, so just finalize the running interpreter.
163-
//
164-
// XXX: this is probably wrong for python embedded
165-
Py_Finalize();
166-
std::exit(1);
176+
// XXX: calling exit() is probably wrong for python embedded
177+
// let the user know why we are exiting
178+
// if (result == NULL) {
179+
if (PyErr_Occurred() != NULL) {
180+
// PyObject *exc, *val, *tb;
181+
// PyErr_Fetch(&exc, &val, &tb);
182+
// PyErr_NormalizeException(&exc, &val, &c_tb);
183+
PyErr_Print();
184+
185+
// Calling Py_Finalize here will segfault
186+
fprintf(stderr, "mix: Fiber got to the end of stack\n");
187+
std::exit(2);
188+
} else {
189+
Py_Finalize();
190+
fprintf(stderr, "mix: Fiber got to the end of stack\n");
191+
std::exit(1);
192+
}
167193

168-
// Unlike greenlet we don't keep in the compiled code a chain of parent and
169-
// child threads of execution, that means we don't have another stack to
170-
// continue running and beyond this point we will segfault.
194+
// beyond this point the application will segfault
171195
}
172196

173197
/**
@@ -279,7 +303,7 @@ py::object py_context_switch(Fiber* origin, Fiber *target) {
279303
return py_context_switch_args_kwds(origin, target, py::tuple(), py::dict());
280304
}
281305

282-
BOOST_PYTHON_MODULE(mix)
306+
BOOST_PYTHON_MODULE(fiber)
283307
{
284308
using namespace boost::python;
285309

requirements/dev

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
tox

scripts/debug.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/bash
2+
3+
error() {
4+
printf "$(tput setaf 1)$@$(tput sgr0)\n" >&2
5+
exit
6+
}
7+
8+
(
9+
cd ./mix
10+
make clean
11+
make
12+
)
13+
pip install -e .
14+
15+
[ -z "${PYTHON}" ] && [ ! -z "${PYENV_ROOT}" ] && {
16+
PYTHON=$(pyenv which python)
17+
}
18+
19+
: ${PYTHON:=python}
20+
21+
[ -n "${PYTHON_SOURCE}" ] && {
22+
ADD_SOURCE="
23+
expect -exact \"(gdb)\"
24+
send \"dir ${PYTHON_SOURCE}\n\"
25+
"
26+
}
27+
28+
# if the user didn't ask to run as test just initialize a session
29+
[ $# -eq 0 ] && {
30+
RUN_CMD='
31+
expect -exact "(gdb)"
32+
send "run\n"
33+
34+
expect -exact ">>>"
35+
36+
send -- "
37+
import mix
38+
39+
def noop():
40+
pass
41+
42+
f1 = mix.Fiber(noop)
43+
"
44+
'
45+
}
46+
47+
# expect -exact "(gdb)"
48+
# send "break fiber.cpp:111\n"
49+
50+
# else check that the test file exists and run a session with it
51+
[ $# -ne 0 -a ! -e "${1}" ] && error "file ${1} does not exists"
52+
[ $# -ne 0 -a ! -f "${1}" ] && error "${1} is not a file"
53+
[ $# -ne 0 -a ! -x "${1}" ] && error "${1} is not an executable file"
54+
[ $# -ne 0 ] && {
55+
RUN_CMD="
56+
expect -exact \"(gdb)\"
57+
send \"run ${1}\"
58+
"
59+
}
60+
61+
read -r -d '' SCRIPT <<EOS
62+
spawn gdb
63+
64+
expect -exact "(gdb)"
65+
send "file ${PYTHON}\n"
66+
67+
${ADD_SOURCE}
68+
69+
expect -exact "(gdb)"
70+
send "dir mix\n"
71+
72+
$RUN_CMD
73+
74+
interact
75+
76+
EOS
77+
78+
/usr/bin/expect -c "${SCRIPT}";
79+
exit $?;

0 commit comments

Comments
 (0)