[SCM] Samba Shared Repository - branch master updated
Andrew Bartlett
abartlet at samba.org
Wed Jun 10 00:56:03 MDT 2015
The branch, master has been updated
via d0f7651 pytevent: Build for two versions of Python at once
via 8dbdd27 pytevent: Port to Python 3
via 5605762 pytalloc: Improve timer wrapper, and test it
via 4399dc5 pytevent: Define missing TeventFd_Type object
via f5838df pytevent: Better error and reference handling
via 2bf1443 buildtools: Always reset the build environment
from 728cfa5 s3: libsmbclient: Use cache targetcli in SMBC_splice_ctx calls also.
https://git.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit d0f76510a950de65f3bb343ea6ca9412d8441a22
Author: Petr Viktorin <pviktori at redhat.com>
Date: Fri May 22 11:52:39 2015 +0200
pytevent: Build for two versions of Python at once
Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Jelmer Vernooij <jelmer at samba.org>
Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
Autobuild-Date(master): Wed Jun 10 08:55:37 CEST 2015 on sn-devel-104
commit 8dbdd27526c4d5358bc2614e90f9aca36f41ed1a
Author: Petr Viktorin <pviktori at redhat.com>
Date: Fri May 22 11:47:56 2015 +0200
pytevent: Port to Python 3
- Use PyStr (String on py2, Unicode on py3) for text strings
- Use PyLong instead of PyInt on Python 3
- Use new module initialization
Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Jelmer Vernooij <jelmer at samba.org>
commit 560576217f11b132f9c32de6e41c623dbc152137
Author: Petr Viktorin <pviktori at redhat.com>
Date: Tue May 26 13:25:12 2015 +0200
pytalloc: Improve timer wrapper, and test it
Using Context.add_timer resulted in crashes due to missing type object
and bad reference handling.
Add a TeventTimer_Type struct, and introduce a clear ownership/lifetime model.
Add a "add_timer_offset" to allow adding timers from Python. (add_timer
requires passing struct timeval as a Python integer, which can't really
be done portably).
Add tests.
Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Jelmer Vernooij <jelmer at samba.org>
commit 4399dc582fa06b04e1cec1d3aa59cd332e4b5ba2
Author: Petr Viktorin <pviktori at redhat.com>
Date: Fri May 22 13:29:11 2015 +0200
pytevent: Define missing TeventFd_Type object
The type objects for Fd was declared but never defined,
resulting in segfaults when it was used.
Define it.
Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Jelmer Vernooij <jelmer at samba.org>
commit f5838df58ea36e64cd0295b595df9cbd10d8c757
Author: Petr Viktorin <pviktori at redhat.com>
Date: Thu Dec 4 12:44:56 2014 +0100
pytevent: Better error and reference handling
py_backend_list:
- Handle cases of PyString_FromString or PyList_Append failing.
- Properly decrease the reference count of the returned strings.
py_register_backend:
- Decref "name" after use
Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Jelmer Vernooij <jelmer at samba.org>
commit 2bf1443bd0f1cb95d11f42db5ef4a7637981ee6b
Author: Petr Viktorin <pviktori at redhat.com>
Date: Fri May 22 13:06:48 2015 +0200
buildtools: Always reset the build environment
In install_library, the Build object's environment was not reset
after an early return, so the extrapython env would be used in
subsequent build steps.
Wrap everything in a try-finally block to make sure the env is reset.
(Almost all of the change is indentation, `git show -w` recommended.)
Signed-off-by: Petr Viktorin <pviktori at redhat.com>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
Reviewed-by: Jelmer Vernooij <jelmer at samba.org>
-----------------------------------------------------------------------
Summary of changes:
buildtools/wafsamba/samba_install.py | 163 +++++++++++-----------
lib/tevent/bindings.py | 52 ++++++-
lib/tevent/pytevent.c | 259 ++++++++++++++++++++++++++++++-----
lib/tevent/wscript | 22 +--
4 files changed, 371 insertions(+), 125 deletions(-)
Changeset truncated at 500 lines:
diff --git a/buildtools/wafsamba/samba_install.py b/buildtools/wafsamba/samba_install.py
index af8d2ad..3d0c23a 100644
--- a/buildtools/wafsamba/samba_install.py
+++ b/buildtools/wafsamba/samba_install.py
@@ -60,95 +60,96 @@ def install_library(self):
bld = self.bld
default_env = bld.all_envs['default']
- if self.env['IS_EXTRA_PYTHON']:
- bld.all_envs['default'] = bld.all_envs['extrapython']
+ try:
+ if self.env['IS_EXTRA_PYTHON']:
+ bld.all_envs['default'] = bld.all_envs['extrapython']
- install_ldflags = install_rpath(self)
- build_ldflags = build_rpath(bld)
+ install_ldflags = install_rpath(self)
+ build_ldflags = build_rpath(bld)
- if not Options.is_install or not getattr(self, 'samba_install', True):
- # just need to set the build rpath if we are not installing
- self.env.RPATH = build_ldflags
- return
+ if not Options.is_install or not getattr(self, 'samba_install', True):
+ # just need to set the build rpath if we are not installing
+ self.env.RPATH = build_ldflags
+ return
- # setup the install path, expanding variables
- install_path = getattr(self, 'samba_inst_path', None)
- if install_path is None:
- if getattr(self, 'private_library', False):
- install_path = '${PRIVATELIBDIR}'
+ # setup the install path, expanding variables
+ install_path = getattr(self, 'samba_inst_path', None)
+ if install_path is None:
+ if getattr(self, 'private_library', False):
+ install_path = '${PRIVATELIBDIR}'
+ else:
+ install_path = '${LIBDIR}'
+ install_path = bld.EXPAND_VARIABLES(install_path)
+
+ target_name = self.target
+
+ if install_ldflags != build_ldflags:
+ # we will be creating a new target name, and using that for the
+ # install link. That stops us from overwriting the existing build
+ # target, which has different ldflags
+ self.done_install_library = True
+ t = self.clone(self.env)
+ t.posted = False
+ t.target += '.inst'
+ self.env.RPATH = build_ldflags
else:
- install_path = '${LIBDIR}'
- install_path = bld.EXPAND_VARIABLES(install_path)
-
- target_name = self.target
-
- if install_ldflags != build_ldflags:
- # we will be creating a new target name, and using that for the
- # install link. That stops us from overwriting the existing build
- # target, which has different ldflags
- self.done_install_library = True
- t = self.clone(self.env)
- t.posted = False
- t.target += '.inst'
- self.env.RPATH = build_ldflags
- else:
- t = self
-
- t.env.RPATH = install_ldflags
-
- dev_link = None
-
- # in the following the names are:
- # - inst_name is the name with .inst. in it, in the build
- # directory
- # - install_name is the name in the install directory
- # - install_link is a symlink in the install directory, to install_name
-
- if getattr(self, 'samba_realname', None):
- install_name = self.samba_realname
- install_link = None
- if getattr(self, 'soname', ''):
+ t = self
+
+ t.env.RPATH = install_ldflags
+
+ dev_link = None
+
+ # in the following the names are:
+ # - inst_name is the name with .inst. in it, in the build
+ # directory
+ # - install_name is the name in the install directory
+ # - install_link is a symlink in the install directory, to install_name
+
+ if getattr(self, 'samba_realname', None):
+ install_name = self.samba_realname
+ install_link = None
+ if getattr(self, 'soname', ''):
+ install_link = self.soname
+ if getattr(self, 'samba_type', None) == 'PYTHON':
+ inst_name = bld.make_libname(t.target, nolibprefix=True, python=True)
+ else:
+ inst_name = bld.make_libname(t.target)
+ elif self.vnum:
+ vnum_base = self.vnum.split('.')[0]
+ install_name = bld.make_libname(target_name, version=self.vnum)
+ install_link = bld.make_libname(target_name, version=vnum_base)
+ inst_name = bld.make_libname(t.target)
+ if not self.private_library:
+ # only generate the dev link for non-bundled libs
+ dev_link = bld.make_libname(target_name)
+ elif getattr(self, 'soname', ''):
+ install_name = bld.make_libname(target_name)
install_link = self.soname
- if getattr(self, 'samba_type', None) == 'PYTHON':
- inst_name = bld.make_libname(t.target, nolibprefix=True, python=True)
- else:
inst_name = bld.make_libname(t.target)
- elif self.vnum:
- vnum_base = self.vnum.split('.')[0]
- install_name = bld.make_libname(target_name, version=self.vnum)
- install_link = bld.make_libname(target_name, version=vnum_base)
- inst_name = bld.make_libname(t.target)
- if not self.private_library:
- # only generate the dev link for non-bundled libs
- dev_link = bld.make_libname(target_name)
- elif getattr(self, 'soname', ''):
- install_name = bld.make_libname(target_name)
- install_link = self.soname
- inst_name = bld.make_libname(t.target)
- else:
- install_name = bld.make_libname(target_name)
- install_link = None
- inst_name = bld.make_libname(t.target)
-
- if t.env.SONAME_ST:
- # ensure we get the right names in the library
- if install_link:
- t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_link)
else:
- t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_name)
- t.env.SONAME_ST = ''
-
- # tell waf to install the library
- bld.install_as(os.path.join(install_path, install_name),
- os.path.join(self.path.abspath(bld.env), inst_name),
- chmod=MODE_755)
- if install_link and install_link != install_name:
- # and the symlink if needed
- bld.symlink_as(os.path.join(install_path, install_link), os.path.basename(install_name))
- if dev_link:
- bld.symlink_as(os.path.join(install_path, dev_link), os.path.basename(install_name))
+ install_name = bld.make_libname(target_name)
+ install_link = None
+ inst_name = bld.make_libname(t.target)
- bld.all_envs['default'] = default_env
+ if t.env.SONAME_ST:
+ # ensure we get the right names in the library
+ if install_link:
+ t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_link)
+ else:
+ t.env.append_value('LINKFLAGS', t.env.SONAME_ST % install_name)
+ t.env.SONAME_ST = ''
+
+ # tell waf to install the library
+ bld.install_as(os.path.join(install_path, install_name),
+ os.path.join(self.path.abspath(bld.env), inst_name),
+ chmod=MODE_755)
+ if install_link and install_link != install_name:
+ # and the symlink if needed
+ bld.symlink_as(os.path.join(install_path, install_link), os.path.basename(install_name))
+ if dev_link:
+ bld.symlink_as(os.path.join(install_path, dev_link), os.path.basename(install_name))
+ finally:
+ bld.all_envs['default'] = default_env
@feature('cshlib')
diff --git a/lib/tevent/bindings.py b/lib/tevent/bindings.py
index 1060caf..55aafbb 100644
--- a/lib/tevent/bindings.py
+++ b/lib/tevent/bindings.py
@@ -22,8 +22,10 @@
# License along with this library; if not, see <http://www.gnu.org/licenses/>.
import signal
+from unittest import TestCase, TestProgram
+import gc
+
import _tevent
-from unittest import TestCase
class BackendListTests(TestCase):
@@ -60,3 +62,51 @@ class ContextTests(TestCase):
def test_add_signal(self):
sig = self.ctx.add_signal(signal.SIGINT, 0, lambda callback: None)
self.assertTrue(isinstance(sig, _tevent.Signal))
+
+ def test_timer(self):
+ """Test a timer is can be scheduled"""
+ collecting_list = []
+ # time "0" has already passed, callback will be scheduled immediately
+ timer = self.ctx.add_timer(0, lambda t: collecting_list.append(True))
+ self.assertTrue(timer.active)
+ self.assertEqual(collecting_list, [])
+ self.ctx.loop_once()
+ self.assertFalse(timer.active)
+ self.assertEqual(collecting_list, [True])
+
+ def test_timer_deallocate_timer(self):
+ """Test timer is scheduled even if reference to it isn't held"""
+ collecting_list = []
+ def callback(t):
+ collecting_list.append(True)
+ timer = self.ctx.add_timer(0, lambda t: collecting_list.append(True))
+ gc.collect()
+ self.assertEqual(collecting_list, [])
+ self.ctx.loop_once()
+ self.assertEqual(collecting_list, [True])
+
+ def test_timer_deallocate_context(self):
+ """Test timer is unscheduled when context is freed"""
+ collecting_list = []
+ def callback(t):
+ collecting_list.append(True)
+ timer = self.ctx.add_timer(0, lambda t: collecting_list.append(True))
+ self.assertTrue(timer.active)
+ del self.ctx
+ gc.collect()
+ self.assertEqual(collecting_list, [])
+ self.assertFalse(timer.active)
+
+ def test_timer_offset(self):
+ """Test scheduling timer with an offset"""
+ collecting_list = []
+ self.ctx.add_timer_offset(0.2, lambda t: collecting_list.append(2))
+ self.ctx.add_timer_offset(0.1, lambda t: collecting_list.append(1))
+ self.assertEqual(collecting_list, [])
+ self.ctx.loop_once()
+ self.assertEqual(collecting_list, [1])
+ self.ctx.loop_once()
+ self.assertEqual(collecting_list, [1, 2])
+
+if __name__ == '__main__':
+ TestProgram()
diff --git a/lib/tevent/pytevent.c b/lib/tevent/pytevent.c
index af3f9d6..5725ae3 100644
--- a/lib/tevent/pytevent.c
+++ b/lib/tevent/pytevent.c
@@ -25,6 +25,17 @@
#include <Python.h>
#include <tevent.h>
+#if PY_MAJOR_VERSION >= 3
+#define PyStr_Check PyUnicode_Check
+#define PyStr_FromString PyUnicode_FromString
+#define PyStr_AsUTF8 PyUnicode_AsUTF8
+#define PyInt_FromLong PyLong_FromLong
+#else
+#define PyStr_Check PyString_Check
+#define PyStr_FromString PyString_FromString
+#define PyStr_AsUTF8 PyString_AsString
+#endif
+
void init_tevent(void);
typedef struct {
@@ -50,6 +61,7 @@ typedef struct {
typedef struct {
PyObject_HEAD
struct tevent_timer *timer;
+ PyObject *callback;
} TeventTimer_Object;
typedef struct {
@@ -175,16 +187,20 @@ static PyObject *py_register_backend(PyObject *self, PyObject *args)
return NULL;
}
- if (!PyString_Check(name)) {
+ if (!PyStr_Check(name)) {
PyErr_SetNone(PyExc_TypeError);
+ Py_DECREF(name);
return NULL;
}
- if (!tevent_register_backend(PyString_AsString(name), &py_tevent_ops)) { /* FIXME: What to do with backend */
+ if (!tevent_register_backend(PyStr_AsUTF8(name), &py_tevent_ops)) { /* FIXME: What to do with backend */
PyErr_SetNone(PyExc_RuntimeError);
+ Py_DECREF(name);
return NULL;
}
+ Py_DECREF(name);
+
Py_RETURN_NONE;
}
@@ -368,38 +384,148 @@ static void py_timer_handler(struct tevent_context *ev,
struct timeval current_time,
void *private_data)
{
- PyObject *callback = private_data, *ret;
- ret = PyObject_CallFunction(callback, "l", te);
+ TeventTimer_Object *self = private_data;
+ PyObject *ret;
+
+ ret = PyObject_CallFunction(self->callback, "l", te);
+ if (ret == NULL) {
+ /* No Python stack to propagate exception to; just print traceback */
+ PyErr_PrintEx(0);
+ }
Py_XDECREF(ret);
}
-static PyObject *py_tevent_context_add_timer(TeventContext_Object *self, PyObject *args)
+static void py_tevent_timer_dealloc(TeventTimer_Object *self)
{
- TeventTimer_Object *ret;
- struct timeval next_event;
- struct tevent_timer *timer;
- PyObject *handler;
- if (!PyArg_ParseTuple(args, "lO", &next_event, &handler))
- return NULL;
-
- timer = tevent_add_timer(self->ev, NULL, next_event, py_timer_handler,
- handler);
- if (timer == NULL) {
- PyErr_SetNone(PyExc_RuntimeError);
- return NULL;
+ if (self->timer) {
+ talloc_free(self->timer);
}
+ Py_DECREF(self->callback);
+ PyObject_Del(self);
+}
+
+static int py_tevent_timer_traverse(TeventTimer_Object *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->callback);
+ return 0;
+}
+
+static PyObject* py_tevent_timer_get_active(TeventTimer_Object *self) {
+ return PyBool_FromLong(self->timer != NULL);
+}
+
+struct PyGetSetDef py_tevent_timer_getset[] = {
+ {
+ .name = "active",
+ .get = (getter)py_tevent_timer_get_active,
+ .doc = "true if the timer is scheduled to run",
+ },
+ {NULL},
+};
+
+static PyTypeObject TeventTimer_Type = {
+ .tp_name = "tevent.Timer",
+ .tp_basicsize = sizeof(TeventTimer_Object),
+ .tp_dealloc = (destructor)py_tevent_timer_dealloc,
+ .tp_traverse = (traverseproc)py_tevent_timer_traverse,
+ .tp_getset = py_tevent_timer_getset,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+static int timer_destructor(void* ptr)
+{
+ TeventTimer_Object *obj = *(TeventTimer_Object **)ptr;
+ obj->timer = NULL;
+ Py_DECREF(obj);
+ return 0;
+}
+
+static PyObject *py_tevent_context_add_timer_internal(TeventContext_Object *self,
+ struct timeval next_event,
+ PyObject *callback)
+{
+ /* Ownership notes:
+ *
+ * There are 5 pieces in play; two tevent contexts and 3 Python objects:
+ * - The tevent timer
+ * - The tevent context
+ * - The Python context -- "self"
+ * - The Python timer (TeventTimer_Object) -- "ret"
+ * - The Python callback function -- "callback"
+ *
+ * We only use the Python context for getting the tevent context,
+ * afterwards it can be destroyed.
+ *
+ * The tevent context owns the tevent timer.
+ *
+ * The tevent timer holds a reference to the Python timer, so the Python
+ * timer must always outlive the tevent timer.
+ * The Python timer has a pointer to the tevent timer; a destructor is
+ * used to set this to NULL when the tevent timer is deallocated.
+ *
+ * The tevent timer can be deallocated in these cases:
+ * 1) when the context is destroyed
+ * 2) after the event fires
+ * Posssibly, API might be added to cancel (free the tevent timer).
+ *
+ * The Python timer holds a reference to the callback.
+ */
+ TeventTimer_Object *ret;
+ TeventTimer_Object **tmp_context;
ret = PyObject_New(TeventTimer_Object, &TeventTimer_Type);
if (ret == NULL) {
PyErr_NoMemory();
- talloc_free(timer);
return NULL;
}
- ret->timer = timer;
+ Py_INCREF(callback);
+ ret->callback = callback;
+ ret->timer = tevent_add_timer(self->ev, NULL, next_event, py_timer_handler,
+ ret);
+ if (ret->timer == NULL) {
+ Py_DECREF(ret);
+ PyErr_SetString(PyExc_RuntimeError, "Could not initialize timer");
+ return NULL;
+ }
+ tmp_context = talloc(ret->timer, TeventTimer_Object*);
+ if (tmp_context == NULL) {
+ talloc_free(ret->timer);
+ Py_DECREF(ret);
+ PyErr_SetString(PyExc_RuntimeError, "Could not initialize timer");
+ return NULL;
+ }
+ Py_INCREF(ret);
+ *tmp_context = ret;
+ talloc_set_destructor(tmp_context, timer_destructor);
return (PyObject *)ret;
}
+static PyObject *py_tevent_context_add_timer(TeventContext_Object *self, PyObject *args)
+{
+ struct timeval next_event;
+ PyObject *callback;
+ if (!PyArg_ParseTuple(args, "lO", &next_event, &callback))
+ return NULL;
+
+ return py_tevent_context_add_timer_internal(self, next_event, callback);
+}
+
+static PyObject *py_tevent_context_add_timer_offset(TeventContext_Object *self, PyObject *args)
+{
+ struct timeval next_event;
+ double offset;
+ int seconds;
+ PyObject *callback;
+ if (!PyArg_ParseTuple(args, "dO", &offset, &callback))
+ return NULL;
+
+ seconds = offset;
+ offset -= seconds;
+ next_event = tevent_timeval_current_ofs(seconds, (int)(offset*1000000));
+ return py_tevent_context_add_timer_internal(self, next_event, callback);
+}
+
static void py_fd_handler(struct tevent_context *ev,
struct tevent_fd *fde,
uint16_t flags,
@@ -411,6 +537,19 @@ static void py_fd_handler(struct tevent_context *ev,
Py_XDECREF(ret);
}
+static void py_tevent_fp_dealloc(TeventFd_Object *self)
+{
+ talloc_free(self->fd);
+ PyObject_Del(self);
+}
+
+static PyTypeObject TeventFd_Type = {
+ .tp_name = "tevent.Fd",
+ .tp_basicsize = sizeof(TeventFd_Object),
+ .tp_dealloc = (destructor)py_tevent_fp_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
static PyObject *py_tevent_context_add_fd(TeventContext_Object *self, PyObject *args)
{
int fd, flags;
@@ -462,6 +601,8 @@ static PyMethodDef py_tevent_context_methods[] = {
METH_VARARGS, "S.add_signal(signum, sa_flags, handler) -> signal" },
{ "add_timer", (PyCFunction)py_tevent_context_add_timer,
METH_VARARGS, "S.add_timer(next_event, handler) -> timer" },
+ { "add_timer_offset", (PyCFunction)py_tevent_context_add_timer_offset,
+ METH_VARARGS, "S.add_timer(offset_seconds, handler) -> timer" },
{ "add_fd", (PyCFunction)py_tevent_context_add_fd,
METH_VARARGS, "S.add_fd(fd, flags, handler) -> fd" },
#ifdef TEVENT_DEPRECATED
@@ -684,9 +825,10 @@ static PyObject *py_set_default_backend(PyObject *self, PyObject *args)
--
Samba Shared Repository
More information about the samba-cvs
mailing list