//
// main.m
// ZCRunLoop
//
// Created by Zenny Chen on 2016/11/21.
// Copyright © 2016年 GreenGames Studio. All rights reserved.
//
@import Foundation;
#include <sys/event.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <semaphore.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdatomic.h>
#include <stdalign.h>
#if defined(__i386__) || defined(__x86_64__)
#define CPU_PAUSE() asm("pause")
#elif defined(__arm__) || defined(__arm64__)
#define CPU_PAUSE() asm("yield")
#else
#define CPU_PAUSE()
#endif
#pragma mark - ZCEvent
/** 自己定制的事件类 */
@interface ZCEvent : NSObject
/** 事件响应时,将消息所发送给的目标 */
@property (nonatomic, retain) id target;
/** 事件响应时,对目标所发送的消息,这里使用NSValue其实是对SEL类型的封装 */
@property (nonatomic, retain) NSValue *message;
/** 将消息发送给目标时,随带的用户自定义参数 */
@property (nonatomic, retain) id parameter;
@end
@implementation ZCEvent
@synthesize target, message, parameter;
- (void)dealloc
{
self.target = nil;
self.message = nil;
self.parameter = nil;
[super dealloc];
}
@end
#pragma mark - ZCTimerEvent
/** 自己定制的定时器事件类 */
@interface ZCTimerEvent : ZCEvent
{
@public
/** 当前定时器到期时间 */
struct timeval expireDate;
}
@end
@implementation ZCTimerEvent
@end
#pragma mark - ZCRunLoop
/** 自己定制的消息循环类 */
@interface ZCRunLoop : NSObject
/** 获取ZCRunLoop单例实例对象 */
+ (instancetype)runLoop;
/**
* 添加定时器事件
* @param target 消息接收者
* @param selector 消息签名
* @param param 用户自定义参数
* @param timeoutInterval 超时时间,单位为秒
*/
- (void)addTimerEvent:(id)target message:(SEL)selector userParam:(id)param timeout:(NSTimeInterval)timeoutInterval;
/**
* 添加消息事件,用于在当前消息队列中处理
* @param target 消息接收者
* @param selector 消息签名
* @param param 用户自定义参数
*/
- (void)addMessageEvent:(id)target message:(SEL)selector param:(id)param;
/** 运行消息循环 */
- (void)run;
@end
static ZCRunLoop *sMainRunLoop;
@implementation ZCRunLoop
{
@private
/** 用于对时间事件队列操作的循环锁的原子标志 */
atomic_bool alignas(64) mTimerEventFlag;
/** 定时器事件队列 */
NSMutableArray<ZCTimerEvent*> *mTimerEvents;
/** 用于即将处理的定时器事件队列 */
NSArray<ZCTimerEvent*> *mTimerEventsForProcessing;
/** 消息事件队列 */
NSMutableArray<ZCEvent*> *mMessageEvents;
/** 信号量,当当前没有消息时,将当前线程阻塞 */
sem_t *mSemaphore;
/** 当前即将到期的事件索引 */
int mMinimumIntervalTimeEventIndex;
/** 用于标识当前消息循环是否即将退出 */
volatile BOOL mWillBeTerminated;
/** 用于对消息事件队列操作的循环锁的原子标志 */
atomic_bool alignas(64) mMessageEventFlag;
}
+ (instancetype)runLoop
{
return sMainRunLoop;
}
/** 定时器响应函数 */
static void alarm_wakeup(int i)
{
[[ZCRunLoop runLoop] addCurrentTimerEventsToProcess];
}
- (instancetype)init
{
self = [super init];
atomic_init(&mTimerEventFlag, true);
atomic_init(&mMessageEventFlag, true);
mTimerEvents = [[NSMutableArray alloc] initWithCapacity:128];
mMessageEvents = [[NSMutableArray alloc] initWithCapacity:128];
mMinimumIntervalTimeEventIndex = -1;
mSemaphore = sem_open("My semaphore", O_CREAT, S_IRUSR | S_IWUSR, 0);
signal(SIGALRM, alarm_wakeup);
return self;
}
- (void)dealloc
{
[mTimerEvents removeAllObjects];
[mTimerEvents release];
[mMessageEvents removeAllObjects];
[mMessageEvents release];
if(mTimerEventsForProcessing != nil)
[mTimerEventsForProcessing release];
sem_close(mSemaphore);
NSLog(@"ZCRunLoop deallocated!");
[super dealloc];
}
- (void)addMessageEvent:(id)target message:(SEL)selector param:(id)param
{
ZCEvent *event = [ZCEvent new];
event.target = target;
event.message = [NSValue valueWithPointer:selector];
event.parameter = param;
while(!atomic_exchange(&mMessageEventFlag, false))
CPU_PAUSE();
[mMessageEvents addObject:event];
[event release];
atomic_store(&mMessageEventFlag, true);
sem_post(mSemaphore);
}
- (void)addTimerEvent:(id)target message:(SEL)selector userParam:(id)param timeout:(NSTimeInterval)timeoutInterval
{
ZCTimerEvent *newEvent = [ZCTimerEvent new];
newEvent.target = target;
newEvent.message = [NSValue valueWithPointer:selector];
newEvent.parameter = param;
struct timeval specDate;
typeof(specDate.tv_sec) secInterval = (typeof(specDate.tv_sec))timeoutInterval;
typeof(specDate.tv_usec) usecInterval = (timeoutInterval - (double)secInterval) * 1000000.0;
specDate.tv_sec = secInterval;
specDate.tv_usec = usecInterval;
// 每添加了一个新的事件,说明当前run-loop一直会处于运行状态
mWillBeTerminated = NO;
// 上旋锁
while(!atomic_exchange(&mTimerEventFlag, false))
CPU_PAUSE();
struct timeval currTime;
gettimeofday(&currTime, NULL);
// 将specDate设置为到期日期,根据指定的超时时间
timeradd(&specDate, &currTime, &specDate);
newEvent->expireDate = specDate;
[mTimerEvents addObject:newEvent];
[newEvent release];
struct itimerval tout_val;
tout_val.it_interval.tv_sec = 0;
tout_val.it_interval.tv_usec = 0;
tout_val.it_value.tv_sec = 0;
tout_val.it_value.tv_usec = 0;
if(mMinimumIntervalTimeEventIndex == -1)
{
// 用于处理加入第一个事件的时候
mMinimumIntervalTimeEventIndex = 0;
tout_val.it_value.tv_sec = secInterval;
tout_val.it_value.tv_usec = usecInterval;
}
else
{
ZCTimerEvent *minEvent = mTimerEvents[mMinimumIntervalTimeEventIndex];
// 将当前离到期日期最近的日期与新添加的到期日期进行比较
if(timercmp(&minEvent->expireDate, &specDate, >))
{
// 倘若当前离到期日期最近的日期比新添加的到期日期要大,
// 那么将新添加的到期日期作为最小超时时间,并重新设定定时器的值
mMinimumIntervalTimeEventIndex = (int)(mTimerEvents.count - 1);
tout_val.it_value.tv_sec = secInterval;
tout_val.it_value.tv_usec = usecInterval;
}
}
if((tout_val.it_value.tv_sec > 0 || tout_val.it_value.tv_usec > 0))
setitimer(ITIMER_REAL, &tout_val, NULL);
// 解旋锁
atomic_store(&mTimerEventFlag, true);
}
- (void)addCurrentTimerEventsToProcess
{
// 上旋锁
while(!atomic_exchange(&mTimerEventFlag, false))
CPU_PAUSE();
if(mTimerEventsForProcessing != nil)
[mTimerEventsForProcessing release];
mTimerEventsForProcessing = [[NSArray alloc] initWithArray:mTimerEvents];
// 解旋锁
atomic_store(&mTimerEventFlag, true);
}
/** 处理定时器事件 */
- (void)processTimerHandler
{
struct timeval currTime;
gettimeofday(&currTime, NULL);
@autoreleasepool {
NSMutableArray *clearArray = [NSMutableArray arrayWithCapacity:128];
// 遍历每一个事件,看看当前是否即将或已经到期的事件,整个处理过程无需上锁
for(ZCTimerEvent *event in mTimerEventsForProcessing)
{
struct timeval eventTime;
timersub(&event->expireDate, &currTime, &eventTime);
// 计算当前事件的到期日期离当前日期相差多少微秒
__auto_type interval = eventTime.tv_sec * 1000000L + eventTime.tv_usec;
// 这里设定小于10微秒的事件作为到期时间
if(interval < 10)
{
// 执行相应的消息发送
[event.target performSelector:(SEL)[event.message pointerValue] withObject:event.parameter];
// 准备将当前事件移除
[clearArray addObject:event];
}
}
[mTimerEventsForProcessing release];
mTimerEventsForProcessing = nil;
// 上旋锁
while(!atomic_exchange(&mTimerEventFlag, false))
CPU_PAUSE();
[mTimerEvents removeObjectsInArray:clearArray];
const NSUInteger length = mTimerEvents.count;
// 如果事件队列中还存有事件,那么挑选出最小的到期时间,并重新设置定时器
if(length > 0)
{
mMinimumIntervalTimeEventIndex = 0;
struct timeval minimumTime = mTimerEvents[0]->expireDate;
for(int i = 1; i < length; i++)
{
if(timercmp(&minimumTime, &mTimerEvents[i]->expireDate, >))
{
mMinimumIntervalTimeEventIndex = i;
minimumTime = mTimerEvents[i]->expireDate;
}
}
struct itimerval tout_val;
tout_val.it_interval.tv_sec = 0;
tout_val.it_interval.tv_usec = 0;
timersub(&minimumTime, &currTime, &tout_val.it_value);
setitimer(ITIMER_REAL, &tout_val, NULL);
}
else // 否则即将退出当前消息循环
mWillBeTerminated = YES;
}
// 解旋锁
atomic_store(&mTimerEventFlag, true);
}
- (void)processMessages
{
while(!atomic_exchange(&mMessageEventFlag, false))
CPU_PAUSE();
// 处理当前每一个消息事件
for(ZCEvent *event in mMessageEvents)
{
// 将消息事件放到定时器事件队列中处理,默认延迟100微秒
[self addTimerEvent:event.target message:event.message.pointerValue userParam:event.parameter timeout:0.0001];
}
// 最后将所有消息事件全都移除
[mMessageEvents removeAllObjects];
atomic_store(&mMessageEventFlag, true);
}
- (void)run
{
// 如果当前不退出消息循环,则挂起当前线程
while(!mWillBeTerminated)
{
sem_wait(mSemaphore);
// 处理当前的定时器消息
[self processTimerHandler];
// 唤醒一次之后处理所有消息事件
[self processMessages];
}
}
@end
#pragma mark - ZCObject
@interface ZCObject : NSObject
@end
@implementation ZCObject
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
{
[[ZCRunLoop runLoop] addTimerEvent:self message:aSelector userParam:anArgument timeout:delay];
}
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg
{
// 延迟100微秒在主线程的run-loop中发送消息
[[ZCRunLoop runLoop] addMessageEvent:self message:aSelector param:arg];
}
@end
#pragma mark - test
@interface MyObject : ZCObject
- (void)hello:(NSNumber*)delaySeconds;
- (void)hey:(NSNumber*)delaySeconds;
@end
@implementation MyObject
- (void)hello:(NSNumber*)delaySeconds
{
NSLog(@"Hello, world! delayed: %@ seconds", delaySeconds);
}
- (void)hey:(NSNumber*)delaySeconds
{
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^void(void) {
[self performSelectorOnMainThread:@selector(hello:) withObject:delaySeconds];
});
}
- (void)hi:(NSNumber*)delaySeconds
{
NSLog(@"Hi, The following operation will be delayed for %@ seconds", delaySeconds);
[self performSelector:@selector(hello:) withObject:delaySeconds afterDelay:delaySeconds.doubleValue];
}
- (void)dealloc
{
NSLog(@"MyObject deallocated!");
[super dealloc];
}
@end
int main(int argc, const char * argv[])
{
sMainRunLoop = [ZCRunLoop new];
MyObject *obj = [MyObject new];
[obj performSelector:@selector(hello:) withObject:@2.0 afterDelay:2.0];
[obj performSelector:@selector(hello:) withObject:@1.5 afterDelay:1.5];
[obj performSelector:@selector(hello:) withObject:@5.0 afterDelay:5.0];
[obj performSelector:@selector(hello:) withObject:@4.0 afterDelay:4.0];
[obj performSelector:@selector(hello:) withObject:@3.0 afterDelay:3.0];
[obj performSelector:@selector(hi:) withObject:@8.5 afterDelay:8.5];
[obj performSelector:@selector(hey:) withObject:@7.75 afterDelay:7.75];
[obj performSelector:@selector(hello:) withObject:@22.0 afterDelay:22.0];
[obj performSelectorOnMainThread:@selector(hey:) withObject:@0.1];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^void(void) {
[obj performSelectorOnMainThread:@selector(hello:) withObject:@6.5];
[obj performSelectorOnMainThread:@selector(hello:) withObject:@6.7];
});
[obj release];
NSLog(@"Running...");
[[ZCRunLoop runLoop] run];
[sMainRunLoop release];
return 0;
}