summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorqsr <qsr@chromium.org>2014-09-18 04:07:00 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-18 11:07:14 +0000
commitb2309d92488dad238e897214a47711ff0e8e379c (patch)
tree7e6e4b64a17efc208926f2ff24b811124be2b2ef
parent9d0221cd251502231877f943fa2872a2f92a5621 (diff)
downloadchromium_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.gyp5
-rw-r--r--mojo/public/python/BUILD.gn7
-rw-r--r--mojo/public/python/mojo/c_environment.pxd30
-rw-r--r--mojo/public/python/mojo/system.pyx31
-rw-r--r--mojo/public/python/src/python_system_helper.cc79
-rw-r--r--mojo/public/python/src/python_system_helper.h22
-rw-r--r--mojo/python/tests/runloop_unittest.py42
-rw-r--r--third_party/cython/rules.gni3
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" ]
}