/* * Abort all message operations relevant to the specified object. * This is called when the target object is deleted. */ void msg_abort(object_t obj) { queue_t q; thread_t t; sched_lock(); /* * Force wakeup all threads in the send queue. */ while (!queue_empty(&obj->sendq)) { q = dequeue(&obj->sendq); t = queue_entry(q, struct thread, ipc_link); sched_unsleep(t, SLP_INVAL); } /* * Force wakeup all threads waiting for receive. */ while (!queue_empty(&obj->recvq)) { q = dequeue(&obj->recvq); t = queue_entry(q, struct thread, ipc_link); sched_unsleep(t, SLP_INVAL); } sched_unlock(); }
/* * Install an exception handler for the current task. * * NULL can be specified as handler to remove current handler. * If handler is removed, all pending exceptions are discarded * immediately. In this case, all threads blocked in * exception_wait() are unblocked. * * Only one exception handler can be set per task. If the * previous handler exists in task, exception_setup() just * override that handler. */ int exception_setup(void (*handler)(int)) { task_t self = cur_task(); list_t head, n; thread_t th; if (handler != NULL && !user_area(handler)) return EFAULT; sched_lock(); if (self->handler && handler == NULL) { /* * Remove existing exception handler. Do clean up * job for all threads in the target task. */ head = &self->threads; for (n = list_first(head); n != head; n = list_next(n)) { /* * Clear pending exceptions. */ th = list_entry(n, struct thread, task_link); irq_lock(); th->excbits = 0; irq_unlock(); /* * If the thread is waiting for an exception, * cancel it. */ if (th->slpevt == &exception_event) sched_unsleep(th, SLP_BREAK); } }
/* * Send a reply message. * * The target object must be the object that we are receiving. * Otherwise, this function will be failed. */ int msg_reply(object_t obj, void *msg, size_t size) { thread_t t; size_t len; if (!user_area(msg)) return EFAULT; sched_lock(); if (!object_valid(obj) || obj != curthread->recvobj) { sched_unlock(); return EINVAL; } /* * Check if sender still exists */ if (curthread->sender == NULL) { /* Clear receive state */ curthread->recvobj = NULL; sched_unlock(); return EINVAL; } /* * Copy a message to the sender's buffer. */ t = curthread->sender; len = MIN(size, t->msgsize); if (len > 0) { if (copyin(msg, t->msgaddr, len)) { sched_unlock(); return EFAULT; } } /* * Wakeup sender with no error. */ sched_unsleep(t, 0); t->receiver = NULL; /* Clear transmit state */ curthread->sender = NULL; curthread->recvobj = NULL; sched_unlock(); return 0; }
/* * Send a reply message. * * The target object must be an appropriate object that current * thread has been received from. Otherwise, this function will * be failed. * * Since the target object may already be deleted, we can not * access the data of the object within this routine. */ int msg_reply(object_t obj, void *msg, size_t size) { thread_t th; size_t len; int err = 0; if (!user_area(msg)) return EFAULT; sched_lock(); if (!object_valid(obj) || obj != cur_thread->recvobj) { sched_unlock(); return EINVAL; } /* * Check if sender still exists */ if (cur_thread->sender == NULL) { err = EINVAL; goto out; } /* * Copy message to the sender's buffer. */ th = cur_thread->sender; len = min(size, th->msgsize); if (len > 0) { if (umem_copyin(msg, th->msgaddr, len)) { sched_unlock(); return EFAULT; } } /* * Wakeup sender with no error. */ sched_unsleep(th, 0); th->receiver = NULL; out: /* Clear transmit state */ cur_thread->sender = NULL; cur_thread->recvobj = NULL; sched_unlock(); return err; }
/* * Install an exception handler for the current task. * * EXC_DFL can be specified as handler to remove the current handler. * If handler is removed, all pending exceptions are discarded * immediately. At this time, all threads blocked in exception_wait() * are automatically unblocked. * * We allow only one exception handler per task. If the handler * has already been set in task, exception_setup() just override * the previous handler. */ int exception_setup(void (*handler)(int)) { task_t self = curtask; list_t head, n; thread_t t; int s; if (handler != EXC_DFL && !user_area(handler)) return EFAULT; if (handler == NULL) return EINVAL; sched_lock(); if (self->handler != EXC_DFL && handler == EXC_DFL) { /* * Remove existing exception handler. Do clean up * job for all threads in the target task. */ head = &self->threads; for (n = list_first(head); n != head; n = list_next(n)) { /* * Clear pending exceptions. */ s = splhigh(); t = list_entry(n, struct thread, task_link); t->excbits = 0; splx(s); /* * If the thread is waiting for an exception, * cancel it. */ if (t->slpevt == &exception_event) { DPRINTF(("Exception cancelled task=%s\n", self->name)); sched_unsleep(t, SLP_BREAK); } } }
/* * Cancel pending message operation of the specified thread. * This is called when the thread is terminated. * * We have to handle the following conditions to prevent deadlock. * * If the terminated thread is sending a message: * 1. A message is already received. * -> The receiver thread will reply to the invalid thread. * * 2. A message is not received yet. * -> The thread remains in send queue of the object. * * When the terminated thread is receiving a message. * 3. A message is already sent. * -> The sender thread will wait for reply forever. * * 4. A message is not sent yet. * -> The thread remains in receive queue of the object. */ void msg_cancel(thread_t t) { sched_lock(); if (t->sendobj != NULL) { if (t->receiver != NULL) t->receiver->sender = NULL; else queue_remove(&t->ipc_link); } if (t->recvobj != NULL) { if (t->sender != NULL) { sched_unsleep(t->sender, SLP_BREAK); t->sender->receiver = NULL; } else queue_remove(&t->ipc_link); } sched_unlock(); }
/* * Clean up pending message operation of specified thread in order * to prevent deadlock. This is called when the thread is killed. * It is necessary to deal with the following conditions. * * If killed thread is sender: * 1. Killed after message is received * -> The received thread will reply to the invalid thread. * * 2. Killed before message is received * -> The thread remains in send queue of the object. * * When thread is receiver: * 3. Killed after message is sent * -> The sender thread continues waiting for reply forever. * * 4. Killed before message is sent * -> The thread remains in receive queue of the object. */ void msg_cleanup(thread_t th) { sched_lock(); if (th->sendobj) { if (th->receiver) th->receiver->sender = NULL; else queue_remove(&th->ipc_link); } if (th->recvobj) { if (th->sender) { sched_unsleep(th->sender, SLP_BREAK); th->sender->receiver = NULL; } else queue_remove(&th->ipc_link); } sched_unlock(); }
/* * Send a message. * * The current thread will be blocked until any other thread * receives and reply the message. A thread can send a * message to any object if it knows the object id. */ int msg_send(object_t obj, void *msg, size_t size) { struct msg_header *hdr; thread_t t; void *kmsg; int rc; if (!user_area(msg)) return EFAULT; if (size < sizeof(struct msg_header)) return EINVAL; sched_lock(); if (!object_valid(obj)) { sched_unlock(); return EINVAL; } /* * A thread can not send a message when it is * already receiving from the target object. * It will obviously cause a deadlock. */ if (obj == curthread->recvobj) { sched_unlock(); return EDEADLK; } /* * Translate message address to the kernel linear * address. So that a receiver thread can access * the message via kernel pointer. We can catch * the page fault here. */ if ((kmsg = kmem_map(msg, size)) == NULL) { sched_unlock(); return EFAULT; } curthread->msgaddr = kmsg; curthread->msgsize = size; /* * The sender ID is filled in the message header * by the kernel. So, the receiver can trust it. */ hdr = (struct msg_header *)kmsg; hdr->task = curtask; /* * If receiver already exists, wake it up. * The highest priority thread can get the message. */ if (!queue_empty(&obj->recvq)) { t = msg_dequeue(&obj->recvq); sched_unsleep(t, 0); } /* * Sleep until we get a reply message. * Note: Do not touch any data in the object * structure after we wakeup. This is because the * target object may be deleted while we are sleeping. */ curthread->sendobj = obj; msg_enqueue(&obj->sendq, curthread); rc = sched_sleep(&ipc_event); if (rc == SLP_INTR) queue_remove(&curthread->ipc_link); curthread->sendobj = NULL; sched_unlock(); /* * Check sleep result. */ switch (rc) { case SLP_BREAK: return EAGAIN; /* Receiver has been terminated */ case SLP_INVAL: return EINVAL; /* Object has been deleted */ case SLP_INTR: return EINTR; /* Exception */ default: /* DO NOTHING */ break; } return 0; }
/* * Send a message. * * The current thread will be blocked until any other thread * receives the message and calls msg_reply() for the target * object. When new message has been reached to the object, it * will be received by highest priority thread waiting for * that message. A thread can send a message to any object if * it knows the object id. */ int msg_send(object_t obj, void *msg, size_t size, u_long timeout) { struct msg_header *hdr; thread_t th; void *kmsg; int rc; if (!user_area(msg)) return EFAULT; if (size < sizeof(struct msg_header)) return EINVAL; sched_lock(); if (!object_valid(obj)) { sched_unlock(); return EINVAL; } if (obj->owner != cur_task() && !task_capable(CAP_IPC)) { sched_unlock(); return EPERM; } /* * A thread can not send a message when the * thread is already receiving from the target * object. This will obviously cause a deadlock. */ if (obj == cur_thread->recvobj) { sched_unlock(); return EDEADLK; } /* * Translate message address to the kernel linear * address. So that a receiver thread can access * the message via kernel pointer. We can catch * the page fault here. */ if ((kmsg = kmem_map(msg, size)) == NULL) { /* Error - no physical address for the message */ sched_unlock(); return EFAULT; } /* * The sender ID in the message header is filled * by the kernel. So, the receiver can trust it. */ hdr = (struct msg_header *)kmsg; hdr->task = cur_task(); /* Save information about the message block. */ cur_thread->msgaddr = kmsg; cur_thread->msgsize = size; /* * If receiver already exists, wake it up. * Highest priority thread will get this message. */ if (!queue_empty(&obj->recvq)) { th = msg_dequeue(&obj->recvq); sched_unsleep(th, 0); } /* * Sleep until we get a reply message. * Note: Do not touch any data in the object * structure after we wakeup. This is because the * target object may be deleted during we were * sleeping. */ cur_thread->sendobj = obj; msg_enqueue(&obj->sendq, cur_thread); rc = sched_tsleep(&ipc_event, timeout); if (rc == SLP_INTR) queue_remove(&cur_thread->ipc_link); cur_thread->sendobj = NULL; sched_unlock(); /* * Check sleep result. */ switch (rc) { case SLP_BREAK: return EAGAIN; /* Receiver has been terminated */ case SLP_INVAL: return EINVAL; /* Object has been deleted */ case SLP_INTR: return EINTR; /* Exception */ case SLP_TIMEOUT: return ETIMEDOUT; /* Timeout */ default: /* DO NOTHING */ break; } return 0; }