|  | // Copyright 2014 The Bazel Authors. All rights reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "src/main/native/unix_jni.h" | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <IOKit/IOMessage.h> | 
|  | #include <IOKit/pwr_mgt/IOPMLib.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/sysctl.h> | 
|  | #include <sys/syslimits.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/xattr.h> | 
|  |  | 
|  | #include <atomic> | 
|  | // Linting disabled for this line because for google code we could use | 
|  | // absl::Mutex but we cannot yet because Bazel doesn't depend on absl. | 
|  | #include <mutex>  // NOLINT | 
|  | #include <string> | 
|  |  | 
|  | namespace blaze_jni { | 
|  |  | 
|  | const int PATH_MAX2 = PATH_MAX * 2; | 
|  |  | 
|  | using std::string; | 
|  |  | 
|  | // See unix_jni.h. | 
|  | string ErrorMessage(int error_number) { | 
|  | char buf[1024] = ""; | 
|  | if (strerror_r(error_number, buf, sizeof buf) < 0) { | 
|  | snprintf(buf, sizeof buf, "strerror_r(%d): errno %d", error_number, errno); | 
|  | } | 
|  |  | 
|  | return string(buf); | 
|  | } | 
|  |  | 
|  |  | 
|  | int portable_fstatat( | 
|  | int dirfd, char *name, portable_stat_struct *statbuf, int flags) { | 
|  | char dirPath[PATH_MAX2];  // Have enough room for relative path | 
|  |  | 
|  | // No fstatat under darwin, simulate it | 
|  | if (flags != 0) { | 
|  | // We don't support any flags | 
|  | errno = ENOSYS; | 
|  | return -1; | 
|  | } | 
|  | if (strlen(name) == 0 || name[0] == '/') { | 
|  | // Absolute path, simply stat | 
|  | return portable_stat(name, statbuf); | 
|  | } | 
|  | // Relative path, construct an absolute path | 
|  | if (fcntl(dirfd, F_GETPATH, dirPath) == -1) { | 
|  | return -1; | 
|  | } | 
|  | int l = strlen(dirPath); | 
|  | if (dirPath[l-1] != '/') { | 
|  | // dirPath is twice the PATH_MAX size, we always have room for the extra / | 
|  | dirPath[l] = '/'; | 
|  | dirPath[l+1] = 0; | 
|  | l++; | 
|  | } | 
|  | strncat(dirPath, name, PATH_MAX2-l-1); | 
|  | char *newpath = realpath(dirPath, NULL);  // this resolve the relative path | 
|  | if (newpath == NULL) { | 
|  | return -1; | 
|  | } | 
|  | int r = portable_stat(newpath, statbuf); | 
|  | free(newpath); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | int StatSeconds(const portable_stat_struct &statbuf, StatTimes t) { | 
|  | switch (t) { | 
|  | case STAT_ATIME: | 
|  | return statbuf.st_atime; | 
|  | case STAT_CTIME: | 
|  | return statbuf.st_ctime; | 
|  | case STAT_MTIME: | 
|  | return statbuf.st_mtime; | 
|  | default: | 
|  | CHECK(false); | 
|  | } | 
|  | } | 
|  |  | 
|  | int StatNanoSeconds(const portable_stat_struct &statbuf, StatTimes t) { | 
|  | switch (t) { | 
|  | case STAT_ATIME: | 
|  | return statbuf.st_atimespec.tv_nsec; | 
|  | case STAT_CTIME: | 
|  | return statbuf.st_ctimespec.tv_nsec; | 
|  | case STAT_MTIME: | 
|  | return statbuf.st_mtimespec.tv_nsec; | 
|  | default: | 
|  | CHECK(false); | 
|  | } | 
|  | } | 
|  |  | 
|  | ssize_t portable_getxattr(const char *path, const char *name, void *value, | 
|  | size_t size, bool *attr_not_found) { | 
|  | ssize_t result = getxattr(path, name, value, size, 0, 0); | 
|  | *attr_not_found = (errno == ENOATTR); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | ssize_t portable_lgetxattr(const char *path, const char *name, void *value, | 
|  | size_t size, bool *attr_not_found) { | 
|  | ssize_t result = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); | 
|  | *attr_not_found = (errno == ENOATTR); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | int portable_sysctlbyname(const char *name_chars, long *mibp, size_t *sizep) { | 
|  | return sysctlbyname(name_chars, mibp, sizep, NULL, 0); | 
|  | } | 
|  |  | 
|  | // Protects all of the g_sleep_state_* statics. | 
|  | // value is "leaked" intentionally because std::mutex is not trivially | 
|  | // destructible at this time, g_sleep_state_mutex is a singleton, and | 
|  | // leaking it has no consequences. | 
|  | std::mutex *g_sleep_state_mutex = new std::mutex; | 
|  |  | 
|  | // Keep track of our pushes and pops of sleep state. | 
|  | static int g_sleep_state_stack = 0; | 
|  |  | 
|  | // Our assertion for disabling sleep. | 
|  | static IOPMAssertionID g_sleep_state_assertion = kIOPMNullAssertionID; | 
|  |  | 
|  | int portable_push_disable_sleep() { | 
|  | std::lock_guard<std::mutex> lock(*g_sleep_state_mutex); | 
|  | assert(g_sleep_state_stack >= 0); | 
|  | if (g_sleep_state_stack == 0) { | 
|  | assert(g_sleep_state_assertion == kIOPMNullAssertionID); | 
|  | CFStringRef reasonForActivity = CFSTR("build.bazel"); | 
|  |  | 
|  | IOReturn success = IOPMAssertionCreateWithName( | 
|  | kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity, | 
|  | &g_sleep_state_assertion); | 
|  | assert(success == kIOReturnSuccess); | 
|  | } | 
|  | g_sleep_state_stack += 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int portable_pop_disable_sleep() { | 
|  | std::lock_guard<std::mutex> lock(*g_sleep_state_mutex); | 
|  | assert(g_sleep_state_stack > 0); | 
|  | g_sleep_state_stack -= 1; | 
|  | if (g_sleep_state_stack == 0) { | 
|  | assert(g_sleep_state_assertion != kIOPMNullAssertionID); | 
|  | IOReturn success = IOPMAssertionRelease(g_sleep_state_assertion); | 
|  | assert(success == kIOReturnSuccess); | 
|  | g_sleep_state_assertion = kIOPMNullAssertionID; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | typedef struct { | 
|  | // Port used to relay sleep call back messages. | 
|  | io_connect_t connect_port; | 
|  |  | 
|  | // Count of suspensions. Atomic because it can be read from any java thread | 
|  | // and is written to from a dispatch_queue thread. | 
|  | std::atomic_int suspend_count; | 
|  | } SuspendState; | 
|  |  | 
|  | static void SleepCallBack(void *refcon, io_service_t service, | 
|  | natural_t message_type, void *message_argument) { | 
|  | SuspendState *state = (SuspendState *)refcon; | 
|  | switch (message_type) { | 
|  | case kIOMessageCanSystemSleep: | 
|  | // This needs to be handled to allow sleep. | 
|  | IOAllowPowerChange(state->connect_port, (intptr_t)message_argument); | 
|  | break; | 
|  |  | 
|  | case kIOMessageSystemWillSleep: | 
|  | ++state->suspend_count; | 
|  | // This needs to be acknowledged to allow sleep. | 
|  | IOAllowPowerChange(state->connect_port, (intptr_t)message_argument); | 
|  | break; | 
|  |  | 
|  | case kIOMessageSystemWillNotSleep: | 
|  | --state->suspend_count; | 
|  | break; | 
|  |  | 
|  | case kIOMessageSystemWillPowerOn: | 
|  | case kIOMessageSystemHasPoweredOn: | 
|  | // We increment g_suspend_count when we are alerted to the sleep as | 
|  | // opposed to when we wake up, because Macs have a "Dark Wake" mode (also | 
|  | // known as PowerNap) which is when the processors (and disk and network) | 
|  | // turn on for brief periods of time | 
|  | // (https://support.apple.com/en-us/HT204032). Dark Wake does NOT trigger | 
|  | // PowerOn messages through our sleep callbacks, but can allow | 
|  | // builds to proceed for a considerable amount of time (for example if | 
|  | // Time Machine is performing a back up). | 
|  | // There is currently a race condition where a build may finish | 
|  | // between the time we receive the kIOMessageSystemWillSleep and the | 
|  | // machine actually goes to sleep (roughly 20 seconds in my experiments) | 
|  | // or between the time we receive the kIOMessageSystemWillSleep and | 
|  | // kIOMessageSystemWillNotSleep. This will result in us reporting that the | 
|  | // build was suspended when it wasn't. I haven't come up with an smart way | 
|  | // of avoiding this issue, but I don't think we really care. Over | 
|  | // reporting "suspensions" is better than under reporting them. | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | int portable_suspend_count() { | 
|  | static dispatch_once_t once_token; | 
|  | static SuspendState suspend_state; | 
|  | dispatch_once(&once_token, ^{ | 
|  | IONotificationPortRef notifyPortRef; | 
|  | io_object_t notifierObject; | 
|  |  | 
|  | // Register to receive system sleep notifications. | 
|  | suspend_state.connect_port = IORegisterForSystemPower( | 
|  | &suspend_state, ¬ifyPortRef, SleepCallBack, ¬ifierObject); | 
|  | CHECK(suspend_state.connect_port != MACH_PORT_NULL); | 
|  | IONotificationPortSetDispatchQueue(notifyPortRef, | 
|  | DISPATCH_TARGET_QUEUE_DEFAULT); | 
|  |  | 
|  | // Register to deal with SIGCONT. | 
|  | // We register for SIGCONT because we can't catch SIGSTOP and we can't | 
|  | // distinguish a SIGCONT after a SIGSTOP from a SIGCONT after SIGTSTP. | 
|  | // We do have the potential of "over counting" suspensions if you send | 
|  | // multiple SIGCONTs to a process without a previous SIGSTOP/SIGTSTP, | 
|  | // but there is no reason to send a SIGCONT without a SIGSTOP/SIGTSTP, and | 
|  | // having this functionality gives us some ability to unit test suspension | 
|  | // counts. | 
|  | sig_t signal_val = signal(SIGCONT, SIG_IGN); | 
|  | CHECK(signal_val != SIG_ERR); | 
|  | dispatch_source_t signal_source = dispatch_source_create( | 
|  | DISPATCH_SOURCE_TYPE_SIGNAL, SIGCONT, 0, DISPATCH_TARGET_QUEUE_DEFAULT); | 
|  | CHECK(signal_source != NULL); | 
|  | dispatch_source_set_event_handler(signal_source, ^{ | 
|  | ++suspend_state.suspend_count; | 
|  | }); | 
|  | dispatch_resume(signal_source); | 
|  | }); | 
|  | return suspend_state.suspend_count; | 
|  | } | 
|  |  | 
|  | int portable_memory_pressure_warning_count() { | 
|  | static dispatch_once_t once_token; | 
|  | static std::atomic_int warning_count; | 
|  | dispatch_once(&once_token, ^{ | 
|  | dispatch_source_t source = dispatch_source_create( | 
|  | DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, DISPATCH_MEMORYPRESSURE_WARN, | 
|  | DISPATCH_TARGET_QUEUE_DEFAULT); | 
|  | CHECK(source != NULL); | 
|  | dispatch_source_set_event_handler(source, ^{ | 
|  | dispatch_source_memorypressure_flags_t pressureLevel = | 
|  | dispatch_source_get_data(source); | 
|  | if (pressureLevel == DISPATCH_MEMORYPRESSURE_WARN) { | 
|  | ++warning_count; | 
|  | } | 
|  | }); | 
|  | dispatch_resume(source); | 
|  | }); | 
|  | return warning_count; | 
|  | } | 
|  |  | 
|  | int portable_memory_pressure_critical_count() { | 
|  | static dispatch_once_t once_token; | 
|  | static std::atomic_int critical_count; | 
|  | dispatch_once(&once_token, ^{ | 
|  | dispatch_source_t source = dispatch_source_create( | 
|  | DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0, | 
|  | DISPATCH_MEMORYPRESSURE_CRITICAL, DISPATCH_TARGET_QUEUE_DEFAULT); | 
|  | CHECK(source != NULL); | 
|  | dispatch_source_set_event_handler(source, ^{ | 
|  | dispatch_source_memorypressure_flags_t pressureLevel = | 
|  | dispatch_source_get_data(source); | 
|  | if (pressureLevel == DISPATCH_MEMORYPRESSURE_CRITICAL) { | 
|  | ++critical_count; | 
|  | } | 
|  | }); | 
|  | dispatch_resume(source); | 
|  | }); | 
|  | return critical_count; | 
|  | } | 
|  |  | 
|  | }  // namespace blaze_jni |