Exemple #1
0
/*
 * sem_wait - lock a semaphore.
 *
 * The value of timeout is msec unit. 0 for no timeout.
 *
 * sem_wait() locks the semaphore referred by sem only if the
 * semaphore value is currently positive. The thread will
 * sleep while the semaphore value is zero. It decrements the
 * semaphore value in return.
 *
 * If waiting thread receives any exception, this routine
 * returns with EINTR in order to invoke exception
 * handler. But, an application assumes this call does NOT
 * return with error. So, system call stub routine must
 * re-call automatically if it gets EINTR.
 */
int
sem_wait(sem_t *sem, u_long timeout)
{
	sem_t s;
	int err, rc;

	sched_lock();
	if ((err = sem_copyin(sem, &s)))
		goto out;

	while (s->value == 0) {
		rc = sched_tsleep(&s->event, timeout);
		if (rc == SLP_TIMEOUT) {
			err = ETIMEDOUT;
			goto out;
		}
		if (rc == SLP_INTR) {
			err = EINTR;
			goto out;
		}
		/* Kick scheduler */
		sched_unlock();
		sched_lock();
	}
	s->value--;
 out:
	sched_unlock();
	return err;
}
Exemple #2
0
/*
 * Wait on a condition.
 *
 * If the thread receives any exception while waiting CV, this
 * routine returns immediately with EINTR in order to invoke
 * exception handler. However, an application assumes this call
 * does NOT return with an error. So, the stub routine in a
 * system call library must call cond_wait() again if it gets
 * EINTR as error.
 */
int
cond_wait(cond_t *cond, mutex_t *mtx, u_long timeout)
{
	cond_t c;
	int err, rc;

	if (umem_copyin(cond, &c, sizeof(cond)))
		return DERR(EFAULT);

	sched_lock();
	if (c == COND_INITIALIZER) {
		if ((err = cond_init(cond))) {
			sched_unlock();
			return err;
		}
		umem_copyin(cond, &c, sizeof(cond));
	} else {
		if (!cond_valid(c)) {
			sched_unlock();
			return DERR(EINVAL);
		}
	}
	ASSERT(c->signal >= 0 && c->signal <= c->wait);
	c->wait++;
	if ((err = mutex_unlock_count(mtx))) {
		if (err < 0) {
			/* mutex was recursively locked - would deadlock */
			mutex_lock(mtx);
			err = DERR(EDEADLK);
		}
		sched_unlock();
		return err;
	}

	rc = sched_tsleep(&c->event, timeout);
	err = mutex_lock(mtx);
	c->wait--;
	if (!err) {
		if (c->signal)
			c->signal--; /* > 1 thread may be waiting */
		else {
			switch (rc) {
			case SLP_TIMEOUT:
				err = ETIMEDOUT;
				break;

			case SLP_INTR:
				err = EINTR;
				break;

			default:		/* unexpected */
				err = DERR(EINVAL);
			}
		}
	}
	sched_unlock();
	return err;
}
Exemple #3
0
/*
 * timer_delay - delay thread execution.
 *
 * The caller thread is blocked for the specified time.
 * Returns 0 on success, or the remaining time (msec) on
 * failure.
 */
u_long
timer_delay(u_long msec)
{
	struct timer *tmr;
	u_long remain = 0;
	int rc;

	rc = sched_tsleep(&delay_event, msec);
	if (rc != SLP_TIMEOUT) {
		tmr = &curthread->timeout;
		remain = hztoms(time_remain(tmr->expire));
	}
	return remain;
}
Exemple #4
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;
}
Exemple #5
0
/*
 * Receive a message.
 *
 * A thread can receive a message from the object which was
 * created by any thread belongs to same task. If the message
 * has not arrived yet, it blocks until any message comes in.
 *
 * The size argument specifies the "maximum" size of the message
 * buffer to receive. If the sent message is larger than this
 * size, the kernel will automatically clip the message to the
 * receive buffer size.
 *
 * When message is received, the sender thread is removed from
 * object's send queue. So, another thread can receive the
 * subsequent message from that object. This is important for
 * the multi-thread server which receives some messages
 * simultaneously.
 */
int
msg_receive(object_t obj, void *msg, size_t size, u_long timeout)
{
	thread_t th;
	size_t len;
	int rc, err = 0;

	if (!user_area(msg))
		return EFAULT;

	sched_lock();

	if (!object_valid(obj)) {
		err = EINVAL;
		goto out;
	}
	if (obj->owner != cur_task()) {
		err = EACCES;
		goto out;
	}
	/*
	 * Check if this thread finished previous receive
	 * operation.  A thread can not receive different
	 * messages at once.
	 */
	if (cur_thread->recvobj) {
		err = EBUSY;
		goto out;
	}
	cur_thread->recvobj = obj;

	/*
	 * If no message exists, wait until message arrives.
	 */
	while (queue_empty(&obj->sendq)) {
		/*
		 * Block until someone sends the message.
		 */
		msg_enqueue(&obj->recvq, cur_thread);
		rc = sched_tsleep(&ipc_event, timeout);
		if (rc != 0) {
			/*
			 * Receive is failed due to some reasons.
			 */
			switch (rc) {
			case SLP_INVAL:
				err = EINVAL;	/* Object has been deleted */
				break;
			case SLP_INTR:
				queue_remove(&cur_thread->ipc_link);
				err = EINTR;	/* Got exception */
				break;
			case SLP_TIMEOUT:
				err = ETIMEDOUT;	/* Timeout */
				break;
			default:
				panic("msg_receive");
				break;
			}
			cur_thread->recvobj = NULL;
			goto out;
		}

		/*
		 * Even if this thread is woken by the sender thread,
		 * the message may be received by another thread
		 * before this thread runs. This can occur when
		 * higher priority thread becomes runnable at that
		 * time. So, it is necessary to check the existence
		 * of the sender, again.
		 */
	}

	th = msg_dequeue(&obj->sendq);

	/*
	 * Copy out the message to the user-space.
	 * The smaller buffer size is used as copy length
	 * between sender and receiver thread.
	 */
	len = min(size, th->msgsize);
	if (len > 0) {
		if (umem_copyout(th->msgaddr, msg, len)) {
			msg_enqueue(&obj->sendq, th);
			cur_thread->recvobj = NULL;
			err = EFAULT;
			goto out;
		}
	}
	/*
	 * Detach the message from the target object.
	 */
	cur_thread->sender = th;
	th->receiver = cur_thread;
 out:
	sched_unlock();
	return err;
}