Home | History | Annotate | Download | only in src
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "mojo/public/python/src/python_system_helper.h"
      6 
      7 #include "Python.h"
      8 
      9 #include "mojo/public/cpp/environment/environment.h"
     10 #include "mojo/public/cpp/environment/logging.h"
     11 #include "mojo/public/cpp/system/macros.h"
     12 #include "mojo/public/cpp/utility/run_loop.h"
     13 
     14 namespace {
     15 
     16 class ScopedGIL {
     17  public:
     18   ScopedGIL() { state_ = PyGILState_Ensure(); }
     19 
     20   ~ScopedGIL() { PyGILState_Release(state_); }
     21 
     22  private:
     23   PyGILState_STATE state_;
     24 
     25   MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedGIL);
     26 };
     27 
     28 class PythonClosure : public mojo::Closure::Runnable {
     29  public:
     30   PythonClosure(PyObject* callable) : callable_(callable) {
     31     MOJO_DCHECK(callable);
     32     Py_XINCREF(callable);
     33   }
     34 
     35   virtual ~PythonClosure() {
     36     ScopedGIL acquire_gil;
     37     Py_DECREF(callable_);
     38   }
     39 
     40   virtual void Run() const MOJO_OVERRIDE {
     41     ScopedGIL acquire_gil;
     42     PyObject* empty_tuple = PyTuple_New(0);
     43     if (!empty_tuple) {
     44       mojo::RunLoop::current()->Quit();
     45       return;
     46     }
     47 
     48     PyObject* result = PyObject_CallObject(callable_, empty_tuple);
     49     Py_DECREF(empty_tuple);
     50     if (result) {
     51       Py_DECREF(result);
     52     } else {
     53       mojo::RunLoop::current()->Quit();
     54       return;
     55     }
     56   }
     57 
     58  private:
     59   PyObject* callable_;
     60 
     61   MOJO_DISALLOW_COPY_AND_ASSIGN(PythonClosure);
     62 };
     63 
     64 void AsyncCallbackForwarder(void* closure, MojoResult result) {
     65   mojo::Callback<void(MojoResult)>* callback =
     66       static_cast<mojo::Callback<void(MojoResult)>*>(closure);
     67   // callback will be deleted when it is run.
     68   callback->Run(result);
     69 }
     70 
     71 }  // namespace
     72 
     73 namespace mojo {
     74 namespace python {
     75 
     76 class PythonAsyncWaiter::AsyncWaiterRunnable
     77     : public mojo::Callback<void(MojoResult)>::Runnable {
     78  public:
     79   AsyncWaiterRunnable(PyObject* callable, CallbackMap* callbacks)
     80       : wait_id_(0), callable_(callable), callbacks_(callbacks) {
     81     MOJO_DCHECK(callable);
     82     MOJO_DCHECK(callbacks_);
     83     Py_XINCREF(callable);
     84   }
     85 
     86   virtual ~AsyncWaiterRunnable() {
     87     ScopedGIL acquire_gil;
     88     Py_DECREF(callable_);
     89   }
     90 
     91   void set_wait_id(int wait_id) { wait_id_ = wait_id; }
     92 
     93   virtual void Run(MojoResult mojo_result) const MOJO_OVERRIDE {
     94     MOJO_DCHECK(wait_id_);
     95 
     96     // Remove to reference to this object from PythonAsyncWaiter and ensure this
     97     // object will be destroyed when this method exits.
     98     MOJO_DCHECK(callbacks_->find(wait_id_) != callbacks_->end());
     99     internal::SharedPtr<mojo::Callback<void(MojoResult)> > self =
    100         (*callbacks_)[wait_id_];
    101     callbacks_->erase(wait_id_);
    102 
    103     ScopedGIL acquire_gil;
    104     PyObject* args_tuple = Py_BuildValue("(i)", mojo_result);
    105     if (!args_tuple) {
    106       mojo::RunLoop::current()->Quit();
    107       return;
    108     }
    109 
    110     PyObject* result = PyObject_CallObject(callable_, args_tuple);
    111     Py_DECREF(args_tuple);
    112     if (result) {
    113       Py_DECREF(result);
    114     } else {
    115       mojo::RunLoop::current()->Quit();
    116       return;
    117     }
    118   }
    119 
    120  private:
    121   MojoAsyncWaitID wait_id_;
    122   PyObject* callable_;
    123   CallbackMap* callbacks_;
    124 
    125   MOJO_DISALLOW_COPY_AND_ASSIGN(AsyncWaiterRunnable);
    126 };
    127 
    128 Closure BuildClosure(PyObject* callable) {
    129   if (!PyCallable_Check(callable))
    130     return Closure();
    131 
    132   return Closure(
    133       static_cast<mojo::Closure::Runnable*>(new PythonClosure(callable)));
    134 }
    135 
    136 PythonAsyncWaiter::PythonAsyncWaiter() {
    137   async_waiter_ = Environment::GetDefaultAsyncWaiter();
    138 }
    139 
    140 PythonAsyncWaiter::~PythonAsyncWaiter() {
    141   for (CallbackMap::const_iterator it = callbacks_.begin();
    142        it != callbacks_.end();
    143        ++it) {
    144     async_waiter_->CancelWait(it->first);
    145   }
    146 }
    147 
    148 MojoAsyncWaitID PythonAsyncWaiter::AsyncWait(MojoHandle handle,
    149                                              MojoHandleSignals signals,
    150                                              MojoDeadline deadline,
    151                                              PyObject* callable) {
    152   AsyncWaiterRunnable* runner = new AsyncWaiterRunnable(callable, &callbacks_);
    153   internal::SharedPtr<mojo::Callback<void(MojoResult)> > callback(
    154       new mojo::Callback<void(MojoResult)>(
    155           static_cast<mojo::Callback<void(MojoResult)>::Runnable*>(runner)));
    156   MojoAsyncWaitID wait_id = async_waiter_->AsyncWait(
    157       handle, signals, deadline, &AsyncCallbackForwarder, callback.get());
    158   callbacks_[wait_id] = callback;
    159   runner->set_wait_id(wait_id);
    160   return wait_id;
    161 }
    162 
    163 void PythonAsyncWaiter::CancelWait(MojoAsyncWaitID wait_id) {
    164   if (callbacks_.find(wait_id) != callbacks_.end()) {
    165     async_waiter_->CancelWait(wait_id);
    166     callbacks_.erase(wait_id);
    167   }
    168 }
    169 
    170 }  // namespace python
    171 }  // namespace mojo
    172