runloop底層實(shí)現(xiàn)(-)weakup&sleep

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
......
dispatch_source_t timeout_timer = NULL;
    struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
    if (seconds <= 0.0) { // instant timeout
        seconds = 0.0;
        timeout_context->termTSR = 0ULL;
    } else if (seconds <= TIMER_INTERVAL_LIMIT) {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT);
        timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_retain(timeout_timer);
        timeout_context->ds = timeout_timer;
        timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
        timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
        dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
        dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
        dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
        uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
        dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
        dispatch_resume(timeout_timer);
    } else { // infinite timeout
        seconds = 9999999999.0;
        timeout_context->termTSR = UINT64_MAX;
    }
......
 
}

上述代碼執(zhí)行了__CFRunLoopTimeout,下面是__CFRunLoopTimeout的實(shí)現(xiàn)

static void __CFRunLoopTimeout(void *arg) {
    struct __timeout_context *context = (struct __timeout_context *)arg;
    context->termTSR = 0ULL;
    CFRUNLOOP_WAKEUP_FOR_TIMEOUT();
    CFRunLoopWakeUp(context->rl);
    // The interval is DISPATCH_TIME_FOREVER, so this won't fire again
}

__CFRunLoopTimeout又調(diào)用了CFRunLoopWakeUp,CFRunLoopWakeUp的實(shí)現(xiàn)如下:

void CFRunLoopWakeUp(CFRunLoopRef rl) {
......
    kern_return_t ret;
    ret = __CFSendTrivialMachMessage(rl->_wakeUpPort, 0, MACH_SEND_TIMEOUT, 0);
    if (ret != MACH_MSG_SUCCESS && ret != MACH_SEND_TIMED_OUT) CRASH("*** Unable to send message to wake up port. (%d) ***", ret);
    __CFRunLoopUnlock(rl);
}

其中__CFSendTrivialMachMessage的實(shí)現(xiàn)如下

static uint32_t __CFSendTrivialMachMessage(mach_port_t port, uint32_t msg_id, CFOptionFlags options, uint32_t timeout) {
    kern_return_t result;
    mach_msg_header_t header;
    header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
    header.msgh_size = sizeof(mach_msg_header_t);
    header.msgh_remote_port = port;
    header.msgh_local_port = MACH_PORT_NULL;
    header.msgh_id = msg_id;
    result = mach_msg(&header, MACH_SEND_MSG|options, header.msgh_size, 0, MACH_PORT_NULL, timeout, MACH_PORT_NULL);
    if (result == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header);
    return result;
}

上面的代碼只看一句mach_msg就可以了,這句發(fā)送了一個(gè)消息給rl->_wakeUpPort喚醒了runloop

而_wakeUpPort是如何工作的呢?
在看下面runloop創(chuàng)建的代碼

static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
   CFRunLoopRef loop = NULL;
   CFRunLoopModeRef rlm;
   uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
   loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopTypeID, size, NULL);
   if (NULL == loop) {
return NULL;
   }
  (void)__CFRunLoopPushPerRunData(loop);
   __CFRunLoopLockInit(&loop->_lock);
   loop->_wakeUpPort = __CFPortAllocate();
   if (CFPORT_NULL == loop->_wakeUpPort) HALT;
   __CFRunLoopSetIgnoreWakeUps(loop);
   loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
   CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
   loop->_commonModeItems = NULL;
   loop->_currentMode = NULL;
   loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
   loop->_blocks_head = NULL;
   loop->_blocks_tail = NULL;
   loop->_counterpart = NULL;
   loop->_pthread = t;
   loop->_winthread = 0;
  rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
   if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
   return loop;
}

其中有一句 loop->_wakeUpPort = __CFPortAllocate();
那么接下來再看__CFPortAllocate:

