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"}));

Reply via email to