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