在iOS 中,除了source1可以自己唤醒run loop之外,其他的事件都需要用户手动唤醒run loop才可以。Run Loop 提供了专门的方法来实现这个功能。其核心部分就是调用mach_msg来向指定的端口发送消息,从而唤醒线程继续工作。
void CFRunLoopWakeUp(CFRunLoopRef rl) {
CHECK_FOR_FORK();
// This lock is crucial to ignorable wakeups, do not remove it.
__CFRunLoopLock(rl);
// 忽略唤醒的情况
if(__CFRunLoopIsIgnoringWakeUps(rl)) {
__CFRunLoopUnlock(rl);
return;
}
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
kern_return_t ret;
/**
We un conditionally try to send the message, since we don't
want to lose a wakeup, but the send may fail if there is
already a wake up pending, since the queue length is 1.
*/
ret = __CFSendTrivialMachMessage(rl->_wakeUpPort, 0, MACH_SEND_TIMEOUT, 0);
if(ret != MACH_MSG_SUCCESS && ret != MACH_SEND_TIMED_OUT) {
CRASH("*** Uable to send message to wake up port, (%d)***", ret);
}
#elif DEPLOYMENT_TARGET_WINDOWS
SetEvent(rl->_wakeUpPort);
#endif
CFRunLoopUnlock(rl);
}
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.msg_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;
}