1 #include "GroupTransform.h"
5 #include <libkern/OSAtomic.h>
9 CFStringRef kSecGroupTransformType
= CFSTR("GroupTransform");
11 GroupTransform::GroupTransform() : Transform(kSecGroupTransformType
, kSecGroupTransformType
)
13 mMembers
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
14 mAllChildrenFinalized
= dispatch_group_create();
15 mPendingStartupActivity
= dispatch_group_create();
18 // Invoked by Transform::Finalize
19 void GroupTransform::FinalizePhase2()
21 // Any time afer mMembers is released this can be deleted, so we need a local.
22 CFArrayRef members
= this->mMembers
;
23 dispatch_group_notify(mPendingStartupActivity
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH
, 0), ^{
25 this->mMembers
= NULL
;
30 // Delay the final delete of the group until all children are gone (and thus unable to die while referencing us).
31 dispatch_group_notify(mAllChildrenFinalized
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH
, 0), ^{
36 void GroupTransform::StartingExecutionInGroup()
38 this->mIsActive
= true;
39 dispatch_group_enter(mPendingStartupActivity
);
40 dispatch_group_enter(mAllChildrenFinalized
);
43 void GroupTransform::StartedExecutionInGroup(bool succesful
)
45 dispatch_group_leave(mPendingStartupActivity
);
46 dispatch_group_leave(mAllChildrenFinalized
);
49 bool GroupTransform::validConnectionPoint(CFStringRef attributeName
)
51 // We don't want connections to/from unexported attributes
52 return NULL
!= this->getAH(attributeName
, false);
55 GroupTransform::~GroupTransform()
57 // mMembers already released (via FinalizePhase2)
58 dispatch_release(mAllChildrenFinalized
);
59 dispatch_release(mPendingStartupActivity
);
62 void GroupTransform::ChildStartedFinalization(Transform
*child
)
64 // child started finalizing before the group?? Likely client over release.
65 (void)transforms_assume(this->mIsFinalizing
);
66 dispatch_group_leave(mAllChildrenFinalized
);
69 CFTypeRef
GroupTransform::Make()
71 return CoreFoundationHolder::MakeHolder(kSecGroupTransformType
, new GroupTransform());
74 static CFComparisonResult
tr_cmp(const void *val1
, const void *val2
, void *context
)
76 return (((intptr_t) val1
== (intptr_t) val2
) ? kCFCompareEqualTo
77 : ((intptr_t) val1
> (intptr_t) val2
) ? kCFCompareGreaterThan
78 : kCFCompareLessThan
);
81 bool GroupTransform::HasMember(SecTransformRef member
)
83 // find the transform in the group
84 CFIndex numMembers
= CFArrayGetCount(mMembers
);
87 for (i
= 0; i
< numMembers
; ++i
)
89 if (CFArrayGetValueAtIndex(mMembers
, i
) == member
)
100 void GroupTransform::RemoveMemberFromGroup(SecTransformRef member
)
102 // find the transform in the group and remove it
103 // XXX: this would be a lot faster if it used CFArrayBSearchValues
104 CFIndex numMembers
= CFArrayGetCount(mMembers
);
107 for (i
= 0; i
< numMembers
; ++i
)
109 if (CFArrayGetValueAtIndex(mMembers
, i
) == member
)
111 // removing the item will release it, so we don't need an explicit release
112 CFArrayRemoveValueAtIndex(mMembers
, i
);
113 numMembers
= CFArrayGetCount(mMembers
);
114 dispatch_group_leave(mAllChildrenFinalized
);
119 void GroupTransform::AddAllChildrenFinalizedCallback(dispatch_queue_t run_on
, dispatch_block_t callback
)
121 dispatch_group_notify(mAllChildrenFinalized
, run_on
, callback
);
125 void GroupTransform::AddMemberToGroup(SecTransformRef member
)
127 // set a backlink to this group in the child (used for abort and other purposes)
128 Transform
* transform
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(member
);
129 // XXX: it seems like we should be able to ensure that we are the only caller to SetGroup, and that if the group is set to us we could skip this search...
130 transform
->SetGroup(this);
131 if (transform
== this) {
132 // We don't want to be in our own membership list, at a minimum
133 // that makes reference counts cranky.
137 // check to make sure that member is not already in the group (the bsearch code is a bit more complex, but cuts run time for the 8163542 test from about 40 minutes to under a minute)
138 CFIndex numMembers
= CFArrayGetCount(mMembers
);
139 CFRange range
= {0, numMembers
};
140 CFIndex at
= CFArrayBSearchValues(mMembers
, range
, member
, tr_cmp
, NULL
);
141 SecTransformRef candiate
= (at
< numMembers
) ? CFArrayGetValueAtIndex(mMembers
, at
) : NULL
;
142 if (member
== candiate
) {
146 CFArrayInsertValueAtIndex(mMembers
, at
, member
);
147 dispatch_group_enter(mAllChildrenFinalized
);
152 std::string
GroupTransform::DebugDescription()
154 return Transform::DebugDescription() + ": GroupTransform";
159 class GroupTransformFactory
: public TransformFactory
162 GroupTransformFactory();
164 virtual CFTypeRef
Make();
169 TransformFactory
* GroupTransform::MakeTransformFactory()
171 return new GroupTransformFactory();
176 GroupTransformFactory::GroupTransformFactory() : TransformFactory(kSecGroupTransformType
)
182 CFTypeRef
GroupTransformFactory::Make()
184 return GroupTransform::Make();
189 CFTypeID
GroupTransform::GetCFTypeID()
191 return CoreFoundationObject::FindObjectType(kSecGroupTransformType
);
197 SecTransformRef
GroupTransform::FindFirstTransform()
199 // look for a transform that has no connections to INPUT (prefer ones where INPUT is required)
202 range
.length
= CFArrayGetCount(mMembers
);
203 SecTransformRef items
[range
.length
];
204 SecTransformRef maybe
= NULL
;
206 CFArrayGetValues(mMembers
, range
, items
);
209 for (i
= 0; i
< range
.length
; ++i
)
211 SecTransformRef tr
= (SecTransformRef
) items
[i
];
212 Transform
* t
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(tr
);
213 SecTransformAttributeRef in
= getAH(kSecTransformInputAttributeName
, false);
214 if (!t
->GetMetaAttribute(in
, kSecTransformMetaAttributeHasInboundConnection
)) {
216 if (t
->GetMetaAttribute(in
, kSecTransformMetaAttributeRequired
)) {
226 SecTransformRef
GroupTransform::GetAnyMember()
228 if (CFArrayGetCount(mMembers
)) {
229 return CFArrayGetValueAtIndex(mMembers
, 0);
235 // Pretty horrible kludge -- we will do Very Bad Things if someone names their transform <anything>Monitor
236 SecTransformRef
GroupTransform::FindMonitor()
238 // list all transforms in the group
241 range
.length
= CFArrayGetCount(mMembers
);
242 SecTransformRef items
[range
.length
];
243 CFArrayGetValues(mMembers
, range
, items
);
245 // check each item to see if it is a monitor
247 for (i
= 0; i
< range
.length
; ++i
)
249 SecTransformRef tr
= (SecTransformRef
) items
[i
];
250 Transform
* t
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(tr
);
252 if (CFStringHasSuffix(t
->mTypeName
, CFSTR("Monitor"))) {
262 SecTransformRef
GroupTransform::FindLastTransform()
264 // If there's a monitor attached to this transform, the last transform is
265 // the transform that points to it. Otherwise, the last transform is the
266 // one that has nothing connected to its output attribute and said attribute
267 // is marked as requiring an outbound connection.
269 // WARNING: if this function and Transform::ExecuteOperation disagree about
270 // where to attach a monitor things could get funky. It would be very nice
271 // to implement one of these in terms of the other.
273 SecTransformRef lastOrMonitor
= FindMonitor(); // this will either be NULL or the monitor.
274 // We win either way.
276 // list all transforms in the group
279 range
.length
= CFArrayGetCount(mMembers
);
280 SecTransformRef items
[range
.length
];
281 CFArrayGetValues(mMembers
, range
, items
);
283 // if the output attribute of a transform matches our target, we win
285 for (i
= 0; i
< range
.length
; ++i
)
287 Transform
* tr
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(items
[i
]);
289 // get the output attribute for the transform
290 transform_attribute
* ta
= tr
->getTA(kSecTransformOutputAttributeName
, false);
292 if (lastOrMonitor
== NULL
)
294 if (ta
->requires_outbound_connection
&& (ta
->connections
== NULL
|| (CFArrayGetCount(ta
->connections
) == 0)))
296 // this a transform with an unattached OUTPUT with RequiresOutboundConnection true
300 if (ta
->connections
) {
301 // get all of the connections for that attribute, and see if one of them is the monitor
302 CFRange connectionRange
;
303 connectionRange
.location
= 0;
304 connectionRange
.length
= CFArrayGetCount(ta
->connections
);
305 SecTransformAttributeRef attributeHandles
[connectionRange
.length
];
306 CFArrayGetValues(ta
->connections
, connectionRange
, attributeHandles
);
309 for (j
= 0; j
< connectionRange
.length
; ++j
)
311 transform_attribute
* ta
= ah2ta(attributeHandles
[j
]);
312 if (ta
->transform
->GetCFObject() == lastOrMonitor
)
321 // this chain is seriously whacked!!!
325 SecTransformRef
GroupTransform::FindByName(CFStringRef name
)
327 __block SecTransformRef ret
= NULL
;
328 static CFErrorRef early_return
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, EEXIST
, NULL
);
330 ForAllNodes(true, true, ^(Transform
*t
){
331 if (!CFStringCompare(name
, t
->GetName(), 0)) {
332 ret
= t
->GetCFObject();
335 return (CFErrorRef
)NULL
;
341 CFDictionaryRef
GroupTransform::Externalize(CFErrorRef
* error
)
346 // Visit all children once. Unlike ForAllNodes there is no way to early exit, nor a way to return a status.
347 // Returns when all work is scheduled, use group to determine completion of work.
348 // See also ForAllNodes below.
349 void GroupTransform::ForAllNodesAsync(bool opExecutesOnGroups
, dispatch_group_t group
, Transform::TransformAsyncOperation op
)
351 dispatch_group_enter(group
);
352 CFIndex lim
= mMembers
? CFArrayGetCount(mMembers
) : 0;
354 if (opExecutesOnGroups
)
356 dispatch_group_async(group
, mDispatchQueue
, ^{
361 for(CFIndex i
= 0; i
< lim
; ++i
)
363 SecTransformRef tr
= CFArrayGetValueAtIndex(mMembers
, i
);
364 Transform
*t
= (Transform
*)CoreFoundationHolder::ObjectFromCFType(tr
);
366 if (CFGetTypeID(tr
) == SecGroupTransformGetTypeID()) {
367 GroupTransform
*g
= (GroupTransform
*)t
;
368 g
->ForAllNodesAsync(true, group
, op
);
370 dispatch_group_async(group
, t
->mDispatchQueue
, ^{
375 dispatch_group_leave(group
);
378 // Visit all nodes once (at most), attempts to stop if any op
379 // returns non-NULL (if parallel is true in flight ops are not
380 // stopped). Returns when all work is complete, and returns
381 // the "first" non-NULL op value (or NULL if all ops returned
382 // NULL). Uses ForAllNodes below to do the dirty work.
383 CFErrorRef
GroupTransform::ForAllNodes(bool parallel
, bool opExecutesOnGroups
, Transform::TransformOperation op
)
385 dispatch_group_t inner_group
= dispatch_group_create();
387 CFErrorRef err
= NULL
;
388 RecurseForAllNodes(inner_group
, &err
, parallel
, opExecutesOnGroups
, op
);
390 dispatch_group_wait(inner_group
, DISPATCH_TIME_FOREVER
);
391 dispatch_release(inner_group
);
396 // Visit all children once (at most), because groups can appear in
397 // multiple other groups use visitedGroups (protected by
398 // rw_queue) to avoid multiple visits. Will stop if an op
400 // (Used only by ForAllNodes above)
401 void GroupTransform::RecurseForAllNodes(dispatch_group_t group
, CFErrorRef
*err_
, bool parallel
, bool opExecutesOnGroups
, Transform::TransformOperation op
)
403 __block CFErrorRef
*err
= err_
;
404 void (^set_error
)(CFErrorRef new_err
) = ^(CFErrorRef new_err
) {
406 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *)new_err
, (void**)err
)) {
411 void (^runOp
)(Transform
*t
) = ^(Transform
*t
){
413 dispatch_group_async(group
, t
->mDispatchQueue
, ^{
421 dispatch_group_enter(group
);
422 if (opExecutesOnGroups
) {
427 CFIndex i
, lim
= CFArrayGetCount(mMembers
);
429 for(i
= 0; i
< lim
&& !*err
; ++i
) {
430 SecTransformRef tr
= CFArrayGetValueAtIndex(mMembers
, i
);
431 Transform
*t
= (Transform
*)CoreFoundationHolder::ObjectFromCFType(tr
);
433 if (CFGetTypeID(tr
) == SecGroupTransformGetTypeID()) {
434 GroupTransform
*g
= (GroupTransform
*)t
;
435 g
->RecurseForAllNodes(group
, err
, parallel
, opExecutesOnGroups
, op
);
441 dispatch_group_leave(group
);
444 // Return a dot (GraphViz) description of the group.
445 // For debugging use. Exact content and style may
446 // change. Currently all transforms and attributes
447 // are displayed, but only string values are shown
448 // (and no meta attributes are indicated).
449 CFStringRef
GroupTransform::DotForDebugging()
451 __block CFMutableStringRef result
= CFStringCreateMutable(NULL
, 0);
452 CFStringAppend(result
, CFSTR("digraph \"G\" {\n"));
454 dispatch_queue_t collect_nodes
= dispatch_queue_create("dot-node-collector", NULL
);
455 dispatch_group_t complete_nodes
= dispatch_group_create();
456 dispatch_queue_t collect_connections
= dispatch_queue_create("dot-connection-collector", NULL
);
457 dispatch_group_t complete_connections
= dispatch_group_create();
458 // Before we reference a node we need it to be declared in the correct
459 // graph cluster, so we defer all the connection output until we
460 // have all the nodes defined.
461 dispatch_suspend(collect_connections
);
464 this->ForAllNodesAsync(true, complete_nodes
, ^(Transform
*t
) {
465 CFStringRef name
= t
->GetName();
466 __block CFMutableStringRef group_nodes_out
= CFStringCreateMutable(NULL
, 0);
467 __block CFMutableStringRef group_connections_out
= CFStringCreateMutable(NULL
, 0);
468 CFStringRef line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\tsubgraph \"cluster_%@\" {\n"), name
);
469 CFStringAppend(group_nodes_out
, line_out
);
473 CFIndex n_attributes
= t
->GetAttributeCount();
474 transform_attribute
**attributes
= (transform_attribute
**)alloca(n_attributes
* sizeof(transform_attribute
*));
475 t
->TAGetAll(attributes
);
476 CFMutableArrayRef most_dot_names
= CFArrayCreateMutable(NULL
, n_attributes
-1, &kCFTypeArrayCallBacks
);
477 for(int i
= 0; i
< n_attributes
; i
++) {
478 CFStringRef label
= attributes
[i
]->name
;
479 if (attributes
[i
]->value
) {
480 if (CFGetTypeID(attributes
[i
]->value
) == CFStringGetTypeID()) {
481 label
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@=%@"), attributes
[i
]->name
, attributes
[i
]->value
);
485 label
= CFStringCreateCopy(NULL
, attributes
[i
]->name
);
488 CFStringRef dot_node_name
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\"%@#%@\""), name
, attributes
[i
]->name
);
489 if (CFStringCompare(CFSTR("NAME"), label
, 0)) {
490 CFArrayAppendValue(most_dot_names
, dot_node_name
);
492 line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\t\t%@ [shape=plaintext, label=\"%@\"]\n"), dot_node_name
, label
);
493 CFStringAppend(group_nodes_out
, line_out
);
498 CFIndex n_connections
= attributes
[i
]->connections
? CFArrayGetCount(attributes
[i
]->connections
) : 0;
499 for(int j
= 0; j
< n_connections
; j
++) {
500 transform_attribute
*connected_to
= ah2ta(CFArrayGetValueAtIndex(attributes
[i
]->connections
, j
));
501 line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\t%@ -> \"%@#%@\"\n"), dot_node_name
, connected_to
->transform
->GetName(), connected_to
->name
);
502 CFStringAppend(group_connections_out
, line_out
);
507 line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\t\t\"%@#NAME\" -> { %@ } [style=invis]\n\t}\n"), name
, CFStringCreateByCombiningStrings(NULL
, most_dot_names
, CFSTR(" ")));
508 CFStringAppend(group_nodes_out
, line_out
);
511 line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\t\"%@#NAME\" -> \"%@#NAME\" [style=dotted,weight=5]\n"), name
, t
->mGroup
->GetName());
512 CFStringAppend(group_connections_out
, line_out
);
517 dispatch_async(collect_nodes
, ^(void) {
518 CFStringAppend(result
, group_nodes_out
);
519 CFRelease(group_nodes_out
);
521 dispatch_group_async(complete_connections
, collect_connections
, ^(void) {
522 // We don't really need to append to result on the collect_nodes queue
523 // because we happen to know no more work is going on on the collect_nodes
524 // queue, but if that ever changed we would have a hard to track down bug...
525 dispatch_async(collect_nodes
, ^(void) {
526 CFStringAppend(result
, group_connections_out
);
527 CFRelease(group_connections_out
);
532 dispatch_group_wait(complete_nodes
, DISPATCH_TIME_FOREVER
);
533 dispatch_release(complete_nodes
);
534 dispatch_resume(collect_connections
);
535 dispatch_release(collect_connections
);
536 dispatch_group_wait(complete_connections
, DISPATCH_TIME_FOREVER
);
537 dispatch_release(complete_connections
);
539 dispatch_sync(collect_nodes
, ^{
540 CFStringAppend(result
, CFSTR("}\n"));
542 dispatch_release(collect_nodes
);