Beispiel #1
0
/*
 * Wait on a monitor until timeout, interrupt, or notification.  Used for
 * Object.wait() and (somewhat indirectly) Thread.sleep() and Thread.join().
 *
 * If another thread calls Thread.interrupt(), we throw InterruptedException
 * and return immediately if one of the following are true:
 *  - blocked in wait(), wait(long), or wait(long, int) methods of Object
 *  - blocked in join(), join(long), or join(long, int) methods of Thread
 *  - blocked in sleep(long), or sleep(long, int) methods of Thread
 * Otherwise, we set the "interrupted" flag.
 *
 * Checks to make sure that "nsec" is in the range 0-999999
 * (i.e. fractions of a millisecond) and throws the appropriate
 * exception if it isn't.
 *
 * The spec allows "spurious wakeups", and recommends that all code using
 * Object.wait() do so in a loop.  This appears to derive from concerns
 * about pthread_cond_wait() on multiprocessor systems.  Some commentary
 * on the web casts doubt on whether these can/should occur.
 *
 * Since we're allowed to wake up "early", we clamp extremely long durations
 * to return at the end of the 32-bit time epoch.
 */
static void waitMonitor(Thread* self, Monitor* mon, s8 msec, s4 nsec,
    bool interruptShouldThrow)
{
    struct timespec ts;
    bool wasInterrupted = false;
    bool timed;
    int ret;

    assert(self != NULL);
    assert(mon != NULL);

    /* Make sure that we hold the lock. */
    if (mon->owner != self) {
        dvmThrowIllegalMonitorStateException(
            "object not locked by thread before wait()");
        return;
    }

    /*
     * Enforce the timeout range.
     */
    if (msec < 0 || nsec < 0 || nsec > 999999) {
        dvmThrowIllegalArgumentException("timeout arguments out of range");
        return;
    }

    /*
     * Compute absolute wakeup time, if necessary.
     */
    if (msec == 0 && nsec == 0) {
        timed = false;
    } else {
        absoluteTime(msec, nsec, &ts);
        timed = true;
    }

    /*
     * Add ourselves to the set of threads waiting on this monitor, and
     * release our hold.  We need to let it go even if we're a few levels
     * deep in a recursive lock, and we need to restore that later.
     *
     * We append to the wait set ahead of clearing the count and owner
     * fields so the subroutine can check that the calling thread owns
     * the monitor.  Aside from that, the order of member updates is
     * not order sensitive as we hold the pthread mutex.
     */
    waitSetAppend(mon, self);
    int prevLockCount = mon->lockCount;
    mon->lockCount = 0;
    mon->owner = NULL;

    const Method* savedMethod = mon->ownerMethod;
    u4 savedPc = mon->ownerPc;
    mon->ownerMethod = NULL;
    mon->ownerPc = 0;

    /*
     * Update thread status.  If the GC wakes up, it'll ignore us, knowing
     * that we won't touch any references in this state, and we'll check
     * our suspend mode before we transition out.
     */
    if (timed)
        dvmChangeStatus(self, THREAD_TIMED_WAIT);
    else
        dvmChangeStatus(self, THREAD_WAIT);

    dvmLockMutex(&self->waitMutex);

    /*
     * Set waitMonitor to the monitor object we will be waiting on.
     * When waitMonitor is non-NULL a notifying or interrupting thread
     * must signal the thread's waitCond to wake it up.
     */
    assert(self->waitMonitor == NULL);
    self->waitMonitor = mon;

    /*
     * Handle the case where the thread was interrupted before we called
     * wait().
     */
    if (self->interrupted) {
        wasInterrupted = true;
        self->waitMonitor = NULL;
        dvmUnlockMutex(&self->waitMutex);
        goto done;
    }

    /*
     * Release the monitor lock and wait for a notification or
     * a timeout to occur.
     */
    dvmUnlockMutex(&mon->lock);

    if (!timed) {
        ret = pthread_cond_wait(&self->waitCond, &self->waitMutex);
        assert(ret == 0);
    } else {
#ifdef HAVE_TIMEDWAIT_MONOTONIC
        ret = pthread_cond_timedwait_monotonic(&self->waitCond, &self->waitMutex, &ts);
#else
        ret = pthread_cond_timedwait(&self->waitCond, &self->waitMutex, &ts);
#endif
        assert(ret == 0 || ret == ETIMEDOUT);
    }
    if (self->interrupted) {
        wasInterrupted = true;
    }

    self->interrupted = false;
    self->waitMonitor = NULL;

    dvmUnlockMutex(&self->waitMutex);

    /* Reacquire the monitor lock. */
    lockMonitor(self, mon);

done:
    /*
     * We remove our thread from wait set after restoring the count
     * and owner fields so the subroutine can check that the calling
     * thread owns the monitor. Aside from that, the order of member
     * updates is not order sensitive as we hold the pthread mutex.
     */
    mon->owner = self;
    mon->lockCount = prevLockCount;
    mon->ownerMethod = savedMethod;
    mon->ownerPc = savedPc;
    waitSetRemove(mon, self);

    /* set self->status back to THREAD_RUNNING, and self-suspend if needed */
    dvmChangeStatus(self, THREAD_RUNNING);

    if (wasInterrupted) {
        /*
         * We were interrupted while waiting, or somebody interrupted an
         * un-interruptible thread earlier and we're bailing out immediately.
         *
         * The doc sayeth: "The interrupted status of the current thread is
         * cleared when this exception is thrown."
         */
        self->interrupted = false;
        if (interruptShouldThrow) {
            dvmThrowInterruptedException(NULL);
        }
    }
}
Beispiel #2
0
int monitorWait0(Monitor *mon, Thread *self, long long ms, int ns,
                 int blocked, int interruptible) {
    char timed = (ms != 0) || (ns != 0);
    char interrupted = FALSE;
    char timeout = FALSE;
    struct timespec ts;
    int old_count;

    /* Check we own the monitor */
    if(mon->owner != self)
        return FALSE;

    disableSuspend(self);

    /* Unlock the monitor.  As it could be recursively
       locked remember the recursion count */
    old_count = mon->count;
    mon->owner = NULL;
    mon->count = 0;

    /* Counter used in thin-lock deflation */
    mon->in_wait++;

    self->wait_mon = mon;

    if(timed) {
       getTimeoutRelative(&ts, ms, ns);
       self->state = TIMED_WAITING;
    } else
       self->state = blocked ? BLOCKED : WAITING;

    if(interruptible && self->interrupted)
        interrupted = TRUE;
    else {
        if(blocked) {
            self->blocked_mon = mon;
            self->blocked_count++;
        } else
            self->waited_count++;

        self->interrupting = FALSE;

        /* Add the thread onto the end of the wait set */
        waitSetAppend(mon, self);

        while(self->wait_next != NULL && !self->interrupting && !timeout)
            if(timed) {
                timeout = pthread_cond_timedwait(&self->wait_cv,
                              &mon->lock, &ts) == ETIMEDOUT;

                /* On Linux/i386 systems using LinuxThreads,
                   pthread_cond_timedwait is implemented using
                   sigjmp/longjmp.  This resets the fpu control
                   word back to 64-bit precision.  The macro is
                   empty for sane platforms. */ 

                FPU_HACK;
            } else
                pthread_cond_wait(&self->wait_cv, &mon->lock);
    }

    /* If we've been interrupted or timed-out, we will not have been
       removed from the wait set.  If we have, we must have been
       notified afterwards.  In this case, the notify has been lost,
       and we must signal another thread */

    if(self->interrupting || timeout) {
        /* An interrupt after a timeout remains pending */
        interrupted = interruptible && !timeout;

        if(self->wait_next != NULL)
            waitSetUnlinkThread(mon, self);
        else {
            /* Notify lost.  Signal another thread only if it
               was on the wait set at the time of the notify */
            if(mon->wait_set != NULL && mon->wait_set->wait_id <
                                                self->notify_id) {
                Thread *thread = waitSetSignalNext(mon);
                thread->notify_id = self->notify_id;
            }
        }
    }

    self->state = RUNNING;
    self->wait_mon = NULL;

    if(blocked)
        self->blocked_mon = NULL;

   /* Restore the monitor owner and recursion count */
    mon->count = old_count;
    mon->owner = self;
    mon->in_wait--;

    enableSuspend(self);

    if(interrupted) {
        self->interrupted = FALSE;
        signalException(java_lang_InterruptedException, NULL);
    }

    return TRUE;
}
Beispiel #3
0
Datei: lock.c Projekt: webos21/xi
int monitorWait0(Monitor *mon, Thread *self, long long ms, int ns, int blocked,
		int interruptible) {
	char timed = (ms != 0) || (ns != 0);
	char interrupted = FALSE;
	char timeout = FALSE;
	int ts = 0;
	int old_count = 0;

	// Check we own the monitor
	if (mon->owner != self)
		return FALSE;

	disableSuspend(self);

	// Unlock the monitor.  As it could be recursively
	// locked remember the recursion count
	old_count = mon->count;
	mon->owner = NULL;
	mon->count = 0;

	// Counter used in thin-lock deflation
	mon->in_wait++;

	self->wait_mon = mon;

	if (timed) {
		ts = getTimeoutRelative(ms, ns);
		self->state = TIMED_WAITING;
	} else {
		self->state = blocked ? BLOCKED : WAITING;
	}

	if (interruptible && self->interrupted) {
		interrupted = TRUE;
	} else {
		if (blocked) {
			self->blocked_mon = mon;
			self->blocked_count++;
		} else {
			self->waited_count++;
		}

		self->interrupting = FALSE;

		// Add the thread onto the end of the wait set
		waitSetAppend(mon, self);

		while (self->wait_next != NULL && !self->interrupting && !timeout)
			if (timed) {
				timeout = xi_thread_cond_timedwait(&self->wait_cv, &mon->lock,
						ts) == XI_COND_RV_ERR_TIMEOUT;

				// On Linux/i386 systems using LinuxThreads,
				// pthread_cond_timedwait is implemented using
				// sigjmp/longjmp.  This resets the fpu control
				// word back to 64-bit precision.  The macro is
				// empty for sane platforms.
				FPU_HACK;
			} else {
				xi_thread_cond_wait(&self->wait_cv, &mon->lock);
			}
	}

	// If we've been interrupted or timed-out, we will not have been
	// removed from the wait set.  If we have, we must have been
	// notified afterwards.  In this case, the notify has been lost,
	// and we must signal another thread

	if (self->interrupting || timeout) {
		// An interrupt after a timeout remains pending
		interrupted = interruptible && !timeout;

		if (self->wait_next != NULL) {
			waitSetUnlinkThread(mon, self);
		} else {
			// Notify lost.  Signal another thread only if it
			// was on the wait set at the time of the notify
			if (mon->wait_set != NULL && mon->wait_set->wait_id
					< self->notify_id) {
				Thread *thread = waitSetSignalNext(mon);
				thread->notify_id = self->notify_id;
			}
		}
	}

	self->state = RUNNING;
	self->wait_mon = NULL;

	if (blocked) {
		self->blocked_mon = NULL;
	}

	// Restore the monitor owner and recursion count
	mon->count = old_count;
	mon->owner = self;
	mon->in_wait--;

	enableSuspend(self);

	if (interrupted) {
		self->interrupted = FALSE;
		signalException(java_lang_InterruptedException, NULL);
	}

	return TRUE;
}