summaryrefslogtreecommitdiff
path: root/luni/src/main/native/AsynchronousCloseMonitor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'luni/src/main/native/AsynchronousCloseMonitor.cpp')
-rw-r--r--luni/src/main/native/AsynchronousCloseMonitor.cpp162
1 files changed, 162 insertions, 0 deletions
diff --git a/luni/src/main/native/AsynchronousCloseMonitor.cpp b/luni/src/main/native/AsynchronousCloseMonitor.cpp
new file mode 100644
index 0000000000..7fea9367f5
--- /dev/null
+++ b/luni/src/main/native/AsynchronousCloseMonitor.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#define LOG_TAG "AsynchronousCloseMonitor"
+
+#include <log/log.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+
+#include <mutex>
+
+#include "AsynchronousCloseMonitor.h"
+
+namespace {
+
+class AsynchronousCloseMonitorImpl {
+public:
+ explicit AsynchronousCloseMonitorImpl(int fd);
+ ~AsynchronousCloseMonitorImpl();
+ bool wasSignaled() const;
+
+ static void init();
+
+ static void signalBlockedThreads(int fd);
+
+private:
+ AsynchronousCloseMonitorImpl(const AsynchronousCloseMonitorImpl&) = delete;
+ AsynchronousCloseMonitorImpl& operator=(const AsynchronousCloseMonitorImpl&) = delete;
+
+ AsynchronousCloseMonitorImpl* mPrev;
+ AsynchronousCloseMonitorImpl* mNext;
+ pthread_t mThread;
+ int mFd;
+ bool mSignaled;
+};
+
+/**
+ * We use an intrusive doubly-linked list to keep track of blocked threads.
+ * This gives us O(1) insertion and removal, and means we don't need to do any allocation.
+ * (The objects themselves are stack-allocated.)
+ * Waking potentially-blocked threads when a file descriptor is closed is O(n) in the total number
+ * of blocked threads (not the number of threads actually blocked on the file descriptor in
+ * question). For now at least, this seems like a good compromise for Android.
+ */
+static std::mutex blockedThreadListMutex;
+static AsynchronousCloseMonitorImpl* blockedThreadList = NULL;
+
+/**
+ * The specific signal chosen here is arbitrary, but bionic needs to know so that SIGRTMIN
+ * starts at a higher value.
+ */
+#if defined(__Fuchsia__)
+static const int BLOCKED_THREAD_SIGNAL = SIGRTMIN + 2;
+#else
+static const int BLOCKED_THREAD_SIGNAL = __SIGRTMIN + 2;
+#endif
+
+static void blockedThreadSignalHandler(int /*signal*/) {
+ // Do nothing. We only sent this signal for its side-effect of interrupting syscalls.
+}
+
+void AsynchronousCloseMonitorImpl::init() {
+ // Ensure that the signal we send interrupts system calls but doesn't kill threads.
+ // Using sigaction(2) lets us ensure that the SA_RESTART flag is not set.
+ // (The whole reason we're sending this signal is to unblock system calls!)
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = blockedThreadSignalHandler;
+ sa.sa_flags = 0;
+ int rc = sigaction(BLOCKED_THREAD_SIGNAL, &sa, NULL);
+ if (rc == -1) {
+ ALOGE("setting blocked thread signal handler failed: %s", strerror(errno));
+ }
+}
+
+void AsynchronousCloseMonitorImpl::signalBlockedThreads(int fd) {
+ std::lock_guard<std::mutex> lock(blockedThreadListMutex);
+ for (AsynchronousCloseMonitorImpl* it = blockedThreadList; it != NULL; it = it->mNext) {
+ if (it->mFd == fd) {
+ it->mSignaled = true;
+ pthread_kill(it->mThread, BLOCKED_THREAD_SIGNAL);
+ // Keep going, because there may be more than one thread...
+ }
+ }
+}
+
+bool AsynchronousCloseMonitorImpl::wasSignaled() const {
+ return mSignaled;
+}
+
+AsynchronousCloseMonitorImpl::AsynchronousCloseMonitorImpl(int fd) {
+ std::lock_guard<std::mutex> lock(blockedThreadListMutex);
+ // Who are we, and what are we waiting for?
+ mThread = pthread_self();
+ mFd = fd;
+ mSignaled = false;
+ // Insert ourselves at the head of the intrusive doubly-linked list...
+ mPrev = NULL;
+ mNext = blockedThreadList;
+ if (mNext != NULL) {
+ mNext->mPrev = this;
+ }
+ blockedThreadList = this;
+}
+
+AsynchronousCloseMonitorImpl::~AsynchronousCloseMonitorImpl() {
+ std::lock_guard<std::mutex> lock(blockedThreadListMutex);
+ // Unlink ourselves from the intrusive doubly-linked list...
+ if (mNext != NULL) {
+ mNext->mPrev = mPrev;
+ }
+ if (mPrev == NULL) {
+ blockedThreadList = mNext;
+ } else {
+ mPrev->mNext = mNext;
+ }
+}
+
+} // namespace
+
+//
+// C ABI and API boundary
+//
+
+extern "C" {
+void async_close_monitor_static_init() {
+ AsynchronousCloseMonitorImpl::init();
+}
+
+void async_close_monitor_signal_blocked_threads(int fd) {
+ AsynchronousCloseMonitorImpl::signalBlockedThreads(fd);
+}
+
+void* async_close_monitor_create(int fd) {
+ return new AsynchronousCloseMonitorImpl(fd);
+}
+
+void async_close_monitor_destroy(void* instance) {
+ auto monitor = reinterpret_cast<AsynchronousCloseMonitorImpl*>(instance);
+ delete monitor;
+}
+
+int async_close_monitor_was_signalled(const void* instance) {
+ auto monitor = reinterpret_cast<const AsynchronousCloseMonitorImpl*>(instance);
+ return monitor->wasSignaled() ? 1 : 0;
+}
+}