/*
* Copyright (c) 2008, 2009, 2010, 2011, 2012
* Jonathan Schleifer <js@webkeks.org>
*
* All rights reserved.
*
* This file is part of ObjFW. It may be distributed under the terms of the
* Q Public License 1.0, which can be found in the file LICENSE.QPL included in
* the packaging of this file.
*
* Alternatively, it may be distributed under the terms of the GNU General
* Public License, either version 2 or 3, which can be found in the file
* LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
* file.
*/
#include "config.h"
#import "OFTimer.h"
#import "OFDate.h"
#import "OFRunLoop.h"
#import "OFCondition.h"
#import "OFInvalidArgumentException.h"
#import "OFNotImplementedException.h"
#import "autorelease.h"
#import "macros.h"
@implementation OFTimer
+ (instancetype)scheduledTimerWithTimeInterval: (double)interval
target: (id)target
selector: (SEL)selector
repeats: (BOOL)repeats
{
void *pool = objc_autoreleasePoolPush();
OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: interval];
id timer = [[[self alloc] initWithFireDate: fireDate
interval: interval
target: target
selector: selector
repeats: repeats] autorelease];
[[OFRunLoop currentRunLoop] addTimer: timer];
[timer retain];
objc_autoreleasePoolPop(pool);
return [timer autorelease];
}
+ (instancetype)scheduledTimerWithTimeInterval: (double)interval
target: (id)target
selector: (SEL)selector
object: (id)object
repeats: (BOOL)repeats
{
void *pool = objc_autoreleasePoolPush();
OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: interval];
id timer = [[[self alloc] initWithFireDate: fireDate
interval: interval
target: target
selector: selector
object: object
repeats: repeats] autorelease];
[[OFRunLoop currentRunLoop] addTimer: timer];
[timer retain];
objc_autoreleasePoolPop(pool);
return [timer autorelease];
}
+ (instancetype)scheduledTimerWithTimeInterval: (double)interval
target: (id)target
selector: (SEL)selector
object: (id)object1
object: (id)object2
repeats: (BOOL)repeats
{
void *pool = objc_autoreleasePoolPush();
OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: interval];
id timer = [[[self alloc] initWithFireDate: fireDate
interval: interval
target: target
selector: selector
object: object1
object: object2
repeats: repeats] autorelease];
[[OFRunLoop currentRunLoop] addTimer: timer];
[timer retain];
objc_autoreleasePoolPop(pool);
return [timer autorelease];
}
#ifdef OF_HAVE_BLOCKS
+ (instancetype)scheduledTimerWithTimeInterval: (double)interval
repeats: (BOOL)repeats
block: (of_timer_block_t)block
{
void *pool = objc_autoreleasePoolPush();
OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: interval];
id timer = [[[self alloc] initWithFireDate: fireDate
interval: interval
repeats: repeats
block: block] autorelease];
[[OFRunLoop currentRunLoop] addTimer: timer];
[timer retain];
objc_autoreleasePoolPop(pool);
return [timer autorelease];
}
#endif
+ (instancetype)timerWithTimeInterval: (double)interval
target: (id)target
selector: (SEL)selector
repeats: (BOOL)repeats
{
void *pool = objc_autoreleasePoolPush();
OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: interval];
id timer = [[[self alloc] initWithFireDate: fireDate
interval: interval
target: target
selector: selector
repeats: repeats] autorelease];
[timer retain];
objc_autoreleasePoolPop(pool);
return [timer autorelease];
}
+ (instancetype)timerWithTimeInterval: (double)interval
target: (id)target
selector: (SEL)selector
object: (id)object
repeats: (BOOL)repeats
{
void *pool = objc_autoreleasePoolPush();
OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: interval];
id timer = [[[self alloc] initWithFireDate: fireDate
interval: interval
target: target
selector: selector
object: object
repeats: repeats] autorelease];
[timer retain];
objc_autoreleasePoolPop(pool);
return [timer autorelease];
}
+ (instancetype)timerWithTimeInterval: (double)interval
target: (id)target
selector: (SEL)selector
object: (id)object1
object: (id)object2
repeats: (BOOL)repeats
{
void *pool = objc_autoreleasePoolPush();
OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: interval];
id timer = [[[self alloc] initWithFireDate: fireDate
interval: interval
target: target
selector: selector
object: object1
object: object2
repeats: repeats] autorelease];
[timer retain];
objc_autoreleasePoolPop(pool);
return [timer autorelease];
}
#ifdef OF_HAVE_BLOCKS
+ (instancetype)timerWithTimeInterval: (double)interval
repeats: (BOOL)repeats
block: (of_timer_block_t)block
{
void *pool = objc_autoreleasePoolPush();
OFDate *fireDate = [OFDate dateWithTimeIntervalSinceNow: interval];
id timer = [[[self alloc] initWithFireDate: fireDate
interval: interval
repeats: repeats
block: block] autorelease];
[timer retain];
objc_autoreleasePoolPop(pool);
return [timer autorelease];
}
#endif
- init
{
Class c = [self class];
[self release];
@throw [OFNotImplementedException exceptionWithClass: c
selector: _cmd];
}
- OF_initWithFireDate: (OFDate*)fireDate_
interval: (double)interval_
target: (id)target_
selector: (SEL)selector_
object: (id)object1_
object: (id)object2_
arguments: (uint8_t)arguments_
repeats: (BOOL)repeats_
{
self = [super init];
@try {
fireDate = [fireDate_ retain];
interval = interval_;
target = [target_ retain];
selector = selector_;
object1 = [object1_ retain];
object2 = [object2_ retain];
arguments = arguments_;
repeats = repeats_;
isValid = YES;
condition = [[OFCondition alloc] init];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
- initWithFireDate: (OFDate*)fireDate_
interval: (double)interval_
target: (id)target_
selector: (SEL)selector_
repeats: (BOOL)repeats_
{
return [self OF_initWithFireDate: fireDate_
interval: interval_
target: target_
selector: selector_
object: nil
object: nil
arguments: 0
repeats: repeats_];
}
- initWithFireDate: (OFDate*)fireDate_
interval: (double)interval_
target: (id)target_
selector: (SEL)selector_
object: (id)object
repeats: (BOOL)repeats_
{
return [self OF_initWithFireDate: fireDate_
interval: interval_
target: target_
selector: selector_
object: object
object: nil
arguments: 1
repeats: repeats_];
}
- initWithFireDate: (OFDate*)fireDate_
interval: (double)interval_
target: (id)target_
selector: (SEL)selector_
object: (id)object1_
object: (id)object2_
repeats: (BOOL)repeats_
{
return [self OF_initWithFireDate: fireDate_
interval: interval_
target: target_
selector: selector_
object: object1_
object: object2_
arguments: 2
repeats: repeats_];
}
#ifdef OF_HAVE_BLOCKS
- initWithFireDate: (OFDate*)fireDate_
interval: (double)interval_
repeats: (BOOL)repeats_
block: (of_timer_block_t)block_
{
self = [super init];
@try {
fireDate = [fireDate_ retain];
interval = interval_;
repeats = repeats_;
block = [block_ copy];
isValid = YES;
condition = [[OFCondition alloc] init];
} @catch (id e) {
[self release];
@throw e;
}
return self;
}
#endif
- (void)dealloc
{
[fireDate release];
[target release];
[object1 release];
[object2 release];
#ifdef OF_HAVE_BLOCKS
[block release];
#endif
[condition release];
[super dealloc];
}
- (of_comparison_result_t)compare: (id <OFComparing>)object_
{
OFTimer *otherTimer;
if (![object_ isKindOfClass: [OFTimer class]])
@throw[OFInvalidArgumentException
exceptionWithClass: [self class]
selector: _cmd];
otherTimer = (OFTimer*)object_;
return [fireDate compare: otherTimer->fireDate];
}
- (void)fire
{
OF_ENSURE(arguments <= 2);
#ifdef OF_HAVE_BLOCKS
if (block != NULL)
block(self);
else {
#endif
switch (arguments) {
case 0:
[target performSelector: selector];
break;
case 1:
[target performSelector: selector
withObject: object1];
break;
case 2:
[target performSelector: selector
withObject: object1
withObject: object2];
break;
}
#ifdef OF_HAVE_BLOCKS
}
#endif
[condition lock];
@try {
done = YES;
[condition signal];
} @finally {
[condition unlock];
}
if (repeats && isValid) {
OFDate *old = fireDate;
fireDate = [[OFDate alloc]
initWithTimeIntervalSinceNow: interval];
[old release];
[[OFRunLoop currentRunLoop] addTimer: self];
} else
isValid = NO;
}
- (OFDate*)fireDate
{
OF_GETTER(fireDate, YES)
}
- (double)timeInterval
{
return interval;
}
- (void)invalidate
{
isValid = NO;
}
- (BOOL)isValid
{
return isValid;
}
- (void)waitUntilDone
{
[condition lock];
@try {
if (done) {
done = NO;
return;
}
[condition wait];
} @finally {
[condition unlock];
}
}
@end