void mutex_pause(void) { thread_will_wait_with_timeout (current_thread(), 1); ETAP_SET_REASON(current_thread(), BLOCKED_ON_MUTEX_LOCK); thread_block (0); }
mach_msg_return_t ipc_mqueue_receive( ipc_mqueue_t mqueue, mach_msg_option_t option, mach_msg_size_t max_size, mach_msg_timeout_t time_out, boolean_t resume, void (*continuation)(void), ipc_kmsg_t *kmsgp, mach_port_seqno_t *seqnop) { ipc_port_t port; ipc_kmsg_t kmsg; mach_port_seqno_t seqno; { ipc_kmsg_queue_t kmsgs = &mqueue->imq_messages; ipc_thread_t self = current_thread(); if (resume) goto after_thread_block; for (;;) { kmsg = ipc_kmsg_queue_first(kmsgs); if (kmsg != IKM_NULL) { /* check space requirements */ if (kmsg->ikm_header.msgh_size > max_size) { * (mach_msg_size_t *) kmsgp = kmsg->ikm_header.msgh_size; imq_unlock(mqueue); return MACH_RCV_TOO_LARGE; } ipc_kmsg_rmqueue_first_macro(kmsgs, kmsg); port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; seqno = port->ip_seqno++; break; } /* must block waiting for a message */ if (option & MACH_RCV_TIMEOUT) { if (time_out == 0) { imq_unlock(mqueue); return MACH_RCV_TIMED_OUT; } thread_will_wait_with_timeout(self, time_out); } else thread_will_wait(self); ipc_thread_enqueue_macro(&mqueue->imq_threads, self); self->ith_state = MACH_RCV_IN_PROGRESS; self->ith_msize = max_size; imq_unlock(mqueue); if (continuation != (void (*)(void)) 0) { counter(c_ipc_mqueue_receive_block_user++); } else { counter(c_ipc_mqueue_receive_block_kernel++); } thread_block(continuation); after_thread_block: imq_lock(mqueue); /* why did we wake up? */ if (self->ith_state == MACH_MSG_SUCCESS) { /* pick up the message that was handed to us */ kmsg = self->ith_kmsg; seqno = self->ith_seqno; port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; break; } switch (self->ith_state) { case MACH_RCV_TOO_LARGE: /* pick up size of the too-large message */ * (mach_msg_size_t *) kmsgp = self->ith_msize; /* fall-through */ case MACH_RCV_PORT_DIED: case MACH_RCV_PORT_CHANGED: /* something bad happened to the port/set */ imq_unlock(mqueue); return self->ith_state; case MACH_RCV_IN_PROGRESS: /* * Awakened for other than IPC completion. * Remove ourselves from the waiting queue, * then check the wakeup cause. */ ipc_thread_rmqueue(&mqueue->imq_threads, self); switch (self->ith_wait_result) { case THREAD_INTERRUPTED: /* receive was interrupted - give up */ imq_unlock(mqueue); return MACH_RCV_INTERRUPTED; case THREAD_TIMED_OUT: /* timeout expired */ assert(option & MACH_RCV_TIMEOUT); time_out = 0; break; case THREAD_RESTART: default: #if MACH_ASSERT assert(!"ipc_mqueue_receive"); #else panic("ipc_mqueue_receive"); #endif } break; default: #if MACH_ASSERT assert(!"ipc_mqueue_receive: strange ith_state"); #else panic("ipc_mqueue_receive: strange ith_state"); #endif } } /* we have a kmsg; unlock the msg queue */ imq_unlock(mqueue); assert(kmsg->ikm_header.msgh_size <= max_size); } { ipc_marequest_t marequest; marequest = kmsg->ikm_marequest; if (marequest != IMAR_NULL) { ipc_marequest_destroy(marequest); kmsg->ikm_marequest = IMAR_NULL; } assert((kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) == 0); assert(port == (ipc_port_t) kmsg->ikm_header.msgh_remote_port); ip_lock(port); if (ip_active(port)) { ipc_thread_queue_t senders; ipc_thread_t sender; assert(port->ip_msgcount > 0); port->ip_msgcount--; senders = &port->ip_blocked; sender = ipc_thread_queue_first(senders); if ((sender != ITH_NULL) && (port->ip_msgcount < port->ip_qlimit)) { ipc_thread_rmqueue(senders, sender); sender->ith_state = MACH_MSG_SUCCESS; thread_go(sender); } } ip_unlock(port); } current_task()->messages_received++; *kmsgp = kmsg; *seqnop = seqno; return MACH_MSG_SUCCESS; }
/* * thread_switch: * * Context switch. User may supply thread hint. * * Fixed priority threads that call this get what they asked for * even if that violates priority order. */ kern_return_t thread_switch( mach_port_t thread_name, int option, mach_msg_timeout_t option_time) { thread_t cur_thread = current_thread(); processor_t myprocessor; ipc_port_t port; /* * Process option. */ switch (option) { case SWITCH_OPTION_NONE: /* * Nothing to do. */ break; case SWITCH_OPTION_DEPRESS: /* * Depress priority for given time. */ thread_depress_priority(cur_thread, option_time); break; case SWITCH_OPTION_WAIT: thread_will_wait_with_timeout(cur_thread, option_time); break; default: return(KERN_INVALID_ARGUMENT); } #ifndef MIGRATING_THREADS /* XXX thread_run defunct */ /* * Check and act on thread hint if appropriate. */ if ((thread_name != 0) && (ipc_port_translate_send(cur_thread->task->itk_space, thread_name, &port) == KERN_SUCCESS)) { /* port is locked, but it might not be active */ /* * Get corresponding thread. */ if (ip_active(port) && (ip_kotype(port) == IKOT_THREAD)) { thread_t thread; spl_t s; thread = (thread_t) port->ip_kobject; /* * Check if the thread is in the right pset. Then * pull it off its run queue. If it * doesn't come, then it's not eligible. */ s = splsched(); thread_lock(thread); if ((thread->processor_set == cur_thread->processor_set) && (rem_runq(thread) != RUN_QUEUE_NULL)) { /* * Hah, got it!! */ thread_unlock(thread); (void) splx(s); ip_unlock(port); /* XXX thread might disappear on us now? */ #if MACH_FIXPRI if (thread->policy == POLICY_FIXEDPRI) { myprocessor = current_processor(); myprocessor->quantum = thread->sched_data; myprocessor->first_quantum = TRUE; } #endif /* MACH_FIXPRI */ counter(c_thread_switch_handoff++); thread_run(thread_switch_continue, thread); /* * Restore depressed priority */ if (cur_thread->depress_priority >= 0) (void) thread_depress_abort(cur_thread); return(KERN_SUCCESS); } thread_unlock(thread); (void) splx(s); } ip_unlock(port); } #endif /* not MIGRATING_THREADS */ /* * No handoff hint supplied, or hint was wrong. Call thread_block() in * hopes of running something else. If nothing else is runnable, * thread_block will detect this. WARNING: thread_switch with no * option will not do anything useful if the thread calling it is the * highest priority thread (can easily happen with a collection * of timesharing threads). */ #if NCPUS > 1 myprocessor = current_processor(); if (myprocessor->processor_set->runq.count > 0 || myprocessor->runq.count > 0) #endif /* NCPUS > 1 */ { counter(c_thread_switch_block++); thread_block(thread_switch_continue); } /* * Restore depressed priority */ if (cur_thread->depress_priority >= 0) (void) thread_depress_abort(cur_thread); return(KERN_SUCCESS); }
mach_msg_return_t ipc_mqueue_send( ipc_kmsg_t kmsg, mach_msg_option_t option, mach_msg_timeout_t time_out) { ipc_port_t port; port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port; assert(IP_VALID(port)); ip_lock(port); if (port->ip_receiver == ipc_space_kernel) { ipc_kmsg_t reply; /* * We can check ip_receiver == ipc_space_kernel * before checking that the port is active because * ipc_port_dealloc_kernel clears ip_receiver * before destroying a kernel port. */ assert(ip_active(port)); ip_unlock(port); reply = ipc_kobject_server(kmsg); if (reply != IKM_NULL) ipc_mqueue_send_always(reply); return MACH_MSG_SUCCESS; } for (;;) { ipc_thread_t self; /* * Can't deliver to a dead port. * However, we can pretend it got sent * and was then immediately destroyed. */ if (!ip_active(port)) { /* * We can't let ipc_kmsg_destroy deallocate * the port right, because we might end up * in an infinite loop trying to deliver * a send-once notification. */ ip_release(port); ip_check_unlock(port); kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL; ipc_kmsg_destroy(kmsg); return MACH_MSG_SUCCESS; } /* * Don't block if: * 1) We're under the queue limit. * 2) Caller used the MACH_SEND_ALWAYS internal option. * 3) Message is sent to a send-once right. */ if ((port->ip_msgcount < port->ip_qlimit) || (option & MACH_SEND_ALWAYS) || (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) == MACH_MSG_TYPE_PORT_SEND_ONCE)) break; /* must block waiting for queue to clear */ self = current_thread(); if (option & MACH_SEND_TIMEOUT) { if (time_out == 0) { ip_unlock(port); return MACH_SEND_TIMED_OUT; } thread_will_wait_with_timeout(self, time_out); } else thread_will_wait(self); ipc_thread_enqueue(&port->ip_blocked, self); self->ith_state = MACH_SEND_IN_PROGRESS; ip_unlock(port); counter(c_ipc_mqueue_send_block++); thread_block((void (*)(void)) 0); ip_lock(port); /* why did we wake up? */ if (self->ith_state == MACH_MSG_SUCCESS) continue; assert(self->ith_state == MACH_SEND_IN_PROGRESS); /* take ourselves off blocked queue */ ipc_thread_rmqueue(&port->ip_blocked, self); /* * Thread wakeup-reason field tells us why * the wait was interrupted. */ switch (self->ith_wait_result) { case THREAD_INTERRUPTED: /* send was interrupted - give up */ ip_unlock(port); return MACH_SEND_INTERRUPTED; case THREAD_TIMED_OUT: /* timeout expired */ assert(option & MACH_SEND_TIMEOUT); time_out = 0; break; case THREAD_RESTART: default: #if MACH_ASSERT assert(!"ipc_mqueue_send"); #else panic("ipc_mqueue_send"); #endif } } if (kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) { ip_unlock(port); /* don't allow the creation of a circular loop */ ipc_kmsg_destroy(kmsg); return MACH_MSG_SUCCESS; } { ipc_mqueue_t mqueue; ipc_pset_t pset; ipc_thread_t receiver; ipc_thread_queue_t receivers; port->ip_msgcount++; assert(port->ip_msgcount > 0); pset = port->ip_pset; if (pset == IPS_NULL) mqueue = &port->ip_messages; else mqueue = &pset->ips_messages; imq_lock(mqueue); receivers = &mqueue->imq_threads; /* * Can unlock the port now that the msg queue is locked * and we know the port is active. While the msg queue * is locked, we have control of the kmsg, so the ref in * it for the port is still good. If the msg queue is in * a set (dead or alive), then we're OK because the port * is still a member of the set and the set won't go away * until the port is taken out, which tries to lock the * set's msg queue to remove the port's msgs. */ ip_unlock(port); /* check for a receiver for the message */ for (;;) { receiver = ipc_thread_queue_first(receivers); if (receiver == ITH_NULL) { /* no receivers; queue kmsg */ ipc_kmsg_enqueue_macro(&mqueue->imq_messages, kmsg); imq_unlock(mqueue); break; } ipc_thread_rmqueue_first_macro(receivers, receiver); assert(ipc_kmsg_queue_empty(&mqueue->imq_messages)); if (kmsg->ikm_header.msgh_size <= receiver->ith_msize) { /* got a successful receiver */ receiver->ith_state = MACH_MSG_SUCCESS; receiver->ith_kmsg = kmsg; receiver->ith_seqno = port->ip_seqno++; imq_unlock(mqueue); thread_go(receiver); break; } receiver->ith_state = MACH_RCV_TOO_LARGE; receiver->ith_msize = kmsg->ikm_header.msgh_size; thread_go(receiver); } } current_task()->messages_sent++; return MACH_MSG_SUCCESS; }