//
//  NSThreadWaiter.m
//  NSThreadExtension
//
//  Created by Jonathan Lung on 10/03/07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//  http://www.cs.toronto.edu/~lungj

#import "NSThreadWaiter.h"

static NSConditionLock* threadCountLock;

// A class for containing SELs
@interface SelectorContainer : NSObject {
	SEL selector;
}

@end
@implementation SelectorContainer
-(id) initWithSelector:(SEL) aSelector {
	self = [super init];
	selector = aSelector;
	return self;
}
-(SEL) getSelector {
	return selector;
}
@end

#pragma mark -

@implementation NSThread (Waiting)
+(void) initialize {
	[super initialize];
	threadCountLock = [[NSConditionLock alloc] initWithCondition:0];
}
+(void)detachNewCountedThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument {
	// Wrap the parameters to this method.
	SelectorContainer* selContainer = [[SelectorContainer alloc] initWithSelector:aSelector];
	// Note that if anArgument is nil, an NSArray of length 2 is created.
	NSArray* params = [[NSArray alloc] initWithObjects:selContainer, aTarget, anArgument, nil];
	// NSArray has already retained selfContainer.
	[selContainer release];

	// Increment the thread count in anticipation of the started thread.
	[threadCountLock lock];
	[threadCountLock unlockWithCondition:[threadCountLock condition] + 1];
	
	// Detach the thread
	[NSThread detachNewThreadSelector:@selector(runSelectedMethod:) toTarget:self withObject:params];
}
// This method calls the selector of the object passed to
// detachNewCountedThreadSelector:toTarget:withObject:.
+(void)runSelectedMethod:(NSArray*)params {	
	SEL aSelector = [(SelectorContainer *)[params objectAtIndex:0] getSelector];
	id aTarget = [params objectAtIndex:1];
	id anArgument = nil;

	if ([params count] > 2) {
		// If [params count] > 2, the original anArgument wasn't nil.
		anArgument = [params objectAtIndex:2];
	}

	[aTarget performSelector:aSelector withObject:anArgument];
	// Normally shouldn't do this...  we don't have ownership.
	// But saves using an autorelease pool and avoids some other problems.
	[params release];
	
	// Decrement the thread count since this thread is done.
	[threadCountLock lock];
	[threadCountLock unlockWithCondition:[threadCountLock condition] - 1];
}

+(void)sleepUntilThreadsNumber:(int) numberOfThreads {
	// Block until number of threads is equal to numberOfThreads.
	// Is not guaranteed
	[threadCountLock lockWhenCondition:numberOfThreads];
	[threadCountLock unlock];
}

+(int)detachedThreads {
	// Lock the thread count before reading and returning the value.
	[threadCountLock lock];
	int threads = [threadCountLock condition];
	[threadCountLock unlock];
	return threads;
}
@end
