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
;
79 bool GroupTransform::HasMember(SecTransformRef member
)
81 // find the transform in the group
82 CFIndex numMembers
= CFArrayGetCount(mMembers
);
85 for (i
= 0; i
< numMembers
; ++i
)
87 if (CFArrayGetValueAtIndex(mMembers
, i
) == member
)
98 void GroupTransform::RemoveMemberFromGroup(SecTransformRef member
)
100 // find the transform in the group and remove it
101 // XXX: this would be a lot faster if it used CFArrayBSearchValues
102 CFIndex numMembers
= CFArrayGetCount(mMembers
);
105 for (i
= 0; i
< numMembers
; ++i
)
107 if (CFArrayGetValueAtIndex(mMembers
, i
) == member
)
109 // removing the item will release it, so we don't need an explicit release
110 CFArrayRemoveValueAtIndex(mMembers
, i
);
111 numMembers
= CFArrayGetCount(mMembers
);
112 dispatch_group_leave(mAllChildrenFinalized
);
117 void GroupTransform::AddAllChildrenFinalizedCallback(dispatch_queue_t run_on
, dispatch_block_t callback
)
119 dispatch_group_notify(mAllChildrenFinalized
, run_on
, callback
);
123 void GroupTransform::AddMemberToGroup(SecTransformRef member
)
125 // set a backlink to this group in the child (used for abort and other purposes)
126 Transform
* transform
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(member
);
127 // 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...
128 transform
->SetGroup(this);
129 if (transform
== this) {
130 // We don't want to be in our own membership list, at a minimum
131 // that makes reference counts cranky.
135 // 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)
136 CFIndex numMembers
= CFArrayGetCount(mMembers
);
137 CFRange range
= {0, numMembers
};
138 CFIndex at
= CFArrayBSearchValues(mMembers
, range
, member
, tr_cmp
, NULL
);
139 SecTransformRef candiate
= (at
< numMembers
) ? CFArrayGetValueAtIndex(mMembers
, at
) : NULL
;
140 if (member
== candiate
) {
144 CFArrayInsertValueAtIndex(mMembers
, at
, member
);
145 dispatch_group_enter(mAllChildrenFinalized
);
150 std::string
GroupTransform::DebugDescription()
152 return Transform::DebugDescription() + ": GroupTransform";
157 class GroupTransformFactory
: public TransformFactory
160 GroupTransformFactory();
162 virtual CFTypeRef
Make();
167 TransformFactory
* GroupTransform::MakeTransformFactory()
169 return new GroupTransformFactory();
174 GroupTransformFactory::GroupTransformFactory() : TransformFactory(kSecGroupTransformType
)
180 CFTypeRef
GroupTransformFactory::Make()
182 return GroupTransform::Make();
187 CFTypeID
GroupTransform::GetCFTypeID()
189 return CoreFoundationObject::FindObjectType(kSecGroupTransformType
);
195 SecTransformRef
GroupTransform::FindFirstTransform()
197 // look for a transform that has no connections to INPUT (prefer ones where INPUT is required)
200 range
.length
= CFArrayGetCount(mMembers
);
201 SecTransformRef items
[range
.length
];
202 SecTransformRef maybe
= NULL
;
204 CFArrayGetValues(mMembers
, range
, items
);
207 for (i
= 0; i
< range
.length
; ++i
)
209 SecTransformRef tr
= (SecTransformRef
) items
[i
];
210 Transform
* t
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(tr
);
211 SecTransformAttributeRef in
= getAH(kSecTransformInputAttributeName
, false);
212 if (!t
->GetMetaAttribute(in
, kSecTransformMetaAttributeHasInboundConnection
)) {
214 if (t
->GetMetaAttribute(in
, kSecTransformMetaAttributeRequired
)) {
224 SecTransformRef
GroupTransform::GetAnyMember()
226 if (CFArrayGetCount(mMembers
)) {
227 return CFArrayGetValueAtIndex(mMembers
, 0);
233 // Pretty horrible kludge -- we will do Very Bad Things if someone names their transform <anything>Monitor
234 SecTransformRef
GroupTransform::FindMonitor()
236 // list all transforms in the group
239 range
.length
= CFArrayGetCount(mMembers
);
240 SecTransformRef items
[range
.length
];
241 CFArrayGetValues(mMembers
, range
, items
);
243 // check each item to see if it is a monitor
245 for (i
= 0; i
< range
.length
; ++i
)
247 SecTransformRef tr
= (SecTransformRef
) items
[i
];
248 Transform
* t
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(tr
);
250 if (CFStringHasSuffix(t
->mTypeName
, CFSTR("Monitor"))) {
260 SecTransformRef
GroupTransform::FindLastTransform()
262 // If there's a monitor attached to this transform, the last transform is
263 // the transform that points to it. Otherwise, the last transform is the
264 // one that has nothing connected to its output attribute and said attribute
265 // is marked as requiring an outbound connection.
267 // WARNING: if this function and Transform::ExecuteOperation disagree about
268 // where to attach a monitor things could get funky. It would be very nice
269 // to implement one of these in terms of the other.
271 SecTransformRef lastOrMonitor
= FindMonitor(); // this will either be NULL or the monitor.
272 // We win either way.
274 // list all transforms in the group
277 range
.length
= CFArrayGetCount(mMembers
);
278 SecTransformRef items
[range
.length
];
279 CFArrayGetValues(mMembers
, range
, items
);
281 // if the output attribute of a transform matches our target, we win
283 for (i
= 0; i
< range
.length
; ++i
)
285 Transform
* tr
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(items
[i
]);
287 // get the output attribute for the transform
288 transform_attribute
* ta
= tr
->getTA(kSecTransformOutputAttributeName
, false);
290 if (lastOrMonitor
== NULL
)
292 if (ta
->requires_outbound_connection
&& (ta
->connections
== NULL
|| (CFArrayGetCount(ta
->connections
) == 0)))
294 // this a transform with an unattached OUTPUT with RequiresOutboundConnection true
298 if (ta
->connections
) {
299 // get all of the connections for that attribute, and see if one of them is the monitor
300 CFRange connectionRange
;
301 connectionRange
.location
= 0;
302 connectionRange
.length
= CFArrayGetCount(ta
->connections
);
303 SecTransformAttributeRef attributeHandles
[connectionRange
.length
];
304 CFArrayGetValues(ta
->connections
, connectionRange
, attributeHandles
);
307 for (j
= 0; j
< connectionRange
.length
; ++j
)
309 transform_attribute
* ta
= ah2ta(attributeHandles
[j
]);
310 if (ta
->transform
->GetCFObject() == lastOrMonitor
)
319 // this chain is seriously whacked!!!
323 SecTransformRef
GroupTransform::FindByName(CFStringRef name
)
325 __block SecTransformRef ret
= NULL
;
326 static CFErrorRef early_return
= CFErrorCreate(NULL
, kCFErrorDomainPOSIX
, EEXIST
, NULL
);
328 ForAllNodes(true, true, ^(Transform
*t
){
329 if (!CFStringCompare(name
, t
->GetName(), 0)) {
330 ret
= t
->GetCFObject();
333 return (CFErrorRef
)NULL
;
339 CFDictionaryRef
GroupTransform::Externalize(CFErrorRef
* error
)
344 // Visit all children once. Unlike ForAllNodes there is no way to early exit, nor a way to return a status.
345 // Returns when all work is scheduled, use group to determine completion of work.
346 // See also ForAllNodes below.
347 void GroupTransform::ForAllNodesAsync(bool opExecutesOnGroups
, dispatch_group_t group
, Transform::TransformAsyncOperation op
)
349 dispatch_group_enter(group
);
350 CFIndex lim
= mMembers
? CFArrayGetCount(mMembers
) : 0;
352 if (opExecutesOnGroups
)
354 dispatch_group_async(group
, mDispatchQueue
, ^{
359 for(CFIndex i
= 0; i
< lim
; ++i
)
361 SecTransformRef tr
= CFArrayGetValueAtIndex(mMembers
, i
);
362 Transform
*t
= (Transform
*)CoreFoundationHolder::ObjectFromCFType(tr
);
364 if (CFGetTypeID(tr
) == SecGroupTransformGetTypeID()) {
365 GroupTransform
*g
= (GroupTransform
*)t
;
366 g
->ForAllNodesAsync(true, group
, op
);
368 dispatch_group_async(group
, t
->mDispatchQueue
, ^{
373 dispatch_group_leave(group
);
376 // Visit all nodes once (at most), attempts to stop if any op
377 // returns non-NULL (if parallel is true in flight ops are not
378 // stopped). Returns when all work is complete, and returns
379 // the "first" non-NULL op value (or NULL if all ops returned
380 // NULL). Uses ForAllNodes below to do the dirty work.
381 CFErrorRef
GroupTransform::ForAllNodes(bool parallel
, bool opExecutesOnGroups
, Transform::TransformOperation op
)
383 dispatch_group_t inner_group
= dispatch_group_create();
385 CFErrorRef err
= NULL
;
386 RecurseForAllNodes(inner_group
, &err
, parallel
, opExecutesOnGroups
, op
);
388 dispatch_group_wait(inner_group
, DISPATCH_TIME_FOREVER
);
389 dispatch_release(inner_group
);
394 // Visit all children once (at most), because groups can appear in
395 // multiple other groups use visitedGroups (protected by
396 // rw_queue) to avoid multiple visits. Will stop if an op
398 // (Used only by ForAllNodes above)
399 void GroupTransform::RecurseForAllNodes(dispatch_group_t group
, CFErrorRef
*err_
, bool parallel
, bool opExecutesOnGroups
, Transform::TransformOperation op
)
401 __block CFErrorRef
*err
= err_
;
402 void (^set_error
)(CFErrorRef new_err
) = ^(CFErrorRef new_err
) {
404 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *)new_err
, (void**)err
)) {
409 void (^runOp
)(Transform
*t
) = ^(Transform
*t
){
411 dispatch_group_async(group
, t
->mDispatchQueue
, ^{
419 dispatch_group_enter(group
);
420 if (opExecutesOnGroups
) {
425 CFIndex i
, lim
= CFArrayGetCount(mMembers
);
427 for(i
= 0; i
< lim
&& !*err
; ++i
) {
428 SecTransformRef tr
= CFArrayGetValueAtIndex(mMembers
, i
);
429 Transform
*t
= (Transform
*)CoreFoundationHolder::ObjectFromCFType(tr
);
431 if (CFGetTypeID(tr
) == SecGroupTransformGetTypeID()) {
432 GroupTransform
*g
= (GroupTransform
*)t
;
433 g
->RecurseForAllNodes(group
, err
, parallel
, opExecutesOnGroups
, op
);
439 dispatch_group_leave(group
);
442 // Return a dot (GraphViz) description of the group.
443 // For debugging use. Exact content and style may
444 // change. Currently all transforms and attributes
445 // are displayed, but only string values are shown
446 // (and no meta attributes are indicated).
447 CFStringRef
GroupTransform::DotForDebugging()
449 __block CFMutableStringRef result
= CFStringCreateMutable(NULL
, 0);
450 CFStringAppend(result
, CFSTR("digraph \"G\" {\n"));
452 dispatch_queue_t collect_nodes
= dispatch_queue_create("dot-node-collector", NULL
);
453 dispatch_group_t complete_nodes
= dispatch_group_create();
454 dispatch_queue_t collect_connections
= dispatch_queue_create("dot-connection-collector", NULL
);
455 dispatch_group_t complete_connections
= dispatch_group_create();
456 // Before we reference a node we need it to be declared in the correct
457 // graph cluster, so we defer all the connection output until we
458 // have all the nodes defined.
459 dispatch_suspend(collect_connections
);
462 this->ForAllNodesAsync(true, complete_nodes
, ^(Transform
*t
) {
463 CFStringRef name
= t
->GetName();
464 __block CFMutableStringRef group_nodes_out
= CFStringCreateMutable(NULL
, 0);
465 __block CFMutableStringRef group_connections_out
= CFStringCreateMutable(NULL
, 0);
466 CFStringRef line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\tsubgraph \"cluster_%@\" {\n"), name
);
467 CFStringAppend(group_nodes_out
, line_out
);
471 CFIndex n_attributes
= t
->GetAttributeCount();
472 transform_attribute
**attributes
= (transform_attribute
**)alloca(n_attributes
* sizeof(transform_attribute
*));
473 t
->TAGetAll(attributes
);
474 CFMutableArrayRef most_dot_names
= CFArrayCreateMutable(NULL
, n_attributes
-1, &kCFTypeArrayCallBacks
);
475 for(int i
= 0; i
< n_attributes
; i
++) {
476 CFStringRef label
= attributes
[i
]->name
;
477 if (attributes
[i
]->value
) {
478 if (CFGetTypeID(attributes
[i
]->value
) == CFStringGetTypeID()) {
479 label
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@=%@"), attributes
[i
]->name
, attributes
[i
]->value
);
483 label
= CFStringCreateCopy(NULL
, attributes
[i
]->name
);
486 CFStringRef dot_node_name
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\"%@#%@\""), name
, attributes
[i
]->name
);
487 if (CFStringCompare(CFSTR("NAME"), label
, 0)) {
488 CFArrayAppendValue(most_dot_names
, dot_node_name
);
490 line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\t\t%@ [shape=plaintext, label=\"%@\"]\n"), dot_node_name
, label
);
491 CFStringAppend(group_nodes_out
, line_out
);
496 CFIndex n_connections
= attributes
[i
]->connections
? CFArrayGetCount(attributes
[i
]->connections
) : 0;
497 for(int j
= 0; j
< n_connections
; j
++) {
498 transform_attribute
*connected_to
= ah2ta(CFArrayGetValueAtIndex(attributes
[i
]->connections
, j
));
499 line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\t%@ -> \"%@#%@\"\n"), dot_node_name
, connected_to
->transform
->GetName(), connected_to
->name
);
500 CFStringAppend(group_connections_out
, line_out
);
505 line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\t\t\"%@#NAME\" -> { %@ } [style=invis]\n\t}\n"), name
, CFStringCreateByCombiningStrings(NULL
, most_dot_names
, CFSTR(" ")));
506 CFStringAppend(group_nodes_out
, line_out
);
509 line_out
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("\t\"%@#NAME\" -> \"%@#NAME\" [style=dotted,weight=5]\n"), name
, t
->mGroup
->GetName());
510 CFStringAppend(group_connections_out
, line_out
);
515 dispatch_async(collect_nodes
, ^(void) {
516 CFStringAppend(result
, group_nodes_out
);
517 CFRelease(group_nodes_out
);
519 dispatch_group_async(complete_connections
, collect_connections
, ^(void) {
520 // We don't really need to append to result on the collect_nodes queue
521 // because we happen to know no more work is going on on the collect_nodes
522 // queue, but if that ever changed we would have a hard to track down bug...
523 dispatch_async(collect_nodes
, ^(void) {
524 CFStringAppend(result
, group_connections_out
);
525 CFRelease(group_connections_out
);
530 dispatch_group_wait(complete_nodes
, DISPATCH_TIME_FOREVER
);
531 dispatch_release(complete_nodes
);
532 dispatch_resume(collect_connections
);
533 dispatch_release(collect_connections
);
534 dispatch_group_wait(complete_connections
, DISPATCH_TIME_FOREVER
);
535 dispatch_release(complete_connections
);
537 dispatch_sync(collect_nodes
, ^{
538 CFStringAppend(result
, CFSTR("}\n"));
540 dispatch_release(collect_nodes
);