Beispiel #1
0
Datei: msg.c Projekt: uiv/Lerdu
/*
 * 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();
}
Beispiel #2
0
/*
 * 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);
		}
	}
Beispiel #3
0
Datei: msg.c Projekt: uiv/Lerdu
/*
 * 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;
}
Beispiel #4
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;
}
Beispiel #5
0
/*
 * 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);
			}
		}
	}
Beispiel #6
0
Datei: msg.c Projekt: uiv/Lerdu
/*
 * 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();
}
Beispiel #7
0
/*
 * 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();
}
Beispiel #8
0
Datei: msg.c Projekt: uiv/Lerdu
/*
 * 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;
}
Beispiel #9
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;
}