GroupTransform.cpp [plain text]
#include "GroupTransform.h"
#include "Utilities.h"
#include "misc.h"
#include <string>
#include <libkern/OSAtomic.h>
using namespace std;
CFStringRef kSecGroupTransformType = CFSTR("GroupTransform");
GroupTransform::GroupTransform() : Transform(kSecGroupTransformType, kSecGroupTransformType)
{
mMembers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
mAllChildrenFinalized = dispatch_group_create();
mPendingStartupActivity = dispatch_group_create();
}
void GroupTransform::FinalizePhase2()
{
CFArrayRef members = this->mMembers;
dispatch_group_notify(mPendingStartupActivity, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if (mMembers) {
this->mMembers = NULL;
CFReleaseSafe(members);
}
});
dispatch_group_notify(mAllChildrenFinalized, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
delete this;
});
}
void GroupTransform::StartingExecutionInGroup()
{
this->mIsActive = true;
dispatch_group_enter(mPendingStartupActivity);
dispatch_group_enter(mAllChildrenFinalized);
}
void GroupTransform::StartedExecutionInGroup(bool succesful)
{
dispatch_group_leave(mPendingStartupActivity);
dispatch_group_leave(mAllChildrenFinalized);
}
bool GroupTransform::validConnectionPoint(CFStringRef attributeName)
{
return NULL != this->getAH(attributeName, false);
}
GroupTransform::~GroupTransform()
{
dispatch_release(mAllChildrenFinalized);
dispatch_release(mPendingStartupActivity);
}
void GroupTransform::ChildStartedFinalization(Transform *child)
{
(void)transforms_assume(this->mIsFinalizing);
dispatch_group_leave(mAllChildrenFinalized);
}
CFTypeRef GroupTransform::Make()
{
return CoreFoundationHolder::MakeHolder(kSecGroupTransformType, new GroupTransform());
}
static CFComparisonResult tr_cmp(const void *val1, const void *val2, void *context)
{
return (((intptr_t) val1 == (intptr_t) val2) ? kCFCompareEqualTo
: ((intptr_t) val1 > (intptr_t) val2) ? kCFCompareGreaterThan
: kCFCompareLessThan);
}
bool GroupTransform::HasMember(SecTransformRef member)
{
CFIndex numMembers = CFArrayGetCount(mMembers);
CFIndex i;
for (i = 0; i < numMembers; ++i)
{
if (CFArrayGetValueAtIndex(mMembers, i) == member)
{
return true;
}
}
return false;
}
void GroupTransform::RemoveMemberFromGroup(SecTransformRef member)
{
CFIndex numMembers = CFArrayGetCount(mMembers);
CFIndex i;
for (i = 0; i < numMembers; ++i)
{
if (CFArrayGetValueAtIndex(mMembers, i) == member)
{
CFArrayRemoveValueAtIndex(mMembers, i);
numMembers = CFArrayGetCount(mMembers);
dispatch_group_leave(mAllChildrenFinalized);
}
}
}
void GroupTransform::AddAllChildrenFinalizedCallback(dispatch_queue_t run_on, dispatch_block_t callback)
{
dispatch_group_notify(mAllChildrenFinalized, run_on, callback);
}
void GroupTransform::AddMemberToGroup(SecTransformRef member)
{
Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(member);
transform->SetGroup(this);
if (transform == this) {
return;
}
CFIndex numMembers = CFArrayGetCount(mMembers);
CFRange range = {0, numMembers};
CFIndex at = CFArrayBSearchValues(mMembers, range, member, tr_cmp, NULL);
SecTransformRef candiate = (at < numMembers) ? CFArrayGetValueAtIndex(mMembers, at) : NULL;
if (member == candiate) {
return;
}
CFArrayInsertValueAtIndex(mMembers, at, member);
dispatch_group_enter(mAllChildrenFinalized);
}
std::string GroupTransform::DebugDescription()
{
return Transform::DebugDescription() + ": GroupTransform";
}
class GroupTransformFactory : public TransformFactory
{
public:
GroupTransformFactory();
virtual CFTypeRef Make();
};
TransformFactory* GroupTransform::MakeTransformFactory()
{
return new GroupTransformFactory();
}
GroupTransformFactory::GroupTransformFactory() : TransformFactory(kSecGroupTransformType)
{
}
CFTypeRef GroupTransformFactory::Make()
{
return GroupTransform::Make();
}
CFTypeID GroupTransform::GetCFTypeID()
{
return CoreFoundationObject::FindObjectType(kSecGroupTransformType);
}
SecTransformRef GroupTransform::FindFirstTransform()
{
CFRange range;
range.location = 0;
range.length = CFArrayGetCount(mMembers);
SecTransformRef items[range.length];
SecTransformRef maybe = NULL;
CFArrayGetValues(mMembers, range, items);
CFIndex i;
for (i = 0; i < range.length; ++i)
{
SecTransformRef tr = (SecTransformRef) items[i];
Transform* t = (Transform*) CoreFoundationHolder::ObjectFromCFType(tr);
SecTransformAttributeRef in = getAH(kSecTransformInputAttributeName, false);
if (!t->GetMetaAttribute(in, kSecTransformMetaAttributeHasInboundConnection)) {
maybe = tr;
if (t->GetMetaAttribute(in, kSecTransformMetaAttributeRequired)) {
return tr;
}
}
}
return maybe;
}
SecTransformRef GroupTransform::GetAnyMember()
{
if (CFArrayGetCount(mMembers)) {
return CFArrayGetValueAtIndex(mMembers, 0);
} else {
return NULL;
}
}
SecTransformRef GroupTransform::FindMonitor()
{
CFRange range;
range.location = 0;
range.length = CFArrayGetCount(mMembers);
SecTransformRef items[range.length];
CFArrayGetValues(mMembers, range, items);
CFIndex i;
for (i = 0; i < range.length; ++i)
{
SecTransformRef tr = (SecTransformRef) items[i];
Transform* t = (Transform*) CoreFoundationHolder::ObjectFromCFType(tr);
if (CFStringHasSuffix(t->mTypeName, CFSTR("Monitor"))) {
return tr;
}
}
return NULL;
}
SecTransformRef GroupTransform::FindLastTransform()
{
SecTransformRef lastOrMonitor = FindMonitor();
CFRange range;
range.location = 0;
range.length = CFArrayGetCount(mMembers);
SecTransformRef items[range.length];
CFArrayGetValues(mMembers, range, items);
CFIndex i;
for (i = 0; i < range.length; ++i)
{
Transform* tr = (Transform*) CoreFoundationHolder::ObjectFromCFType(items[i]);
transform_attribute* ta = tr->getTA(kSecTransformOutputAttributeName, false);
if (lastOrMonitor == NULL)
{
if (ta->requires_outbound_connection && (ta->connections == NULL || (CFArrayGetCount(ta->connections) == 0)))
{
return items[i];
}
} else {
if (ta->connections) {
CFRange connectionRange;
connectionRange.location = 0;
connectionRange.length = CFArrayGetCount(ta->connections);
SecTransformAttributeRef attributeHandles[connectionRange.length];
CFArrayGetValues(ta->connections, connectionRange, attributeHandles);
CFIndex j;
for (j = 0; j < connectionRange.length; ++j)
{
transform_attribute* ta = ah2ta(attributeHandles[j]);
if (ta->transform->GetCFObject() == lastOrMonitor)
{
return items[i];
}
}
}
}
}
return NULL;
}
SecTransformRef GroupTransform::FindByName(CFStringRef name)
{
__block SecTransformRef ret = NULL;
static CFErrorRef early_return = CFErrorCreate(NULL, kCFErrorDomainPOSIX, EEXIST, NULL);
ForAllNodes(true, true, ^(Transform *t){
if (!CFStringCompare(name, t->GetName(), 0)) {
ret = t->GetCFObject();
return early_return;
}
return (CFErrorRef)NULL;
});
return ret;
}
CFDictionaryRef GroupTransform::Externalize(CFErrorRef* error)
{
return NULL;
}
void GroupTransform::ForAllNodesAsync(bool opExecutesOnGroups, dispatch_group_t group, Transform::TransformAsyncOperation op)
{
dispatch_group_enter(group);
CFIndex lim = mMembers ? CFArrayGetCount(mMembers) : 0;
if (opExecutesOnGroups)
{
dispatch_group_async(group, mDispatchQueue, ^{
op(this);
});
}
for(CFIndex i = 0; i < lim; ++i)
{
SecTransformRef tr = CFArrayGetValueAtIndex(mMembers, i);
Transform *t = (Transform*)CoreFoundationHolder::ObjectFromCFType(tr);
if (CFGetTypeID(tr) == SecGroupTransformGetTypeID()) {
GroupTransform *g = (GroupTransform*)t;
g->ForAllNodesAsync(true, group, op);
} else {
dispatch_group_async(group, t->mDispatchQueue, ^{
op(t);
});
}
}
dispatch_group_leave(group);
}
CFErrorRef GroupTransform::ForAllNodes(bool parallel, bool opExecutesOnGroups, Transform::TransformOperation op)
{
dispatch_group_t inner_group = dispatch_group_create();
CFErrorRef err = NULL;
RecurseForAllNodes(inner_group, &err, parallel, opExecutesOnGroups, op);
dispatch_group_wait(inner_group, DISPATCH_TIME_FOREVER);
dispatch_release(inner_group);
return err;
}
void GroupTransform::RecurseForAllNodes(dispatch_group_t group, CFErrorRef *err_, bool parallel, bool opExecutesOnGroups, Transform::TransformOperation op)
{
__block CFErrorRef *err = err_;
void (^set_error)(CFErrorRef new_err) = ^(CFErrorRef new_err) {
if (new_err) {
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *)new_err, (void**)err)) {
CFReleaseNull(new_err);
}
}
};
void (^runOp)(Transform *t) = ^(Transform *t){
if (parallel) {
dispatch_group_async(group, t->mDispatchQueue, ^{
set_error(op(t));
});
} else {
set_error(op(t));
}
};
dispatch_group_enter(group);
if (opExecutesOnGroups) {
runOp(this);
}
CFIndex i, lim = CFArrayGetCount(mMembers);
for(i = 0; i < lim && !*err; ++i) {
SecTransformRef tr = CFArrayGetValueAtIndex(mMembers, i);
Transform *t = (Transform*)CoreFoundationHolder::ObjectFromCFType(tr);
if (CFGetTypeID(tr) == SecGroupTransformGetTypeID()) {
GroupTransform *g = (GroupTransform*)t;
g->RecurseForAllNodes(group, err, parallel, opExecutesOnGroups, op);
} else {
runOp(t);
}
}
dispatch_group_leave(group);
}
CFStringRef GroupTransform::DotForDebugging()
{
__block CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
CFStringAppend(result, CFSTR("digraph \"G\" {\n"));
dispatch_queue_t collect_nodes = dispatch_queue_create("dot-node-collector", NULL);
dispatch_group_t complete_nodes = dispatch_group_create();
dispatch_queue_t collect_connections = dispatch_queue_create("dot-connection-collector", NULL);
dispatch_group_t complete_connections = dispatch_group_create();
dispatch_suspend(collect_connections);
this->ForAllNodesAsync(true, complete_nodes, ^(Transform *t) {
CFStringRef name = t->GetName();
__block CFMutableStringRef group_nodes_out = CFStringCreateMutable(NULL, 0);
__block CFMutableStringRef group_connections_out = CFStringCreateMutable(NULL, 0);
CFStringRef line_out = CFStringCreateWithFormat(NULL, NULL, CFSTR("\tsubgraph \"cluster_%@\" {\n"), name);
CFStringAppend(group_nodes_out, line_out);
CFReleaseNull(line_out);
line_out = NULL;
CFIndex n_attributes = t->GetAttributeCount();
transform_attribute **attributes = (transform_attribute **)alloca(n_attributes * sizeof(transform_attribute *));
t->TAGetAll(attributes);
CFMutableArrayRef most_dot_names = CFArrayCreateMutable(NULL, n_attributes -1, &kCFTypeArrayCallBacks);
for(int i = 0; i < n_attributes; i++) {
CFStringRef label = attributes[i]->name;
if (attributes[i]->value) {
if (CFGetTypeID(attributes[i]->value) == CFStringGetTypeID()) {
label = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@=%@"), attributes[i]->name, attributes[i]->value);
}
}
if (!label) {
label = CFStringCreateCopy(NULL, attributes[i]->name);
}
CFStringRef dot_node_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("\"%@#%@\""), name, attributes[i]->name);
if (CFStringCompare(CFSTR("NAME"), label, 0)) {
CFArrayAppendValue(most_dot_names, dot_node_name);
}
line_out = CFStringCreateWithFormat(NULL, NULL, CFSTR("\t\t%@ [shape=plaintext, label=\"%@\"]\n"), dot_node_name, label);
CFStringAppend(group_nodes_out, line_out);
CFReleaseNull(line_out);
line_out = NULL;
CFReleaseNull(label);
CFIndex n_connections = attributes[i]->connections ? CFArrayGetCount(attributes[i]->connections) : 0;
for(int j = 0; j < n_connections; j++) {
transform_attribute *connected_to = ah2ta(CFArrayGetValueAtIndex(attributes[i]->connections, j));
line_out = CFStringCreateWithFormat(NULL, NULL, CFSTR("\t%@ -> \"%@#%@\"\n"), dot_node_name, connected_to->transform->GetName(), connected_to->name);
CFStringAppend(group_connections_out, line_out);
CFReleaseNull(line_out);
}
CFSafeRelease(dot_node_name);
}
CFStringRef combinedString = CFStringCreateByCombiningStrings(NULL, most_dot_names, CFSTR(" "));
line_out = CFStringCreateWithFormat(NULL, NULL, CFSTR("\t\t\"%@#NAME\" -> { %@ } [style=invis]\n\t}\n"), name, combinedString);
CFStringAppend(group_nodes_out, line_out);
CFReleaseNull(line_out);
CFSafeRelease(most_dot_names);
CFSafeRelease(combinedString);
if (t->mGroup) {
line_out = CFStringCreateWithFormat(NULL, NULL, CFSTR("\t\"%@#NAME\" -> \"%@#NAME\" [style=dotted,weight=5]\n"), name, t->mGroup->GetName());
CFStringAppend(group_connections_out, line_out);
CFReleaseNull(line_out);
}
line_out = NULL;
dispatch_async(collect_nodes, ^(void) {
CFStringAppend(result, group_nodes_out);
CFReleaseNull(group_nodes_out);
});
dispatch_group_async(complete_connections, collect_connections, ^(void) {
dispatch_async(collect_nodes, ^(void) {
CFStringAppend(result, group_connections_out);
CFReleaseNull(group_connections_out);
});
});
});
dispatch_group_wait(complete_nodes, DISPATCH_TIME_FOREVER);
dispatch_release(complete_nodes);
dispatch_resume(collect_connections);
dispatch_release(collect_connections);
dispatch_group_wait(complete_connections, DISPATCH_TIME_FOREVER);
dispatch_release(complete_connections);
dispatch_sync(collect_nodes, ^{
CFStringAppend(result, CFSTR("}\n"));
});
dispatch_release(collect_nodes);
return result;
}