Apache Mesos
jobobject.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License
12 
13 #ifndef __PROCESS_WINDOWS_JOBOBJECT_HPP__
14 #define __PROCESS_WINDOWS_JOBOBJECT_HPP__
15 
16 #include <map>
17 #include <string>
18 
19 #include <stout/lambda.hpp>
20 #include <stout/nothing.hpp>
21 #include <stout/windows.hpp> // For `SharedHandle`.
22 #include <stout/windows/os.hpp> // For `os` namespace.
23 
24 #include <process/defer.hpp>
25 #include <process/future.hpp>
26 #include <process/reap.hpp>
27 #include <process/subprocess.hpp>
28 #include <process/process.hpp>
29 
30 #include <glog/logging.h> // For `CHECK` macro.
31 
32 
33 namespace process {
34 namespace internal {
35 
36 class JobObjectManager : public Process<JobObjectManager>
37 {
38 public:
39  JobObjectManager() : ProcessBase("__job_object_manager") {}
40  virtual ~JobObjectManager() {}
41 
42  void manage(
43  const pid_t pid,
44  const std::wstring& name,
45  const SharedHandle& handle)
46  {
47  jobs.emplace(pid, JobData{name, handle});
48 
49  process::reap(pid)
50  .onAny(defer(self(), &Self::cleanup, lambda::_1, pid));
51  }
52 
53 protected:
54  void cleanup(Future<Option<int>> exit_code, const pid_t pid)
55  {
56  CHECK(!exit_code.isPending());
57  CHECK(!exit_code.isDiscarded());
58 
59  Try<Nothing> killJobResult = os::kill_job(jobs.at(pid).handle);
60  CHECK(!killJobResult.isError())
61  << "Failed to kill job object: " << killJobResult.error();
62 
63  // Finally, erase the `JobData`, closing the last handle to the job object.
64  // All functionality requiring a live job object handle (but possibly a
65  // dead process) must happen prior to this, e.g. in a another parent hook.
66  jobs.erase(pid);
67  }
68 
69 private:
70  struct JobData {
71  std::wstring name;
72  SharedHandle handle;
73  };
74 
75  std::map<pid_t, JobData> jobs;
76 };
77 
78 // Global job object manager process. Defined in `process.cpp`.
79 extern PID<JobObjectManager> job_object_manager;
80 
81 } // namespace internal {
82 
83 inline Subprocess::ParentHook Subprocess::ParentHook::CREATE_JOB() {
84  return Subprocess::ParentHook([](pid_t pid) -> Try<Nothing> {
85  // NOTE: There are two very important parts to this hook. First, Windows
86  // does not have a process hierarchy in the same sense that Unix does, so
87  // in order to be able to kill a task, we have to put it in a job object.
88  // Then, when we terminate the job object, it will terminate all the
89  // processes in the task (including any processes that were subsequently
90  // created by any process in this task). Second, the lifetime of the job
91  // object is greater than the lifetime of the processes it contains. Thus
92  // the job object handle is explicitly owned by the global job object
93  // manager process.
95  if (name.isError()) {
96  return Error(name.error());
97  }
98 
99  // This creates a named job object in the Windows kernel.
100  // This handle must remain in scope (and open) until
101  // a running process is assigned to it.
102  Try<SharedHandle> handle = os::create_job(name.get());
103  if (handle.isError()) {
104  return Error(handle.error());
105  }
106 
107  // This actually assigns the process `pid` to the job object.
108  Try<Nothing> result = os::assign_job(handle.get(), pid);
109  if (result.isError()) {
110  return Error(result.error());
111  }
112 
113  // Save the handle to the job object to ensure the object remains
114  // open for the entire lifetime of the agent process, and is closed
115  // when the process is reaped.
116  dispatch(
119  pid,
120  name.get(),
121  handle.get());
122 
123  return Nothing();
124  });
125 }
126 
127 } // namespace process {
128 
129 #endif // __PROCESS_WINDOWS_JOBOBJECT_HPP__
Definition: nothing.hpp:16
Definition: errorbase.hpp:35
Definition: windows.hpp:78
Definition: try.hpp:34
process::Future< bool > cleanup(const std::string &hierarchy)
void cleanup(Future< Option< int >> exit_code, const pid_t pid)
Definition: jobobject.hpp:54
pid_t pid() const
Definition: subprocess.hpp:241
Definition: process.hpp:72
Future< Option< int > > reap(pid_t pid)
Try< std::wstring > name_job(pid_t pid)
Definition: os.hpp:549
Try< Nothing > assign_job(SharedHandle job_handle, pid_t pid)
Definition: os.hpp:864
void dispatch(const PID< T > &pid, void(T::*method)())
Definition: dispatch.hpp:174
DWORD pid_t
Definition: windows.hpp:187
virtual ~JobObjectManager()
Definition: jobobject.hpp:40
Try< SharedHandle > create_job(const std::wstring &name)
Definition: os.hpp:605
const Future< T > & onAny(AnyCallback &&callback) const
Definition: future.hpp:1458
Try< Nothing > kill_job(SharedHandle job_handle)
Definition: os.hpp:895
Definition: jobobject.hpp:36
static Try error(const E &e)
Definition: try.hpp:42
Result< Process > process(pid_t pid)
Definition: freebsd.hpp:30
bool isError() const
Definition: try.hpp:71
JobObjectManager()
Definition: jobobject.hpp:39
PID< JobObjectManager > job_object_manager
Definition: process.hpp:493
Deferred< void()> defer(const PID< T > &pid, void(T::*method)())
Definition: defer.hpp:35
void manage(const pid_t pid, const std::wstring &name, const SharedHandle &handle)
Definition: jobobject.hpp:42
const T & get() const
Definition: try.hpp:73
constexpr const char * name
Definition: shell.hpp:41
Definition: future.hpp:57