static __CFPort __CFPortAllocate(void) {
   __CFPort result = CFPORT_NULL;
   kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &result);
   if (KERN_SUCCESS != ret) {
       char msg[256];
       snprintf(msg, 256, "*** The system has no mach ports available. You may be able to diagnose which application(s) are using ports by using 'top' or Activity Monitor. (%d) ***", ret);
       CRSetCrashLogMessage(msg); 
       __THE_SYSTEM_HAS_NO_PORTS_AVAILABLE__(ret); 
       return CFPORT_NULL;
   }
   ret = mach_port_insert_right(mach_task_self(), result, result, MACH_MSG_TYPE_MAKE_SEND);
   if (KERN_SUCCESS != ret) CRASH("*** Unable to set send right on mach port. (%d) ***", ret);
   mach_port_limits_t limits;
   limits.mpl_qlimit = 1;
   ret = mach_port_set_attributes(mach_task_self(), result, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT);
   if (KERN_SUCCESS != ret) CRASH("*** Unable to set attributes on mach port. (%d) ***", ret);    
   return result;
}

上述方法完成了wakeupport的創(chuàng)建
那么wakeupport收到消息后由誰來處理呢?
看下面的方法

static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout) {
    Boolean originalBuffer = true;
    kern_return_t ret = KERN_SUCCESS;
    for (;;) { /* In that sleep of death what nightmares may come ... */
        mach_msg_header_t *msg = (mach_msg_header_t *)*buffer;
        msg->msgh_bits = 0;
        msg->msgh_local_port = port;
        msg->msgh_remote_port = MACH_PORT_NULL;
        msg->msgh_size = buffer_size;
        msg->msgh_id = 0;
        if (TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } else { CFRUNLOOP_POLL(); }
        ret = mach_msg(msg, MACH_RCV_MSG|MACH_RCV_LARGE|((TIMEOUT_INFINITY != timeout) ? MACH_RCV_TIMEOUT :0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV),0, msg->msgh_size, port, timeout, MACH_PORT_NULL);
        CFRUNLOOP_WAKEUP(ret);
        if (MACH_MSG_SUCCESS == ret) {
            *livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL;
            return true;
        }
        if (MACH_RCV_TIMED_OUT == ret) {
            if (!originalBuffer) free(msg);
            *buffer = NULL;
            *livePort = MACH_PORT_NULL;
            return false;
        }
        if (MACH_RCV_TOO_LARGE != ret) break;
        buffer_size = round_msg(msg->msgh_size + MAX_TRAILER_SIZE);
        if (originalBuffer) *buffer = NULL;
        originalBuffer = false;
        *buffer = realloc(*buffer, buffer_size);
    }
    HALT;
    return false;
}

上述類執(zhí)行之后回停留在ret = mach_msg... 這一句等待消息,而當(dāng)最前面的那行代碼ret = __CFSendTrivialMachMessage(rl->_wakeUpPort, 0, MACH_SEND_TIMEOUT, 0);執(zhí)行后 ret = mach_msg... 將收到消息繼續(xù)執(zhí)行。
閉環(huán)---

總結(jié):
sleep本質(zhì)上來說就是通過調(diào)用mach_msg使runloop進(jìn)入等待消息的狀態(tài)。
wakeup本質(zhì)上來說就是通過發(fā)送一套消息給mach_msg監(jiān)聽的端口打破mach_msg等待的狀態(tài)繼續(xù)向下執(zhí)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 最近離職了,可以盡情熬夜寫點(diǎn)總結(jié),不用擔(dān)心第二天上班爽并蛋疼著,這篇的主角 RunLoop 一座大山,涵蓋的...
    zerocc2014閱讀 12,549評(píng)論 13 67
  • RunLoop 是 iOS 和 OS X 開發(fā)中非?;A(chǔ)的一個(gè)概念,這篇文章將從 CFRunLoop 的源碼入手,...
    iOS_Alex閱讀 973評(píng)論 0 10
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling閱讀 1,558評(píng)論 0 13
  • http://www.cocoachina.com/ios/20150601/11970.html RunLoop...
    紫色冰雨閱讀 953評(píng)論 0 3
  • 我就是為了證明自己,你想要的就會(huì)得到,丟掉了,隨時(shí)都能找回來。好好做一個(gè)知識(shí)淵博品位高雅的文藝青年
    Beijing閱讀 128評(píng)論 0 1

友情鏈接更多精彩內(nèi)容