diff --git a/LiteCore/Support/Channel.hh b/LiteCore/Support/Channel.hh index 5ffba7606..9488d6d2e 100644 --- a/LiteCore/Support/Channel.hh +++ b/LiteCore/Support/Channel.hh @@ -28,7 +28,7 @@ // Set to 1 to have Actor objects track their calls through manifests to provide an // async stack trace on exception -#define ACTORS_USE_MANIFESTS 0 +#define ACTORS_USE_MANIFESTS 1 namespace litecore { namespace actor { diff --git a/LiteCore/Support/GCDMailbox.cc b/LiteCore/Support/GCDMailbox.cc index 2c3d2af57..a01e8b727 100644 --- a/LiteCore/Support/GCDMailbox.cc +++ b/LiteCore/Support/GCDMailbox.cc @@ -36,12 +36,18 @@ namespace litecore { namespace actor { static char kQueueMailboxSpecificKey; + static char kQueueManifestKey; - static const qos_class_t kQOS = QOS_CLASS_UTILITY; + static std::shared_ptr get_queue_manifest(dispatch_queue_t queue) { + auto* existing = (std::shared_ptr*)dispatch_queue_get_specific(queue, &kQueueManifestKey); + if(existing) { + return *existing; + } + + return make_shared(); + } -#if ACTORS_USE_MANIFESTS - thread_local shared_ptr GCDMailbox::sQueueManifest = nullptr; -#endif + static const qos_class_t kQOS = QOS_CLASS_UTILITY; GCDMailbox::GCDMailbox(Actor *a, const std::string &name, GCDMailbox *parentMailbox) :_actor(a) @@ -89,7 +95,8 @@ namespace litecore { namespace actor { #if ACTORS_USE_MANIFESTS stringstream manifest; manifest << "Queue Manifest History:" << endl; - sQueueManifest->dump(manifest); + auto queueManifest = get_queue_manifest(_queue); + queueManifest->dump(manifest); manifest << endl << "Actor Manifest History:" << endl; _localManifest.dump(manifest); const auto dumped = manifest.str(); @@ -105,7 +112,9 @@ namespace litecore { namespace actor { retain(_actor); #if ACTORS_USE_MANIFESTS - auto queueManifest = sQueueManifest ? sQueueManifest : make_shared(); + // Either queueManifest is set (see below inside of wrappedBlock) or this is a top + // level call to enqueue and a new queueManifest needs to be created + auto queueManifest = get_queue_manifest(_queue); queueManifest->addEnqueueCall(_actor, name); _localManifest.addEnqueueCall(_actor, name); #endif @@ -113,7 +122,10 @@ namespace litecore { namespace actor { auto wrappedBlock = ^{ #if ACTORS_USE_MANIFESTS queueManifest->addExecution(_actor, name); - sQueueManifest = queueManifest; + + // Set the captured queue manifest to be the current queue manifest so that + // any calls to enqueue inside of safelyCall() will use the same one (see above) + dispatch_queue_set_specific(_queue, &kQueueManifestKey, (void *)&queueManifest, nullptr); _localManifest.addExecution(_actor, name); #endif endLatency(); @@ -121,7 +133,7 @@ namespace litecore { namespace actor { safelyCall(block); afterEvent(); #if ACTORS_USE_MANIFESTS - sQueueManifest.reset(); + dispatch_queue_set_specific(_queue, &kQueueManifestKey, nullptr, nullptr); #endif }; dispatch_async(_queue, wrappedBlock); @@ -134,7 +146,9 @@ namespace litecore { namespace actor { retain(_actor); #if ACTORS_USE_MANIFESTS - auto queueManifest = sQueueManifest ? sQueueManifest : make_shared(); + // Either queueManifest is set (see below inside of wrappedBlock) or this is a top + // level call to enqueueAfter and a new queueManifest needs to be created + auto queueManifest = get_queue_manifest(_queue); queueManifest->addEnqueueCall(_actor, name, delay.count()); _localManifest.addEnqueueCall(_actor, name, delay.count()); #endif @@ -142,7 +156,10 @@ namespace litecore { namespace actor { auto wrappedBlock = ^{ #if ACTORS_USE_MANIFESTS queueManifest->addExecution(_actor, name); - sQueueManifest = queueManifest; + + // Set the captured queue manifest to be the queue manifest so that + // any calls to enqueue inside of safelyCall() will use the same one (see above) + dispatch_queue_set_specific(_queue, &kQueueManifestKey, (void *)&queueManifest, nullptr); _localManifest.addExecution(_actor, name); #endif endLatency(); @@ -150,7 +167,7 @@ namespace litecore { namespace actor { safelyCall(block); afterEvent(); #if ACTORS_USE_MANIFESTS - sQueueManifest.reset(); + dispatch_queue_set_specific(_queue, &kQueueManifestKey, nullptr, nullptr); #endif }; int64_t ns = std::chrono::duration_cast(delay).count(); diff --git a/LiteCore/Support/GCDMailbox.hh b/LiteCore/Support/GCDMailbox.hh index 72a53ff99..5fb2b66e0 100644 --- a/LiteCore/Support/GCDMailbox.hh +++ b/LiteCore/Support/GCDMailbox.hh @@ -60,7 +60,6 @@ namespace litecore { namespace actor { #if ACTORS_USE_MANIFESTS mutable ChannelManifest _localManifest; - static thread_local std::shared_ptr sQueueManifest; #endif #if ACTORS_TRACK_STATS diff --git a/LiteCore/Support/ThreadedMailbox.cc b/LiteCore/Support/ThreadedMailbox.cc index 3a5c62538..33b111892 100644 --- a/LiteCore/Support/ThreadedMailbox.cc +++ b/LiteCore/Support/ThreadedMailbox.cc @@ -142,12 +142,17 @@ namespace litecore { namespace actor { retain(_actor); #if ACTORS_USE_MANIFESTS + // Either sThreadManifest is set (see below inside of wrappedBlock) or this is a top + // level call to enqueue and a new threadManifest needs to be created auto threadManifest = sThreadManifest ? sThreadManifest : make_shared(); threadManifest->addEnqueueCall(_actor, name); _localManifest.addEnqueueCall(_actor, name); const auto wrappedBlock = [f, threadManifest, name, SELF] { threadManifest->addExecution(_actor, name); + + // Set the captured thread manifest to be the thread_local manifest so that + // any calls to enqueue inside of safelyCall() will use the same one (see above) sThreadManifest = threadManifest; _localManifest.addExecution(_actor, name); #else @@ -177,6 +182,8 @@ namespace litecore { namespace actor { retain(_actor); #if ACTORS_USE_MANIFESTS + // Either sThreadManifest is set (see below inside of wrappedBlock) or this is a top + // level call to enqueueAfter and a new threadManifest needs to be created auto threadManifest = sThreadManifest ? sThreadManifest : make_shared(); threadManifest->addEnqueueCall(_actor, name, delay.count()); _localManifest.addEnqueueCall(_actor, name, delay.count()); @@ -185,6 +192,9 @@ namespace litecore { namespace actor { const auto wrappedBlock = [f, threadManifest, name, SELF] { threadManifest->addExecution(_actor, name); + + // Set the captured thread manifest to be the thread_local manifest so that + // any calls to enqueue inside of safelyCall() will use the same one (see above) sThreadManifest = threadManifest; _localManifest.addExecution(_actor, name); #else diff --git a/Xcode/LiteCore.xcodeproj/project.pbxproj b/Xcode/LiteCore.xcodeproj/project.pbxproj index 375c4f610..3434092fe 100644 --- a/Xcode/LiteCore.xcodeproj/project.pbxproj +++ b/Xcode/LiteCore.xcodeproj/project.pbxproj @@ -495,6 +495,7 @@ 42030A412498445600283CE8 /* FilePath.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27E89BA41D679542002C32B3 /* FilePath.cc */; }; 42030A422498446000283CE8 /* LibC++Debug.cc in Sources */ = {isa = PBXBuildFile; fileRef = 27BF023C1FB61F5F003D5BB8 /* LibC++Debug.cc */; }; 42B6B0E225A6A9D9004B20A7 /* URLTransformer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 42B6B0E125A6A9D9004B20A7 /* URLTransformer.cc */; }; + 42E1561827BDD6CC000B6182 /* ChannelManifest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 274C1DC325C4A79C00B0EEAC /* ChannelManifest.cc */; }; 726F2B901EB2C36E00C1EC3C /* DefaultLogger.cc in Sources */ = {isa = PBXBuildFile; fileRef = 726F2B8F1EB2C36E00C1EC3C /* DefaultLogger.cc */; }; 728EC54D1EC14611002C9A73 /* c4Listener.h in Headers */ = {isa = PBXBuildFile; fileRef = 728EC54C1EC14611002C9A73 /* c4Listener.h */; }; 72A3AF891F424EC0001E16D4 /* PrebuiltCopier.cc in Sources */ = {isa = PBXBuildFile; fileRef = 72A3AF871F424EC0001E16D4 /* PrebuiltCopier.cc */; }; @@ -4295,6 +4296,7 @@ 273D25F62564666A008643D2 /* VectorDocument.cc in Sources */, 27CCD4AF2315DB11003DEB99 /* Address.cc in Sources */, 279976331E94AAD000B27639 /* IncomingRev+Blobs.cc in Sources */, + 42E1561827BDD6CC000B6182 /* ChannelManifest.cc in Sources */, 27DD1513193CD005009A367D /* RevID.cc in Sources */, 2734F61A206ABEB000C982FF /* ReplicatorTypes.cc in Sources */, 2753AF721EBD190600C12E98 /* LogDecoder.cc in Sources */,