Repository navigation
iOS,macOS: make Logger thread-safe, conform to Sendable - #190488
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces thread safety to the Darwin platform Logger by conforming LogLevel, Logger, and OutputWriter types to Sendable (or @unchecked Sendable) and protecting shared state with an NSLock. It also adds a concurrent test to verify behavior under TSan. Feedback on the changes highlights compatibility issues with older iOS and macOS deployment targets due to the use of lock.withLock (which requires iOS 16.0+ / macOS 13.0+), recommending manual lock() and unlock() calls instead. Additionally, the review points out a potential data race in StringOutputWriter where mutable properties are accessed concurrently in tests without synchronization.
118a193 to
16c52a0
Compare
Previously, `Logger.shared` was a mutable static variable, and updating `Logger.logLevel` or `Logger.outputWriter` replaced the entire singleton instance without any synchronisation. This could cause data races when logging from multiple threads, and prevented `Logger` from conforming to `Sendable` under strict Swift concurrency. We now make `shared` an immutable constant and guard its mutable `logLevel` and `outputWriter` state behind a lock. The caller-supplied message autoclosure is evaluated outside the lock, both to keep the lock scope minimal and to avoid deadlocking should the closure re-enter `Logger` since the lock is not reentrant. Issue: flutter#44030
16c52a0 to
9bb1ba1
Compare
| // someone accidentally calling the Logger from within the autoclosure. | ||
| let line = message() | ||
| lock.withLock { | ||
| _outputWriter.writeLine(level: level, line) |
There was a problem hiding this comment.
It looks like writeLine calls are always protected by a NSLock in Logger, but the OutputWriter.writeLine implementation typically still have be thread-safe because it can be used outside of Logger.
I feel it makes a little bit more sense to make it OutputWriter's responsibility to make sure its writeLine implementation is thread-safe (which is already the case since OutputWriter must be Sendable and writeLine is nonisolated), instead of trying to guarantee thread-safety on both ends, so:
lock.withLock { _outputWriter }.writeLine(level: level, message())A potential issue with the current implementation is that in case the output writer does something weird in writeLine (like accessing the Logger) it looks possible to cause a deadlock since the lock is not recursive.
There was a problem hiding this comment.
In our case OutputWriter is our own internal protocol and both implementations are at the bottom of this file and don't use locks so we're safe. I've got a followup that replaces this altogether with os_log which will solve it altogether though!
…12420) Manual roll requested by stuartmorgan@google.com flutter/flutter@b766512...27b0988 2026-08-05 kevmoo@users.noreply.github.com reland(tool): remove redundant --enable-experiment=record-use flag (flutter/flutter#190591) 2026-08-05 chris@bracken.jp Windows: Propagate enabled accessibility state (flutter/flutter#190507) 2026-08-05 engine-flutter-autoroll@skia.org Roll Fuchsia Test Scripts from ltbuIH9Z3T_yOuigu... to vcANVO8VIDQHasH1X... (flutter/flutter#190589) 2026-08-05 256906086+mvincentong@users.noreply.github.com Document frozen embedder API structs (flutter/flutter#186842) 2026-08-05 49402500+fahaddoc@users.noreply.github.com Document super call order for State.didChangeDependencies (flutter/flutter#185945) 2026-08-05 dkwingsmt@users.noreply.github.com Move examples of `flutter/widgets` widgets out from `flutter/material` (flutter/flutter#189532) 2026-08-05 93888664+ColeSpringer@users.noreply.github.com [web] Use thread local strike caches in skwasm (flutter/flutter#190048) 2026-08-05 43089218+chika3742@users.noreply.github.com doc: fix typo in see also section for PrimaryScrollController.maybeOf (flutter/flutter#190386) 2026-08-05 jason-simmons@users.noreply.github.com Migrate the shell unit tests from legacy Dart native functions to FFI (flutter/flutter#190473) 2026-08-05 47866232+chunhtai@users.noreply.github.com render proxy box now defaults baseline calculation to null (flutter/flutter#190269) 2026-08-05 36861262+QuncCccccc@users.noreply.github.com Update Widgets Localizations from Translation Console (flutter/flutter#190503) 2026-08-04 154381524+flutteractionsbot@users.noreply.github.com Revert: fix(tool): remove redundant --enable-experiment=record-use flag (flutter/flutter#190583) 2026-08-04 engine-flutter-autoroll@skia.org Roll Fuchsia Test Scripts from 1frGe_KltAJKkeyPg... to ltbuIH9Z3T_yOuigu... (flutter/flutter#190561) 2026-08-04 chris@bracken.jp iOS,macOS: add tsan and ubsan support for Swift (flutter/flutter#190497) 2026-08-04 34465683+rkishan516@users.noreply.github.com fix: update on_message_ to nullptr after window destroy so that dart gets destroy message (flutter/flutter#185807) 2026-08-04 bkonyi@google.com [flutter_tools] Gracefully handle locked Windows files during clean (flutter/flutter#190095) 2026-08-04 chris@bracken.jp iOS,macOS: make Logger thread-safe, conform to Sendable (flutter/flutter#190488) 2026-08-04 chris@bracken.jp iOS: Eliminate use of IOSContextNoop in platform view tests (reland) (flutter/flutter#190509) 2026-08-04 kevmoo@users.noreply.github.com fix(tool): remove redundant --enable-experiment=record-use flag (flutter/flutter#190475) 2026-08-04 awolff@google.com android_hardware_smoke_test: Detect blank image failures or EGL initialization warnings and retry (flutter/flutter#190110) 2026-08-04 30870216+gaaclarke@users.noreply.github.com Bumps text gamma on windows to match skia. (flutter/flutter#190477) 2026-08-04 engine-flutter-autoroll@skia.org Roll Skia from 48b58ee222f1 to a8583a0a2c11 (2 revisions) (flutter/flutter#190537) 2026-08-04 kevmoo@users.noreply.github.com [tool][web] Intercept dart2wasm errors & append JS migration footers (flutter/flutter#190476) 2026-08-04 dacoharkes@google.com [record_use] Migrate IconTreeShaker to `package:record_use` (flutter/flutter#190225) 2026-08-04 s4bre.py@gmail.com Handle unexpected exceptions during Azure metadata detection (flutter/flutter#189457) 2026-08-04 15619084+vashworth@users.noreply.github.com Fix merge conflict from flutter/flutter#190369 (flutter/flutter#190544) 2026-08-04 15619084+vashworth@users.noreply.github.com Prepare device support symbols (flutter/flutter#190369) 2026-08-04 engine-flutter-autoroll@skia.org Roll Packages from ac87e65 to 3498b9d (1 revision) (flutter/flutter#190532) 2026-08-04 engine-flutter-autoroll@skia.org Roll Skia from a08d918ebd6a to 48b58ee222f1 (11 revisions) (flutter/flutter#190527) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Previously,
Logger.sharedwas a mutable static variable, and updatingLogger.logLevelorLogger.outputWriterreplaced the entire singleton instance without any synchronisation. This could cause data races when logging from multiple threads, and preventedLoggerfrom conforming toSendableunder strict Swift concurrency.We now make
sharedan immutable constant and guard its mutablelogLevelandoutputWriterstate behind a lock.The caller-supplied message autoclosure is evaluated outside the lock, both to keep the lock scope minimal and to avoid deadlocking should the closure re-enter
Loggersince the lock is not reentrant.Issue: #44030
Issue: #181683
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.