diff options
author | qsr <qsr@chromium.org> | 2014-09-18 04:07:00 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-18 11:07:14 +0000 |
commit | b2309d92488dad238e897214a47711ff0e8e379c (patch) | |
tree | 7e6e4b64a17efc208926f2ff24b811124be2b2ef | |
parent | 9d0221cd251502231877f943fa2872a2f92a5621 (diff) | |
download | chromium_src-b2309d92488dad238e897214a47711ff0e8e379c.zip chromium_src-b2309d92488dad238e897214a47711ff0e8e379c.tar.gz chromium_src-b2309d92488dad238e897214a47711ff0e8e379c.tar.bz2 |
mojo: Add a runloop utility in python
This is necessary to be able to use async operation on handles.
This is a reland of https://codereview.chromium.org/552783004 after the
build dependency issue has been fixed.
R=sdefresne@chromium.org
BUG=415485
Review URL: https://codereview.chromium.org/577193002
Cr-Commit-Position: refs/heads/master@{#295451}
-rw-r--r-- | mojo/mojo.gyp | 5 | ||||
-rw-r--r-- | mojo/public/python/BUILD.gn | 7 | ||||
-rw-r--r-- | mojo/public/python/mojo/c_environment.pxd | 30 | ||||
-rw-r--r-- | mojo/public/python/mojo/system.pyx | 31 | ||||
-rw-r--r-- | mojo/public/python/src/python_system_helper.cc | 79 | ||||
-rw-r--r-- | mojo/public/python/src/python_system_helper.h | 22 | ||||
-rw-r--r-- | mojo/python/tests/runloop_unittest.py | 42 | ||||
-rw-r--r-- | third_party/cython/rules.gni | 3 |
8 files changed, 219 insertions, 0 deletions
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp index 7948336..96e2a34 100644 --- a/mojo/mojo.gyp +++ b/mojo/mojo.gyp @@ -566,10 +566,15 @@ }, 'sources': [ 'public/python/mojo/c_core.pxd', + 'public/python/mojo/c_environment.pxd', 'public/python/mojo/system.pyx', + 'public/python/src/python_system_helper.cc', + 'public/python/src/python_system_helper.h', ], 'dependencies': [ + 'mojo_base.gyp:mojo_environment_standalone', 'mojo_base.gyp:mojo_system', + 'mojo_base.gyp:mojo_utility', ], 'includes': [ '../third_party/cython/cython_compiler.gypi' ], }, diff --git a/mojo/public/python/BUILD.gn b/mojo/public/python/BUILD.gn index 12c3d75..e71cf62 100644 --- a/mojo/public/python/BUILD.gn +++ b/mojo/public/python/BUILD.gn @@ -17,10 +17,17 @@ python_binary_module("system") { python_base_module = "mojo" sources = [ "mojo/c_core.pxd", + "mojo/c_environment.pxd", "mojo/system.pyx", ] + additional_sources = [ + "src/python_system_helper.cc", + "src/python_system_helper.h", + ] deps = [ "//mojo/public/c/system", + "//mojo/public/cpp/environment:standalone", + "//mojo/public/cpp/utility", ":base", ] } diff --git a/mojo/public/python/mojo/c_environment.pxd b/mojo/public/python/mojo/c_environment.pxd new file mode 100644 index 0000000..16f5c54 --- /dev/null +++ b/mojo/public/python/mojo/c_environment.pxd @@ -0,0 +1,30 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# distutils: language = c++ + +from libc.stdint cimport int64_t + + +cdef extern from "mojo/public/cpp/bindings/callback.h" nogil: + cdef cppclass CClosure "mojo::Callback<void()>": + CClosure() + + +cdef extern from "mojo/public/python/src/python_system_helper.h" \ + namespace "mojo" nogil: + cdef CClosure BuildClosure(object) + + +cdef extern from "mojo/public/cpp/utility/run_loop.h" nogil: + cdef cppclass CRunLoop "mojo::RunLoop": + CRunLoop() + void Run() except * + void RunUntilIdle() except * + void Quit() + void PostDelayedTask(CClosure& task, int64_t delay) + +cdef extern from "mojo/public/cpp/environment/environment.h" nogil: + cdef cppclass CEnvironment "mojo::Environment": + CEnvironment() diff --git a/mojo/public/python/mojo/system.pyx b/mojo/public/python/mojo/system.pyx index bd03550..84fbfef 100644 --- a/mojo/public/python/mojo/system.pyx +++ b/mojo/public/python/mojo/system.pyx @@ -5,6 +5,8 @@ # distutils language = c++ cimport c_core +cimport c_environment + from cpython.buffer cimport PyBUF_CONTIG from cpython.buffer cimport PyBUF_CONTIG_RO @@ -694,3 +696,32 @@ class DuplicateSharedBufferOptions(object): def __init__(self): self.flags = DuplicateSharedBufferOptions.FLAG_NONE + + +cdef class RunLoop(object): + """RunLoop to use when using asynchronous operations on handles.""" + + cdef c_environment.CRunLoop c_run_loop + + def Run(self): + """Run the runloop until Quit is called.""" + self.c_run_loop.Run() + + def RunUntilIdle(self): + """Run the runloop until Quit is called or no operation is waiting.""" + self.c_run_loop.RunUntilIdle() + + def Quit(self): + """Quit the runloop.""" + self.c_run_loop.Quit() + + def PostDelayedTask(self, runnable, delay=0): + """ + Post a task on the runloop. This must be called from the thread owning the + runloop. + """ + cdef c_environment.CClosure closure = c_environment.BuildClosure(runnable) + self.c_run_loop.PostDelayedTask(closure, delay) + + +cdef c_environment.CEnvironment* _ENVIRONMENT = new c_environment.CEnvironment() diff --git a/mojo/public/python/src/python_system_helper.cc b/mojo/public/python/src/python_system_helper.cc new file mode 100644 index 0000000..9103205 --- /dev/null +++ b/mojo/public/python/src/python_system_helper.cc @@ -0,0 +1,79 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/python/src/python_system_helper.h" + +#include "Python.h" + +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/utility/run_loop.h" + +namespace { + +class ScopedGIL { + public: + ScopedGIL() { + state_ = PyGILState_Ensure(); + } + + ~ScopedGIL() { + PyGILState_Release(state_); + } + + private: + PyGILState_STATE state_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedGIL); +}; + +class PythonClosure : public mojo::Closure::Runnable { + public: + PythonClosure(PyObject* callable) : callable_(callable) { + MOJO_CHECK(callable); + Py_XINCREF(callable); + } + + virtual ~PythonClosure() { + ScopedGIL acquire_gil; + Py_DECREF(callable_); + } + + virtual void Run() const MOJO_OVERRIDE { + ScopedGIL acquire_gil; + PyObject* empty_tuple = PyTuple_New(0); + if (!empty_tuple) { + mojo::RunLoop::current()->Quit(); + return; + } + + PyObject* result = PyObject_CallObject(callable_, empty_tuple); + Py_DECREF(empty_tuple); + if (result) { + Py_DECREF(result); + } else { + mojo::RunLoop::current()->Quit(); + return; + } + } + + private: + PyObject* callable_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(PythonClosure); +}; + +} // namespace + +namespace mojo { + +Closure BuildClosure(PyObject* callable) { + if (!PyCallable_Check(callable)) + return Closure(); + + return Closure( + static_cast<mojo::Closure::Runnable*>(new PythonClosure(callable))); +} + +} // namespace mojo diff --git a/mojo/public/python/src/python_system_helper.h b/mojo/public/python/src/python_system_helper.h new file mode 100644 index 0000000..1962ba7 --- /dev/null +++ b/mojo/public/python/src/python_system_helper.h @@ -0,0 +1,22 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_ +#define MOJO_PUBLIC_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_ + +#include "Python.h" + +#include "mojo/public/cpp/bindings/callback.h" + +namespace mojo { + +// Create a mojo::Closure from a callable python object. +mojo::Closure BuildClosure(PyObject* callable); + +// Initalize mojo::RunLoop +void InitRunLoop(); + +} // namespace mojo + +#endif // MOJO_PUBLIC_PYTHON_SRC_PYTHON_SYSTEM_HELPER_H_ diff --git a/mojo/python/tests/runloop_unittest.py b/mojo/python/tests/runloop_unittest.py new file mode 100644 index 0000000..3549dbe --- /dev/null +++ b/mojo/python/tests/runloop_unittest.py @@ -0,0 +1,42 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import unittest + +# pylint: disable=F0401 +import mojo.embedder +from mojo import system + + +def _Increment(array): + def _Closure(): + array.append(0) + return _Closure + + +class RunLoopTest(unittest.TestCase): + + def setUp(self): + mojo.embedder.Init() + + def testRunLoop(self): + loop = system.RunLoop() + array = [] + for _ in xrange(10): + loop.PostDelayedTask(_Increment(array)) + loop.RunUntilIdle() + self.assertEquals(len(array), 10) + + def testRunLoopWithException(self): + loop = system.RunLoop() + def Throw(): + raise Exception("error") + array = [] + loop.PostDelayedTask(Throw) + loop.PostDelayedTask(_Increment(array)) + with self.assertRaisesRegexp(Exception, '^error$'): + loop.Run() + self.assertEquals(len(array), 0) + loop.RunUntilIdle() + self.assertEquals(len(array), 1) diff --git a/third_party/cython/rules.gni b/third_party/cython/rules.gni index fce3e4c..252e948 100644 --- a/third_party/cython/rules.gni +++ b/third_party/cython/rules.gni @@ -72,6 +72,9 @@ template("python_binary_module") { datadeps = invoker.datadeps } sources = [ cython_output ] + if (defined(invoker.additional_sources)) { + sources += invoker.additional_sources + } configs += [ ":$config_name" ] } |