Run by the rest handling threads. Pushes the task to Python and wait for its finish.
| 149 | |
| 150 | // Run by the rest handling threads. Pushes the task to Python and wait for its finish. |
| 151 | python_plugin::TaskOutput::ErrorCode PythonPluginManagerImpl::CallInternal( |
| 152 | const std::string& user, const std::string& function, const std::string& input, double timeout, |
| 153 | bool in_process, bool read_only, std::string& output) { |
| 154 | // check timeout |
| 155 | if (timeout <= 0) timeout = (double)3600 * 24 * 365; |
| 156 | std::unique_ptr<PythonWorkerProcess> proc(nullptr); |
| 157 | AutoCleanupAction rollback(nullptr); |
| 158 | { |
| 159 | // pick a free process, or create a new one |
| 160 | std::lock_guard<std::mutex> l(_mtx); |
| 161 | if (_free_processes.empty()) { |
| 162 | // no free process, create a new one |
| 163 | LOG_DEBUG() << "Creating a new Python process"; |
| 164 | proc.reset(new PythonWorkerProcess(db_dir_)); |
| 165 | } else { |
| 166 | proc.swap(_free_processes.front()); |
| 167 | _free_processes.pop_front(); |
| 168 | if (!proc->IsAlive()) proc.reset(new PythonWorkerProcess(db_dir_)); |
| 169 | } |
| 170 | PythonWorkerProcess* ptr = proc.get(); |
| 171 | _busy_processes.insert(ptr); |
| 172 | // on failure, proc is killed and should be removed from busy processes |
| 173 | rollback.Reset([ptr, this]() { |
| 174 | std::lock_guard<std::mutex> l(_mtx); |
| 175 | _busy_processes.erase(ptr); |
| 176 | }); |
| 177 | } |
| 178 | |
| 179 | proc->ClearOutput(); |
| 180 | // write task |
| 181 | python_plugin::TaskInput task_input; |
| 182 | task_input.user = user; |
| 183 | task_input.graph = graph_name_; |
| 184 | task_input.plugin_dir = plugin_dir_; |
| 185 | task_input.function = function; |
| 186 | task_input.input = input; |
| 187 | task_input.read_only = read_only; |
| 188 | task_input.WriteToMessageQueue(proc->GetSendMQ()); |
| 189 | // wait for output |
| 190 | python_plugin::TaskOutput task_output; |
| 191 | double start_time = fma_common::GetTime(); |
| 192 | python_plugin::TaskOutput::ErrorCode ec; |
| 193 | while (true) { |
| 194 | // if master thread is killing this process |
| 195 | if (TaskTracker::GetInstance().ShouldKillCurrentTask() || proc->ShouldKill()) { |
| 196 | proc->Kill(); |
| 197 | std::lock_guard<std::mutex> l(_mtx); |
| 198 | _marked_processes.insert(std::move(proc)); |
| 199 | THROW_CODE(TaskKilled); |
| 200 | } |
| 201 | // if process failed, break |
| 202 | if (!proc->IsAlive()) { |
| 203 | output = proc->Stderr(); |
| 204 | THROW_CODE(InternalError, "Plugin failed unexpectly. Stderr:\n{}", output); |
| 205 | } |
| 206 | bool r = task_output.ReadFromMessageQueue(proc->GetRecvMQ(), 1000); |
| 207 | if (r) { |
| 208 | // done with the task |
nothing calls this directly
no test coverage detected