]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | #include "Monitor.h" |
2 | #include <Block.h> | |
3 | #include "misc.h" | |
4 | #include "GroupTransform.h" | |
5 | #include "Utilities.h" | |
6 | ||
7 | void Monitor::Wait() | |
8 | { | |
9 | } | |
10 | ||
11 | ||
12 | ||
13 | bool Monitor::IsExternalizable() | |
14 | { | |
15 | return false; // monitors aren't really part of the transform | |
16 | } | |
17 | ||
18 | void 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 | ||
71 | BlockMonitor::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 | ||
86 | BlockMonitor::~BlockMonitor() | |
87 | { | |
88 | Block_release(mBlock); | |
89 | } | |
90 | ||
91 | void 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 | ||
105 | CFTypeRef BlockMonitor::Make(dispatch_queue_t queue, SecMessageBlock block) | |
106 | { | |
107 | return CoreFoundationHolder::MakeHolder(gInternalCFObjectName, new BlockMonitor(queue, block)); | |
108 | } |