]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_transform/lib/Monitor.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_transform / lib / Monitor.cpp
diff --git a/Security/libsecurity_transform/lib/Monitor.cpp b/Security/libsecurity_transform/lib/Monitor.cpp
new file mode 100644 (file)
index 0000000..2b9958d
--- /dev/null
@@ -0,0 +1,108 @@
+#include "Monitor.h"
+#include <Block.h>
+#include "misc.h"
+#include "GroupTransform.h"
+#include "Utilities.h"
+
+void Monitor::Wait()
+{
+}
+
+
+
+bool Monitor::IsExternalizable()
+{
+       return false; // monitors aren't really part of the transform
+}
+
+void BlockMonitor::AttributeChanged(CFStringRef name, CFTypeRef value)
+{
+       // deliver the attribute to the queue
+       CFTypeRef realValue = value;
+       CFErrorRef error = NULL;
+       bool isFinal = false;
+       
+       if (mSeenFinal)
+       {
+               // A NULL and CFErrorRef might both be enqueued already, and the 2nd can race the teardown.   Without this check we would trigger final processing
+               // more then once resulting in our own overlease issues, and could well cause our client to make similar errors.
+               return;
+       }
+
+       if (realValue != NULL)
+       {
+               // do some basic checking
+               if (CFGetTypeID(value) == CFErrorGetTypeID())
+               {
+                       realValue = NULL;
+                       error = (CFErrorRef) value;
+                       isFinal = true;
+               }
+       }
+       else
+       {
+               isFinal = true;
+       }
+       
+       mSeenFinal = isFinal;
+       
+       if (realValue)
+       {
+               CFRetain(realValue);
+       }       
+       
+       if (mDispatchQueue == NULL)
+       {
+               mBlock(realValue, error, isFinal);
+       }
+       else
+       {
+        // ^{ mBlock } gets referenced via this (no retain), localBlock gets owned by
+        // the block passed to dispatch_async
+        SecMessageBlock localBlock = mBlock;
+        dispatch_async(mDispatchQueue, ^{
+            localBlock(realValue, error, isFinal);
+        });
+       }
+}
+
+
+
+BlockMonitor::BlockMonitor(dispatch_queue_t queue, SecMessageBlock block) : Monitor(CFSTR("BlockMonitor")), mDispatchQueue(queue), mSeenFinal(FALSE)
+{
+    mBlock = ^(CFTypeRef value, CFErrorRef error, Boolean isFinal) {
+               block(value, error, isFinal);
+               if (value)
+               {
+                       CFRelease(value);
+               }
+               if (isFinal && mGroup) {
+            LastValueSent();
+               }
+       };
+       mBlock = Block_copy(mBlock);
+}
+
+BlockMonitor::~BlockMonitor()
+{
+       Block_release(mBlock);
+}
+
+void BlockMonitor::LastValueSent()
+{
+    // The initial execute did a retain on our parent to keep it from
+    // going out of scope.  Since this chain is now done, release it.
+    // NOTE: this needs to be the last thing we do otherwise *this
+    // can be deleted out from under us, leading to a crash most frequently
+    // inside the block we dispatch_async, sometimes inside of mBlock.
+    Transform *rootGroup = this->GetRootGroup();
+    CFTypeRef rootGroupRef = rootGroup->GetCFObject();
+    dispatch_async(rootGroup->mDispatchQueue, ^{
+        CFRelease(rootGroupRef);
+    });
+}
+
+CFTypeRef BlockMonitor::Make(dispatch_queue_t queue, SecMessageBlock block)
+{
+       return CoreFoundationHolder::MakeHolder(gInternalCFObjectName, new BlockMonitor(queue, block));
+}