Googles appar
Huvudmeny

Post a Comment On: cbloom rants

"12-08-11 - Some Semaphores"

6 Comments -

1 – 6 of 6
Comment deleted

This comment has been removed by the author.

December 8, 2011 at 5:39 PM

Blogger Drawknob said...

I assume for a fastsemaphore_t::timed_wait() one would best simply pass a timeout value to m_base_sem.wait() and not bother to be checking for a timeout before that within the short spin loop or within try_wait() in order to avoid a performance penalty, given that even a VDSO call to clock_gettime() in Linux, albeit not having the expense of a full kernel call, is not cheap (on Windows, I'm guessing timeGetTime() is a kernel call).

December 13, 2011 at 4:19 PM

Blogger cbloom said...

Sure. The only disadvantage of just passing the timeout on to the base semaphore is that you don't count the time spent in the spins. But as long as the spin count is low that time is negligible.

The only time I ever used timed waits is for debugging/robustness. Instead of an infinite wait I usually use a timed wait for 100 millis or so and print an error message if the time is hit.

FYI there are of course much cheaper ways to get the time without kernel calls.

December 15, 2011 at 12:13 PM

Blogger Unknown said...

Hi, Charles. I just ran across this post after spending today putting together a fastsemaphore with timed_wait(). I haven't found an implementation of timed_wait() that avoids breaking the invariant "m_count = -waiters", and this causes some subtle complications.

Consider this timed_wait() implementation; the problem is at (*1):

....bool timed_wait(int timeout) {
........if (m_count($).fetch_add(-1) < 1) {
............bool success = m_base_sem.timed_wait(timeout);
............// (*1)
............if (success) { return true; }
............m_count($).fetch_add(1);
............return false;
........}
....}

And this chain of events:

....t0 calls wait(10)
......m_count is decremented to -1
....t0 times out, progresses to (*1)
....t1 calls post()
......m_count is incremented to 0
......m_base_sem gets posted
....t1 calls try_wait()
......m_count is 0; try_wait returns false

At the end of this sequence, t1 is justified in thinking that a resource has been successfully passed to t0; but it hasn't. There is now a resource "hidden" in m_base_sem.

This is hard for me to analyze, but I think the proper invariants are

* When m_count < 0, num resources = m_base_sem.count()
* When m_count >= 0, num resources = m_count + m_base_sem.count()
* When m_count >= 0, num waiters = 0

And therefore try_wait() needs to change:

....bool try_wait() {
........// previous code remains the same
........// return false;
........return m_base_sem.try_wait();
....}

What do you think?

September 18, 2014 at 8:09 PM

Blogger cbloom said...

Off hand my guess is that the fix is to change timed_wait like this :


....bool timed_wait(int timeout) {
........if (m_count($).fetch_add(-1) < 1) {
............bool success = m_base_sem.timed_wait(timeout);
............// (*1)
............if (success) { return true; }
............post();
............return false;
........}
....}



However, I don't really like to even talk about this because I consider all code that uses timed waits to be broken for various reasons.

September 19, 2014 at 10:06 AM

Blogger Drawknob said...

Hi Paul, I'm not sure I understand the semantics of this argument: "At the end of this sequence, t1 is justified in thinking that a resource has been successfully passed to t0; but it hasn't."

I don't think t0 can assume anything as far as a resource being passed onto another thread; the way I see it, correctness only needs to guarantee the conditions when a thread can safely assume it has obtained the semaphore-protected resource for itself. If it doesn't have the resource for itself, that a necessary but not sufficient condition to guarantee that another thread has already obtained it -- I don't think the change of hands has to be atomic; it just has to be mutually exclusive (to borrow mutex terminology).

I'm not sure cbloom's "fix" would work anyway, since when a keyed event wait times out, if I recall correctly, it won't eat up a matching keyed event wake, and that would cause the wake to block.

As for cbloom's belief that timed waits are unsavory, I'd say an argument on the other side is to try to have feature parity with C++11 synchronization primitives, all of which have timed waits (mutexes, condvars, and upcoming shared mutexes).

March 26, 2015 at 4:45 PM

You can use some HTML tags, such as <b>, <i>, <a>

This blog does not allow anonymous comments.

Comment moderation has been enabled. All comments must be approved by the blog author.

You will be asked to sign in after submitting your comment.