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 
24 
25 #include <process/defer.hpp>
26 #include <process/future.hpp>
27 #include <process/reap.hpp>
28 #include <process/subprocess.hpp>
29 #include <process/process.hpp>
30 
31 #include <glog/logging.h> // For `CHECK` macro.
32 
33 
34 namespace process {
35 namespace internal {
36 
37 class JobObjectManager : public Process<JobObjectManager>
38 {
39 public:
40  JobObjectManager() : ProcessBase("__job_object_manager") {}
41  virtual ~JobObjectManager() {}
42 
43  void manage(
44  const pid_t pid,
45  const std::wstring& name,
46  const SharedHandle& handle)
47  {
48  jobs.emplace(pid, JobData{name, handle});
49 
50  process::reap(pid)
51  .onAny(defer(self(), &Self::cleanup, lambda::_1, pid));
52  }
53 
54 protected:
55  void cleanup(Future<Option<int>> exit_code, const pid_t pid)
56  {
57  CHECK(!exit_code.isPending());
58  CHECK(!exit_code.isDiscarded());
59 
60  Try<Nothing> killJobResult = os::kill_job(jobs.at(pid).handle);
61  CHECK(!killJobResult.isError())
62  << "Failed to kill job object: " << killJobResult.error();
63 
64  // Finally, erase the `JobData`, closing the last handle to the job object.
65  // All functionality requiring a live job object handle (but possibly a
66  // dead process) must happen prior to this, e.g. in a another parent hook.
67  jobs.erase(pid);
68  }
69 
70 private:
71  struct JobData {
72  std::wstring name;
73  SharedHandle handle;
74  };
75 
76  std::map<pid_t, JobData> jobs;
77 };
78 
79 // Global job object manager process. Defined in `process.cpp`.
81 
82 } // namespace internal {
83 
84 inline Subprocess::ParentHook Subprocess::ParentHook::CREATE_JOB() {
85  return Subprocess::ParentHook([](pid_t pid) -> Try<Nothing> {
86  // NOTE: There are two very important parts to this hook. First, Windows
87  // does not have a process hierarchy in the same sense that Unix does, so
88  // in order to be able to kill a task, we have to put it in a job object.
89  // Then, when we terminate the job object, it will terminate all the
90  // processes in the task (including any processes that were subsequently
91  // created by any process in this task). Second, the lifetime of the job
92  // object is greater than the lifetime of the processes it contains. Thus
93  // the job object handle is explicitly owned by the global job object
94  // manager process.
96  if (name.isError()) {
97  return Error(name.error());
98  }
99 
100  // This creates a named job object in the Windows kernel.
101  // This handle must remain in scope (and open) until
102  // a running process is assigned to it.
103  Try<SharedHandle> handle = os::create_job(name.get());
104  if (handle.isError()) {
105  return Error(handle.error());
106  }
107 
108  // This actually assigns the process `pid` to the job object.
109  Try<Nothing> result = os::assign_job(handle.get(), pid);
110  if (result.isError()) {
111  return Error(result.error());
112  }
113 
114  // Save the handle to the job object to ensure the object remains
115  // open for the entire lifetime of the agent process, and is closed
116  // when the process is reaped.
117  dispatch(
120  pid,
121  name.get(),
122  handle.get());
123 
124  return Nothing();
125  });
126 }
127 
128 } // namespace process {
129 
130 #endif // __PROCESS_WINDOWS_JOBOBJECT_HPP__
Definition: nothing.hpp:16
Definition: errorbase.hpp:36
T & get()&
Definition: try.hpp:80
Definition: windows.hpp:72
Definition: check.hpp:33
process::Future< bool > cleanup(const std::string &hierarchy)
void cleanup(Future< Option< int >> exit_code, const pid_t pid)
Definition: jobobject.hpp:55
Definition: process.hpp:72
Future< Option< int > > reap(pid_t pid)
Try< std::wstring > name_job(pid_t pid)
Definition: jobobject.hpp:39
Try< Nothing > assign_job(SharedHandle job_handle, pid_t pid)
Definition: jobobject.hpp:351
DWORD pid_t
Definition: windows.hpp:181
virtual ~JobObjectManager()
Definition: jobobject.hpp:41
Try< SharedHandle > create_job(const std::wstring &name)
Definition: jobobject.hpp:85
const Future< T > & onAny(AnyCallback &&callback) const
Definition: future.hpp:1442
Try< Nothing > kill_job(SharedHandle job_handle)
Definition: jobobject.hpp:381
Definition: jobobject.hpp:37
static Try error(const E &e)
Definition: try.hpp:43
void dispatch(const UPID &pid, std::unique_ptr< lambda::CallableOnce< void(ProcessBase *)>> f, const Option< const std::type_info * > &functionType=None())
A "process identifier" used to uniquely identify a process when dispatching messages.
Definition: pid.hpp:289
Definition: attributes.hpp:24
bool isError() const
Definition: try.hpp:78
Definition: executor.hpp:48
JobObjectManager()
Definition: jobobject.hpp:40
PID< JobObjectManager > job_object_manager
A hook can be passed to a subprocess call.
Definition: subprocess.hpp:152
Definition: process.hpp:505
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:43
constexpr const char * name
Definition: shell.hpp:43
Definition: future.hpp:58