1#include "Monitor.h"
2#include <Block.h>
3#include "misc.h"
4#include "GroupTransform.h"
5#include "Utilities.h"
6
7void Monitor::Wait()
8{
9}
10
11
12
13bool Monitor::IsExternalizable()
14{
15	return false; // monitors aren't really part of the transform
16}
17
18void BlockMonitor::AttributeChanged(CFStringRef name, CFTypeRef value)
19{
20	// deliver the attribute to the queue
21	CFTypeRef realValue = value;
22	CFErrorRef error = NULL;
23	bool isFinal = false;
24
25	if (mSeenFinal)
26	{
27		// A NULL and CFErrorRef might both be enqueued already, and the 2nd can race the teardown.   Without this check we would trigger final processing
28		// more then once resulting in our own overlease issues, and could well cause our client to make similar errors.
29		return;
30	}
31
32	if (realValue != NULL)
33	{
34		// do some basic checking
35		if (CFGetTypeID(value) == CFErrorGetTypeID())
36		{
37			realValue = NULL;
38			error = (CFErrorRef) value;
39			isFinal = true;
40		}
41	}
42	else
43	{
44		isFinal = true;
45	}
46
47	mSeenFinal = isFinal;
48
49	if (realValue)
50	{
51		CFRetain(realValue);
52	}
53
54	if (mDispatchQueue == NULL)
55	{
56		mBlock(realValue, error, isFinal);
57	}
58	else
59	{
60        // ^{ mBlock } gets referenced via this (no retain), localBlock gets owned by
61        // the block passed to dispatch_async
62        SecMessageBlock localBlock = mBlock;
63        dispatch_async(mDispatchQueue, ^{
64            localBlock(realValue, error, isFinal);
65        });
66	}
67}
68
69
70
71BlockMonitor::BlockMonitor(dispatch_queue_t queue, SecMessageBlock block) : Monitor(CFSTR("BlockMonitor")), mDispatchQueue(queue), mSeenFinal(FALSE)
72{
73    mBlock = ^(CFTypeRef value, CFErrorRef error, Boolean isFinal) {
74		block(value, error, isFinal);
75		if (value)
76		{
77			CFRelease(value);
78		}
79		if (isFinal && mGroup) {
80            LastValueSent();
81		}
82	};
83	mBlock = Block_copy(mBlock);
84}
85
86BlockMonitor::~BlockMonitor()
87{
88	Block_release(mBlock);
89}
90
91void BlockMonitor::LastValueSent()
92{
93    // The initial execute did a retain on our parent to keep it from
94    // going out of scope.  Since this chain is now done, release it.
95    // NOTE: this needs to be the last thing we do otherwise *this
96    // can be deleted out from under us, leading to a crash most frequently
97    // inside the block we dispatch_async, sometimes inside of mBlock.
98    Transform *rootGroup = this->GetRootGroup();
99    CFTypeRef rootGroupRef = rootGroup->GetCFObject();
100    dispatch_async(rootGroup->mDispatchQueue, ^{
101        CFRelease(rootGroupRef);
102    });
103}
104
105CFTypeRef BlockMonitor::Make(dispatch_queue_t queue, SecMessageBlock block)
106{
107	return CoreFoundationHolder::MakeHolder(gInternalCFObjectName, new BlockMonitor(queue, block));
108}
109