/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; }
/* * 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; }