From 6766f149f2778f1b37b094df2cf67cf5fcf02cf2 Mon Sep 17 00:00:00 2001 From: Martyn Gigg <martyn.gigg@gmail.com> Date: Wed, 11 Jul 2018 22:00:58 +0100 Subject: [PATCH] Add function to retrieve other process ids using psutil Refs #0 --- MantidPlot/CMakeLists.txt | 2 + MantidPlot/src/Process.cpp | 111 +++++++++++++++++++++++++++++++++++++ MantidPlot/src/Process.h | 36 ++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 MantidPlot/src/Process.cpp create mode 100644 MantidPlot/src/Process.h diff --git a/MantidPlot/CMakeLists.txt b/MantidPlot/CMakeLists.txt index 7ea2943c40f..b5b9670e1c5 100644 --- a/MantidPlot/CMakeLists.txt +++ b/MantidPlot/CMakeLists.txt @@ -91,6 +91,7 @@ set ( QTIPLOT_SRCS src/ApplicationWindow.cpp src/PluginFit.cpp src/PolynomFitDialog.cpp src/PolynomialFit.cpp + src/Process.cpp src/ProjectRecovery.cpp src/ProjectSaveView.cpp src/ProjectSerialiser.cpp @@ -291,6 +292,7 @@ set ( QTIPLOT_HDRS src/ApplicationWindow.h src/PluginFit.h src/PolynomFitDialog.h src/PolynomialFit.h + src/Process.h src/ProjectRecovery.h src/ProjectSerialiser.h src/ProjectSaveView.h diff --git a/MantidPlot/src/Process.cpp b/MantidPlot/src/Process.cpp new file mode 100644 index 00000000000..24be2e51167 --- /dev/null +++ b/MantidPlot/src/Process.cpp @@ -0,0 +1,111 @@ +// clang-format off +#include "MantidQtWidgets/Common/PythonThreading.h" +// clang-format on + +#include "Process.h" + +#include <iostream> +#include <stdexcept> +#include <QCoreApplication> + +namespace { + +class PyObjectNewReference { +public: + PyObjectNewReference(PyObject *object) : m_object(object) {} + ~PyObjectNewReference() { Py_XDECREF(m_object); } + + PyObjectNewReference(const PyObjectNewReference &) = delete; + PyObjectNewReference &operator=(const PyObjectNewReference &) = delete; + + PyObjectNewReference(PyObjectNewReference &&) = default; + PyObjectNewReference &operator=(PyObjectNewReference &&) = default; + + inline PyObject *ptr() const { return m_object; } + +private: + PyObject *m_object; +}; + +/** + * @brief Retrieve a named attribute + * @param source The source object + * @param name The name of the attribute + * @return The attribute + * @throws std::runtime_error if an error occurs retrieving the attribute + */ +PyObjectNewReference attr(PyObject *source, const char *name) { + PyObjectNewReference attr(PyObject_GetAttrString(source, name)); + if (attr.ptr()) { + return attr; + } else { + PyErr_Clear(); + throw std::runtime_error(std::string("Process: No attribute ") + name + + " found"); + } +} + +/** + * @brief Call a named function with an check for errors + * @param source The source object + * @param name The name of the attribute to call + * @return The return value of the function + * @throws std::runtime_error if an error occurs retrieving the attribute + */ +PyObjectNewReference call(PyObject *source, const char *name) { + auto result = PyObject_CallFunction(attr(source, name).ptr(), nullptr); + if (result) + return PyObjectNewReference(result); + else { + PyErr_Clear(); + throw std::runtime_error(std::string("Process: Error calling function ") + + name); + } +} + +/** + * @return Return a pointer to the psutil module. A new reference is returned. + */ +PyObjectNewReference psutil() { + if (auto process = PyImport_ImportModule("psutil")) { + return PyObjectNewReference(process); + } else { + PyErr_Clear(); + throw std::runtime_error("Python module psutil cannot be imported."); + } +} +} // namespace + +namespace Process { + +/** + * @brief Return a list of process IDs for other instances of this process. + * @return A list of other processes running. The PID for this process is + * removed from the list. An empty list is returned + * if no other processes are running. + * @throws std::runtime_error if the PID list cannot be determined + */ +std::vector<int64_t> otherInstancePIDs() { + ScopedPythonGIL lock; + const int64_t ourPID(QCoreApplication::applicationPid()); + const PyObjectNewReference ourName(PyString_FromString( + QCoreApplication::applicationName().toLatin1().data())); + auto psutilModule(psutil()); + auto processIter(call(psutilModule.ptr(), "process_iter")); + + std::vector<int64_t> otherPIDs; + PyObject *item(nullptr); + while ((item = PyIter_Next(processIter.ptr()))) { + auto name = call(item, "name"); + if (PyObject_RichCompareBool(name.ptr(), ourName.ptr(), Py_EQ)) { + auto pid = PyLong_AsLong(attr(item, "pid").ptr()); + if (pid != ourPID) { + otherPIDs.emplace_back(pid); + } + } + Py_DECREF(item); + } + return otherPIDs; +} + +} // namespace Process diff --git a/MantidPlot/src/Process.h b/MantidPlot/src/Process.h new file mode 100644 index 00000000000..e7740f35ac4 --- /dev/null +++ b/MantidPlot/src/Process.h @@ -0,0 +1,36 @@ +#ifndef PROCESS_H_ +#define PROCESS_H_ +/* +Copyright © 2008-18 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge +National Laboratory & European Spallation Source + +This file is part of Mantid. + +Mantid is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Mantid is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +File change history is stored at: <https://github.com/mantidproject/mantid> +Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +#include <cstdint> +#include <vector> + +/* + * A minimal wrapper around Python's psutil package to gather information + * about processes + */ + +namespace Process { +std::vector<int64_t> otherInstancePIDs(); +} +#endif // PROCESS_H_ -- GitLab