This is an automated email from the ASF dual-hosted git repository.
bmahler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git
The following commit(s) were added to refs/heads/master by this push:
new f249bf065 [cgroups2] Introduce interface for reading threads in a
cgroup.
f249bf065 is described below
commit f249bf06590d8f54c73b57ef2dad51093aade18d
Author: Devin Leamy <[email protected]>
AuthorDate: Wed Apr 3 12:10:56 2024 -0400
[cgroups2] Introduce interface for reading threads in a cgroup.
- `cgroups2::threads` reads the threads in a cgroup into a `set`,
similar to `cgroups2::processes`.
Moving a process into a cgroup moves all the threads in that
process into the cgroup. A test is introduced to ensure the
threads move, as expected.
This closes #546
---
src/linux/cgroups2.cpp | 24 ++++++++++++
src/linux/cgroups2.hpp | 4 ++
src/tests/containerizer/cgroups2_tests.cpp | 59 ++++++++++++++++++++++++++++++
3 files changed, 87 insertions(+)
diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp
index 27ef3a141..fa74e88a0 100644
--- a/src/linux/cgroups2.cpp
+++ b/src/linux/cgroups2.cpp
@@ -407,6 +407,30 @@ Try<set<pid_t>> processes(const string& cgroup)
}
+Try<set<pid_t>> threads(const string& cgroup)
+{
+ Try<string> contents = cgroups2::read<string>(cgroup, control::THREADS);
+ if (contents.isError()) {
+ return Error("Failed to read 'cgroup.threads' in"
+ " '" + cgroup + "': " + contents.error());
+ }
+
+ set<pid_t> tids;
+ foreach (const string& line, strings::split(*contents, "\n")) {
+ if (line.empty()) continue;
+
+ Try<pid_t> tid = numify<pid_t>(line);
+ if (tid.isError()) {
+ return Error("Failed to parse '" + line + "' as a tid: " + tid.error());
+ }
+
+ tids.insert(*tid);
+ }
+
+ return tids;
+}
+
+
string path(const string& cgroup)
{
return path::join(cgroups2::MOUNT_POINT, cgroup);
diff --git a/src/linux/cgroups2.hpp b/src/linux/cgroups2.hpp
index 17b9b1af8..cab679428 100644
--- a/src/linux/cgroups2.hpp
+++ b/src/linux/cgroups2.hpp
@@ -84,6 +84,10 @@ Try<std::string> cgroup(pid_t pid);
Try<std::set<pid_t>> processes(const std::string& cgroup);
+// Get the threads inside of a cgroup.
+Try<std::set<pid_t>> threads(const std::string& cgroup);
+
+
// Get the absolute of a cgroup. The cgroup provided should not start with '/'.
std::string path(const std::string& cgroup);
diff --git a/src/tests/containerizer/cgroups2_tests.cpp
b/src/tests/containerizer/cgroups2_tests.cpp
index b3d9dff26..b05b656cd 100644
--- a/src/tests/containerizer/cgroups2_tests.cpp
+++ b/src/tests/containerizer/cgroups2_tests.cpp
@@ -14,8 +14,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <memory>
#include <set>
#include <string>
+#include <thread>
#include <tuple>
#include <utility>
#include <vector>
@@ -38,7 +40,9 @@
using std::pair;
using std::set;
using std::string;
+using std::thread;
using std::tuple;
+using std::unique_ptr;
using std::vector;
namespace cpu = cgroups2::cpu;
@@ -181,6 +185,61 @@ TEST_F(Cgroups2Test, ROOT_CGROUPS2_AssignProcesses)
}
+TEST_F(Cgroups2Test, ROOT_CGROUPS2_Threads)
+{
+ const size_t NUM_THREADS = 5;
+
+ ASSERT_SOME(cgroups2::create(TEST_CGROUP));
+
+ pid_t pid = ::fork();
+ ASSERT_NE(-1, pid);
+
+ if (pid == 0) {
+ vector<unique_ptr<std::thread>> runningThreads(NUM_THREADS);
+
+ for (size_t i = 0; i < NUM_THREADS; ++i) {
+ runningThreads[i] = unique_ptr<std::thread>(new std::thread([]() {
+ os::sleep(Seconds(10));
+ }));
+ }
+
+ // Check the test cgroup is initially empty.
+ Try<set<pid_t>> cgroupThreads = cgroups2::threads(TEST_CGROUP);
+ if (cgroupThreads.isError()) {
+ SAFE_EXIT(EXIT_FAILURE, "Failed to read threads in the cgroup");
+ }
+ if (!cgroupThreads->empty()) {
+ SAFE_EXIT(EXIT_FAILURE, "Expected no threads in the cgroup");
+ }
+
+ // Assign ourselves to the test cgroup.
+ if (cgroups2::assign(TEST_CGROUP, ::getpid()).isError()) {
+ SAFE_EXIT(EXIT_FAILURE, "Failed to assign to the test cgroup");
+ }
+
+ Try<set<pid_t>> threads = proc::threads(::getpid());
+ if (threads.isError()) {
+ SAFE_EXIT(EXIT_FAILURE, "Failed to read threads");
+ }
+
+ // Check that the test cgroup now only contains all child threads.
+ cgroupThreads = cgroups2::threads(TEST_CGROUP);
+ if (cgroupThreads.isError()) {
+ SAFE_EXIT(EXIT_FAILURE, "Failed to read threads in the cgroup");
+ }
+ if (*threads != *cgroupThreads) {
+ SAFE_EXIT(
+ EXIT_FAILURE,
+ "The threads in the cgroup did not match the processes's threads");
+ }
+
+ ::_exit(EXIT_SUCCESS);
+ }
+
+ AWAIT_EXPECT_WEXITSTATUS_EQ(EXIT_SUCCESS, process::reap(pid));
+}
+
+
TEST_F(Cgroups2Test, ROOT_CGROUPS2_CpuStats)
{
ASSERT_SOME(enable_controllers({"cpu"}));