使用Objective-C实现自定义的RunLoop

//
//  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;
}

2018/6/19 posted in  RunLoop