liblog: remove Rwlocks for logd_socket and pmsg_fd
These historically used atomics to manage their lifetime. They were
unfortunately unsafe and later replace with a RwLock. A lock is
also problematic as it is too heavy weight for the typical use case
and implies that logging is neither async nor fork safe.
This change returns us to using atomics with two key changes:
1) compare_exchange_strong() is used instead of atomic_exchange().
The latter has a race condition where a separate thread could have
read the atomic value into a register, while the thread performing
the atomic_exchange closes that FD. The new code only changes the
FD in the atomic if it is uninitialized.
2) Using the fact that DGRAM sockets can have connect() called on them
multiple times, it uses a single logd_socket for the duration of
the program.
These sockets are thread/async/fork safely created and accessed.
The one caveat is __android_log_close(), which is intended only to be
used by zygote when it is single threaded and is therefore not thread
safe. It will close this socket and reset the underlying variable,
such that the next log message will go through the above
initialization.
Bug: 65062446
Test: logging works, logging unit tests
Test: new unit test
Change-Id: Ia4dbf7479dbe50683d124558ab2f83bff53b8f5f
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 06e5e04..0751e2c 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -23,30 +23,36 @@
#include <sys/types.h>
#include <time.h>
-#include <shared_mutex>
-
#include <log/log_properties.h>
#include <private/android_logger.h>
#include "logger.h"
-#include "rwlock.h"
#include "uio.h"
-static int pmsg_fd;
-static RwLock pmsg_fd_lock;
+static atomic_int pmsg_fd;
-static void PmsgOpen() {
- auto lock = std::unique_lock{pmsg_fd_lock};
- if (pmsg_fd > 0) {
- // Someone raced us and opened the socket already.
+// pmsg_fd should only beopened once. If we see that pmsg_fd is uninitialized, we open "/dev/pmsg0"
+// then attempt to compare/exchange it into pmsg_fd. If the compare/exchange was successful, then
+// that will be the fd used for the duration of the program, otherwise a different thread has
+// already opened and written the fd to the atomic, so close the new fd and return.
+static void GetPmsgFd() {
+ if (pmsg_fd != 0) {
return;
}
- pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ if (new_fd <= 0) {
+ return;
+ }
+
+ int uninitialized_value = 0;
+ if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
+ close(new_fd);
+ return;
+ }
}
void PmsgClose() {
- auto lock = std::unique_lock{pmsg_fd_lock};
if (pmsg_fd > 0) {
close(pmsg_fd);
}
@@ -77,13 +83,7 @@
}
}
- auto lock = std::shared_lock{pmsg_fd_lock};
-
- if (pmsg_fd <= 0) {
- lock.unlock();
- PmsgOpen();
- lock.lock();
- }
+ GetPmsgFd();
if (pmsg_fd <= 0) {
return -EBADF;