Skip to content

Commit c8af268

Browse files
committed
Made PlaygroundLogger avoid recursive logging.
Added a thread-local `bool`, `PGLThreadIsLogging`, which is used in each of the logger entrypoints. This is defined in C as it's not possible (to my knowledge) to create a thread-local variable in Swift. Each of the logger entrypoints first checks this value; if it's `true`, then the function returns nil. If it's `false`, then the function sets it to `true`, defers setting it back to `false`, and then generates the log packet. This allows logging to complete successfully in cases when logging a value would cause that value to be logged (e.g. if the implementation of `CustomStringConvertible` or `CustomPlaygroundDisplayConvertible` caused `self` to be logged). This addresses <rdar://problem/41460357> / SR-8349.
1 parent f2e299a commit c8af268

File tree

6 files changed

+99
-5
lines changed

6 files changed

+99
-5
lines changed

PlaygroundLogger/PlaygroundLogger.xcodeproj/project.pbxproj

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@
9393
5EF6403E204148B80007BDD2 /* NSBezierPath+PGLKeyedArchivingUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF6403C204148B80007BDD2 /* NSBezierPath+PGLKeyedArchivingUtilities.m */; settings = {COMPILER_FLAGS = "-fobjc-arc-exceptions"; }; };
9494
5EF64041204149DE0007BDD2 /* UIBezierPath+PGLKeyedArchivingUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 5EF6403F204149DE0007BDD2 /* UIBezierPath+PGLKeyedArchivingUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
9595
5EF64042204149DE0007BDD2 /* UIBezierPath+PGLKeyedArchivingUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EF64040204149DE0007BDD2 /* UIBezierPath+PGLKeyedArchivingUtilities.m */; settings = {COMPILER_FLAGS = "-fobjc-arc-exceptions"; }; };
96+
5EFB2A5321211BC300BA43D3 /* PGLThreadIsLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = 5EFB2A5121211BC300BA43D3 /* PGLThreadIsLogging.h */; settings = {ATTRIBUTES = (Public, ); }; };
97+
5EFB2A5421211BC300BA43D3 /* PGLThreadIsLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EFB2A5221211BC300BA43D3 /* PGLThreadIsLogging.m */; };
9698
5EFE9193203F6CF900E21BAA /* LegacyPlaygroundLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ECE8F901FFCD2A70034D9BC /* LegacyPlaygroundLoggerTests.swift */; };
9799
5EFE919B203F6DD700E21BAA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFE919A203F6DD700E21BAA /* AppDelegate.swift */; };
98100
5EFE919D203F6DD700E21BAA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFE919C203F6DD700E21BAA /* ViewController.swift */; };
@@ -269,6 +271,8 @@
269271
5EF6403C204148B80007BDD2 /* NSBezierPath+PGLKeyedArchivingUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSBezierPath+PGLKeyedArchivingUtilities.m"; sourceTree = "<group>"; };
270272
5EF6403F204149DE0007BDD2 /* UIBezierPath+PGLKeyedArchivingUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIBezierPath+PGLKeyedArchivingUtilities.h"; sourceTree = "<group>"; };
271273
5EF64040204149DE0007BDD2 /* UIBezierPath+PGLKeyedArchivingUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIBezierPath+PGLKeyedArchivingUtilities.m"; sourceTree = "<group>"; };
274+
5EFB2A5121211BC300BA43D3 /* PGLThreadIsLogging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PGLThreadIsLogging.h; sourceTree = "<group>"; };
275+
5EFB2A5221211BC300BA43D3 /* PGLThreadIsLogging.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PGLThreadIsLogging.m; sourceTree = "<group>"; };
272276
5EFE9189203F6CC400E21BAA /* PlaygroundLoggerTests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PlaygroundLoggerTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
273277
5EFE918D203F6CC400E21BAA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
274278
5EFE9198203F6DD700E21BAA /* PlaygroundLoggerTestHost_iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PlaygroundLoggerTestHost_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -354,6 +358,7 @@
354358
5EFE91B1203F6E8D00E21BAA /* PlaygroundLoggerTestHost_tvOS */,
355359
5E11805820414E0C00B73EE9 /* Config Files */,
356360
5E2646281FB64876002DC6B6 /* Products */,
361+
5EFB2A352121145E00BA43D3 /* Frameworks */,
357362
);
358363
sourceTree = "<group>";
359364
};
@@ -599,10 +604,19 @@
599604
5E184716202BB80200F01AD1 /* PGLConcurrentMap.h */,
600605
5E184717202BB80200F01AD1 /* PGLConcurrentMap_MRR.m */,
601606
5E5FE50A202D13C800E28C3C /* PGLConcurrentMap.swift */,
607+
5EFB2A5121211BC300BA43D3 /* PGLThreadIsLogging.h */,
608+
5EFB2A5221211BC300BA43D3 /* PGLThreadIsLogging.m */,
602609
);
603610
path = Utilities;
604611
sourceTree = "<group>";
605612
};
613+
5EFB2A352121145E00BA43D3 /* Frameworks */ = {
614+
isa = PBXGroup;
615+
children = (
616+
);
617+
name = Frameworks;
618+
sourceTree = "<group>";
619+
};
606620
5EFE918A203F6CC400E21BAA /* PlaygroundLoggerTests_iOS */ = {
607621
isa = PBXGroup;
608622
children = (
@@ -651,6 +665,7 @@
651665
files = (
652666
5E184718202BB80200F01AD1 /* PGLConcurrentMap.h in Headers */,
653667
5EF64041204149DE0007BDD2 /* UIBezierPath+PGLKeyedArchivingUtilities.h in Headers */,
668+
5EFB2A5321211BC300BA43D3 /* PGLThreadIsLogging.h in Headers */,
654669
5EF6403D204148B80007BDD2 /* NSBezierPath+PGLKeyedArchivingUtilities.h in Headers */,
655670
5EF64039204145A80007BDD2 /* NSAttributedString+PGLKeyedArchivingUtilities.h in Headers */,
656671
5E2646381FB64876002DC6B6 /* PlaygroundLogger.h in Headers */,
@@ -778,7 +793,7 @@
778793
5E26461E1FB64876002DC6B6 /* Project object */ = {
779794
isa = PBXProject;
780795
attributes = {
781-
LastSwiftUpdateCheck = 0930;
796+
LastSwiftUpdateCheck = 1000;
782797
LastUpgradeCheck = 0910;
783798
ORGANIZATIONNAME = "Apple Inc. and the Swift project authors";
784799
TargetAttributes = {
@@ -952,6 +967,7 @@
952967
5E2755CF1FB657F200B69C83 /* LogEntry.swift in Sources */,
953968
5E2756221FC4873000B69C83 /* UInt64+TaggedOpaqueRepresentation.swift in Sources */,
954969
5E2756281FC4895100B69C83 /* Double+TaggedOpaqueRepresentation.swift in Sources */,
970+
5EFB2A5421211BC300BA43D3 /* PGLThreadIsLogging.m in Sources */,
955971
5E2756481FC4DE1500B69C83 /* NSView+OpaqueImageRepresentable.swift in Sources */,
956972
5E2755D31FB672DC00B69C83 /* SendData.swift in Sources */,
957973
5E27567E1FCF429900B69C83 /* NSCursor+CustomOpaqueLoggable.swift in Sources */,

PlaygroundLogger/PlaygroundLogger/LegacySupport/LegacyEntrypoints.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ fileprivate func legacySendDataStub(_: NSData) -> Void {
3535
}
3636

3737
@_silgen_name("playground_log_hidden")
38-
public func legacyLog<T>(instance: T, name: String, id: Int, startLine: Int, endLine: Int, startColumn: Int, endColumn: Int) -> AnyObject {
38+
public func legacyLog<T>(instance: T, name: String, id: Int, startLine: Int, endLine: Int, startColumn: Int, endColumn: Int) -> AnyObject? {
39+
guard !PGLThreadIsLogging else { return nil }
40+
PGLThreadIsLogging = true
41+
defer { PGLThreadIsLogging = false }
42+
3943
let packet = LogPacket(describingResult: instance, named: name, withPolicy: .default, startLine: startLine, endLine: endLine, startColumn: startColumn, endColumn: endColumn)
4044

4145
let data: Data
@@ -68,7 +72,11 @@ public func legacyLog<T>(instance: T, name: String, id: Int, startLine: Int, end
6872
}
6973

7074
@_silgen_name ("playground_log_scope_entry")
71-
public func legacyLogScopeEntry(startLine: Int, endLine: Int, startColumn: Int, endColumn: Int) -> AnyObject {
75+
public func legacyLogScopeEntry(startLine: Int, endLine: Int, startColumn: Int, endColumn: Int) -> AnyObject? {
76+
guard !PGLThreadIsLogging else { return nil }
77+
PGLThreadIsLogging = true
78+
defer { PGLThreadIsLogging = false }
79+
7280
let packet = LogPacket(scopeEntryWithStartLine: startLine, endLine: endLine, startColumn: startColumn, endColumn: endColumn)
7381

7482
// Encoding a scope entry packet should not fail under any circumstances.
@@ -78,7 +86,11 @@ public func legacyLogScopeEntry(startLine: Int, endLine: Int, startColumn: Int,
7886
}
7987

8088
@_silgen_name ("playground_log_scope_exit")
81-
public func legacyLogScopeExit(startLine: Int, endLine: Int, startColumn: Int, endColumn: Int) -> AnyObject {
89+
public func legacyLogScopeExit(startLine: Int, endLine: Int, startColumn: Int, endColumn: Int) -> AnyObject? {
90+
guard !PGLThreadIsLogging else { return nil }
91+
PGLThreadIsLogging = true
92+
defer { PGLThreadIsLogging = false }
93+
8294
let packet = LogPacket(scopeExitWithStartLine: startLine, endLine: endLine, startColumn: startColumn, endColumn: endColumn)
8395

8496
// Encoding a scope exit packet should not fail under any circumstances.
@@ -88,7 +100,11 @@ public func legacyLogScopeExit(startLine: Int, endLine: Int, startColumn: Int, e
88100
}
89101

90102
@_silgen_name ("playground_log_postprint")
91-
public func legacyLogPostPrint(startLine: Int, endLine: Int, startColumn: Int, endColumn: Int) -> AnyObject {
103+
public func legacyLogPostPrint(startLine: Int, endLine: Int, startColumn: Int, endColumn: Int) -> AnyObject? {
104+
guard !PGLThreadIsLogging else { return nil }
105+
PGLThreadIsLogging = true
106+
defer { PGLThreadIsLogging = false }
107+
92108
let printedString = Thread.current.threadDictionary[printedStringThreadDictionaryKey] as! String? ?? ""
93109

94110
Thread.current.threadDictionary.removeObject(forKey: printedStringThreadDictionaryKey)

PlaygroundLogger/PlaygroundLogger/LoggerEntrypoints.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ func logResult(_ result: Any,
1919
endLine: Int,
2020
startColumn: Int,
2121
endColumn: Int) {
22+
guard !PGLThreadIsLogging else { return }
23+
PGLThreadIsLogging = true
24+
defer { PGLThreadIsLogging = false }
25+
2226
let packet = LogPacket(describingResult: result, named: name, withPolicy: .default, startLine: startLine, endLine: endLine, startColumn: startColumn, endColumn: endColumn)
2327

2428
let data: Data
@@ -54,6 +58,10 @@ func logScopeEntry(startLine: Int,
5458
endLine: Int,
5559
startColumn: Int,
5660
endColumn: Int) {
61+
guard !PGLThreadIsLogging else { return }
62+
PGLThreadIsLogging = true
63+
defer { PGLThreadIsLogging = false }
64+
5765
let packet = LogPacket(scopeEntryWithStartLine: startLine, endLine: endLine, startColumn: startColumn, endColumn: endColumn)
5866

5967
// Encoding a scope entry packet should not fail under any circumstances.
@@ -66,6 +74,10 @@ func logScopeExit(startLine: Int,
6674
endLine: Int,
6775
startColumn: Int,
6876
endColumn: Int) {
77+
guard !PGLThreadIsLogging else { return }
78+
PGLThreadIsLogging = true
79+
defer { PGLThreadIsLogging = false }
80+
6981
let packet = LogPacket(scopeExitWithStartLine: startLine, endLine: endLine, startColumn: startColumn, endColumn: endColumn)
7082

7183
// Encoding a scope exit packet should not fail under any circumstances.
@@ -77,13 +89,20 @@ func logScopeExit(startLine: Int,
7789
let printedStringThreadDictionaryKey: NSString = "org.swift.PlaygroundLogger.printedString"
7890

7991
func printHook(string: String) {
92+
// Don't store the printed string if we're already logging elsewhere in this thread.
93+
guard !PGLThreadIsLogging else { return }
94+
8095
Thread.current.threadDictionary[printedStringThreadDictionaryKey] = string as NSString
8196
}
8297

8398
func logPostPrint(startLine: Int,
8499
endLine: Int,
85100
startColumn: Int,
86101
endColumn: Int) {
102+
guard !PGLThreadIsLogging else { return }
103+
PGLThreadIsLogging = true
104+
defer { PGLThreadIsLogging = false }
105+
87106
guard let printedString = Thread.current.threadDictionary[printedStringThreadDictionaryKey] as! String? else {
88107
return
89108
}

PlaygroundLogger/PlaygroundLogger/PlaygroundLogger.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ FOUNDATION_EXPORT double PlaygroundLoggerVersionNumber;
1919
FOUNDATION_EXPORT const unsigned char PlaygroundLoggerVersionString[];
2020

2121
#import <PlaygroundLogger/PGLConcurrentMap.h>
22+
#import <PlaygroundLogger/PGLThreadIsLogging.h>
2223

2324
#import <PlaygroundLogger/NSAttributedString+PGLKeyedArchivingUtilities.h>
2425
#import <PlaygroundLogger/NSBezierPath+PGLKeyedArchivingUtilities.h>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===--- PGLThreadIsLogging.h ---------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#import <sys/cdefs.h>
14+
#import <stdbool.h>
15+
16+
__BEGIN_DECLS
17+
18+
/// A thread-local `bool` which indicates whether or not the current thread is
19+
/// logging.
20+
///
21+
/// This is used by the functions in LoggerEntrypoints.swift and
22+
/// LegacyEntrypoints.swift to prevent generating log packets while in already
23+
/// generating a log packet. It means the side-effects of logging are not
24+
/// themselves logged.
25+
extern __thread bool PGLThreadIsLogging;
26+
27+
__END_DECLS
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//===--- PGLThreadIsLogging.m ---------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#import <PlaygroundLogger/PGLThreadIsLogging.h>
14+
15+
__thread bool PGLThreadIsLogging = false;

0 commit comments

Comments
 (0)