This function will increase the memory footprint gradually. `TaskInfo.data` specifies the upper limit (in MB) of the memory footprint. The upper limit can be larger than the amount of memory allocated to this executor. In that case, the isolator (e.g. cgroups) may detect that and destroy the container before the executor can sent a TASK_FINISHED update.
| 85 | // may detect that and destroy the container before the executor can |
| 86 | // sent a TASK_FINISHED update. |
| 87 | void run(ExecutorDriver* driver, const TaskInfo& task) |
| 88 | { |
| 89 | TaskStatus status; |
| 90 | status.mutable_task_id()->MergeFrom(task.task_id()); |
| 91 | status.set_state(TASK_RUNNING); |
| 92 | |
| 93 | driver->sendStatusUpdate(status); |
| 94 | |
| 95 | // Get the balloon limit (in MB). |
| 96 | Try<Bytes> _limit = Bytes::parse(task.data()); |
| 97 | CHECK(_limit.isSome()); |
| 98 | const size_t limit = _limit->bytes() / Bytes::MEGABYTES; |
| 99 | |
| 100 | // On systems with swap partitions we explicitly prevent memory used by the |
| 101 | // executor from being swapped out with `mlock`. Since the amount of memory a |
| 102 | // process can lock is controlled by an rlimit, we only `mlock` when |
| 103 | // strictly necessary to prevent complicating the test setup. |
| 104 | Try<bool> hasSwap_ = hasSwap(); |
| 105 | const bool lockMemory = hasSwap_.isError() || hasSwap_.get(); |
| 106 | |
| 107 | if (lockMemory) { |
| 108 | LOG(INFO) |
| 109 | << "System might use swap partitions, will explicitly" |
| 110 | << " lock memory to prevent swapping"; |
| 111 | } |
| 112 | |
| 113 | const size_t chunk = BALLOON_STEP_MB * 1024 * 1024; |
| 114 | for (size_t i = 0; i < limit / BALLOON_STEP_MB; i++) { |
| 115 | LOG(INFO) |
| 116 | << "Increasing memory footprint by " << BALLOON_STEP_MB << " MB"; |
| 117 | |
| 118 | // Allocate page-aligned virtual memory. |
| 119 | void* buffer = nullptr; |
| 120 | if (posix_memalign(&buffer, os::pagesize(), chunk) != 0) { |
| 121 | LOG(FATAL) << ErrnoError( |
| 122 | "Failed to allocate page-aligned memory, posix_memalign").message; |
| 123 | } |
| 124 | |
| 125 | // We use `memset` and possibly `mlock` here to make sure that the |
| 126 | // memory actually gets paged in and thus accounted for. |
| 127 | if (memset(buffer, 1, chunk) != buffer) { |
| 128 | LOG(FATAL) << ErrnoError("Failed to fill memory, memset").message; |
| 129 | } |
| 130 | |
| 131 | if (lockMemory) { |
| 132 | if (mlock(buffer, chunk) != 0) { |
| 133 | LOG(FATAL) << ErrnoError("Failed to lock memory, mlock").message; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | // Try not to increase the memory footprint too fast. |
| 138 | os::sleep(Seconds(1)); |
| 139 | } |
| 140 | |
| 141 | LOG(INFO) << "Finishing task " << task.task_id().value(); |
| 142 | |
| 143 | status.set_state(TASK_FINISHED); |
| 144 |