blob: 22b6bbb4a5d47eff3e459168b387df4e58b3ddf9 [file] [log] [blame]
// 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/environment.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);
};
enum ScopedPyRefAcquire {
kAcquire,
};
class ScopedPyRef {
public:
ScopedPyRef(PyObject* object) : object_(object) {}
ScopedPyRef(PyObject* object, ScopedPyRefAcquire) : object_(object) {
Py_XINCREF(object_);
}
~ScopedPyRef() {
if (object_) {
ScopedGIL acquire_gil;
Py_DECREF(object_);
}
}
operator PyObject*() const { return object_; }
private:
PyObject* object_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedPyRef);
};
class PythonClosure : public mojo::Closure::Runnable {
public:
PythonClosure(PyObject* callable) : callable_(callable, kAcquire) {
MOJO_DCHECK(callable);
}
void Run() const override {
ScopedGIL acquire_gil;
ScopedPyRef empty_tuple(PyTuple_New(0));
if (!empty_tuple) {
mojo::RunLoop::current()->Quit();
return;
}
ScopedPyRef result(PyObject_CallObject(callable_, empty_tuple));
if (!result) {
mojo::RunLoop::current()->Quit();
return;
}
}
private:
ScopedPyRef callable_;
MOJO_DISALLOW_COPY_AND_ASSIGN(PythonClosure);
};
void AsyncCallbackForwarder(void* closure, MojoResult result) {
mojo::Callback<void(MojoResult)>* callback =
static_cast<mojo::Callback<void(MojoResult)>*>(closure);
// callback will be deleted when it is run.
callback->Run(result);
}
} // namespace
namespace mojo {
namespace python {
class PythonAsyncWaiter::AsyncWaiterRunnable
: public mojo::Callback<void(MojoResult)>::Runnable {
public:
AsyncWaiterRunnable(PyObject* callable, CallbackMap* callbacks)
: wait_id_(0), callable_(callable, kAcquire), callbacks_(callbacks) {
MOJO_DCHECK(callable);
MOJO_DCHECK(callbacks_);
}
void set_wait_id(int wait_id) { wait_id_ = wait_id; }
void Run(MojoResult mojo_result) const override {
MOJO_DCHECK(wait_id_);
// Remove to reference to this object from PythonAsyncWaiter and ensure this
// object will be destroyed when this method exits.
MOJO_DCHECK(callbacks_->find(wait_id_) != callbacks_->end());
internal::SharedPtr<mojo::Callback<void(MojoResult)>> self =
(*callbacks_)[wait_id_];
callbacks_->erase(wait_id_);
ScopedGIL acquire_gil;
ScopedPyRef args_tuple(Py_BuildValue("(i)", mojo_result));
if (!args_tuple) {
mojo::RunLoop::current()->Quit();
return;
}
ScopedPyRef result(PyObject_CallObject(callable_, args_tuple));
if (!result) {
mojo::RunLoop::current()->Quit();
return;
}
}
private:
MojoAsyncWaitID wait_id_;
ScopedPyRef callable_;
CallbackMap* callbacks_;
MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiterRunnable);
};
Closure BuildClosure(PyObject* callable) {
if (!PyCallable_Check(callable))
return Closure();
return Closure(
static_cast<mojo::Closure::Runnable*>(new PythonClosure(callable)));
}
PythonAsyncWaiter::PythonAsyncWaiter() {
async_waiter_ = Environment::GetDefaultAsyncWaiter();
}
PythonAsyncWaiter::~PythonAsyncWaiter() {
for (CallbackMap::const_iterator it = callbacks_.begin();
it != callbacks_.end();
++it) {
async_waiter_->CancelWait(it->first);
}
}
MojoAsyncWaitID PythonAsyncWaiter::AsyncWait(MojoHandle handle,
MojoHandleSignals signals,
MojoDeadline deadline,
PyObject* callable) {
AsyncWaiterRunnable* runner = new AsyncWaiterRunnable(callable, &callbacks_);
internal::SharedPtr<mojo::Callback<void(MojoResult)>> callback(
new mojo::Callback<void(MojoResult)>(
static_cast<mojo::Callback<void(MojoResult)>::Runnable*>(runner)));
MojoAsyncWaitID wait_id = async_waiter_->AsyncWait(
handle, signals, deadline, &AsyncCallbackForwarder, callback.get());
callbacks_[wait_id] = callback;
runner->set_wait_id(wait_id);
return wait_id;
}
void PythonAsyncWaiter::CancelWait(MojoAsyncWaitID wait_id) {
if (callbacks_.find(wait_id) != callbacks_.end()) {
async_waiter_->CancelWait(wait_id);
callbacks_.erase(wait_id);
}
}
} // namespace python
} // namespace mojo