Commit 830c88cc authored by qsr's avatar qsr Committed by Commit bot

mojo: Add a runloop utility in python

This is necessary to be able to use async operation on handles.

R=sdefresne@chromium.org,cmasone@chromium.org

Review URL: https://codereview.chromium.org/552783004

Cr-Commit-Position: refs/heads/master@{#295267}
parent b2114e78
...@@ -566,10 +566,15 @@ ...@@ -566,10 +566,15 @@
}, },
'sources': [ 'sources': [
'public/python/mojo/c_core.pxd', 'public/python/mojo/c_core.pxd',
'public/python/mojo/c_environment.pxd',
'public/python/mojo/system.pyx', 'public/python/mojo/system.pyx',
'public/python/src/python_system_helper.cc',
'public/python/src/python_system_helper.h',
], ],
'dependencies': [ 'dependencies': [
'mojo_base.gyp:mojo_environment_standalone',
'mojo_base.gyp:mojo_system', 'mojo_base.gyp:mojo_system',
'mojo_base.gyp:mojo_utility',
], ],
'includes': [ '../third_party/cython/cython_compiler.gypi' ], 'includes': [ '../third_party/cython/cython_compiler.gypi' ],
}, },
......
...@@ -17,10 +17,17 @@ python_binary_module("system") { ...@@ -17,10 +17,17 @@ python_binary_module("system") {
python_base_module = "mojo" python_base_module = "mojo"
sources = [ sources = [
"mojo/c_core.pxd", "mojo/c_core.pxd",
"mojo/c_environment.pxd",
"mojo/system.pyx", "mojo/system.pyx",
] ]
additional_sources = [
"src/python_system_helper.cc",
"src/python_system_helper.h",
]
deps = [ deps = [
"//mojo/public/c/system", "//mojo/public/c/system",
"//mojo/public/cpp/environment:standalone",
"//mojo/public/cpp/utility",
":base", ":base",
] ]
} }
......
# 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()
CClosure(CRunnable)
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()
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
# distutils language = c++ # distutils language = c++
cimport c_core cimport c_core
cimport c_environment
from cpython.buffer cimport PyBUF_CONTIG from cpython.buffer cimport PyBUF_CONTIG
from cpython.buffer cimport PyBUF_CONTIG_RO from cpython.buffer cimport PyBUF_CONTIG_RO
...@@ -694,3 +696,32 @@ class DuplicateSharedBufferOptions(object): ...@@ -694,3 +696,32 @@ class DuplicateSharedBufferOptions(object):
def __init__(self): def __init__(self):
self.flags = DuplicateSharedBufferOptions.FLAG_NONE 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()
// 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
// 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_
# 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)
...@@ -72,6 +72,9 @@ template("python_binary_module") { ...@@ -72,6 +72,9 @@ template("python_binary_module") {
datadeps = invoker.datadeps datadeps = invoker.datadeps
} }
sources = [ cython_output ] sources = [ cython_output ]
if (defined(invoker.additional_sources)) {
sources += invoker.additional_sources
}
configs += [ ":$config_name" ] configs += [ ":$config_name" ]
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment