Boost logo

Boost :

From: Preston A. Elder (prez_at_[hidden])
Date: 2007-11-13 10:30:01


On Tue, 06 Nov 2007 13:22:00 +0000, Preston A. Elder wrote:

Anthony,

I take it from the fact you let this thread die, you have no intention of
implementing mutex interrupt semantics (as stated below, it is in
actuality quite easy, and you can disable it using your existing
semantics for disabling lock interruption - and does not need to be a
separate class).

I ask because if you don't, I will have to as I require the ability to be
able to interrupt not just a condition, but a mutex that is waiting, but
has not yet acquired a lock. In fact, as previously stated, I don't
think interruption is effective at all without such a feature (as
breaking out of a condition will not matter if the first thing it does
after it breaks the condition is try to acquire a contested mutex).

Unfortunately, if I have to implement this, I will have to do it as a
'band-aid' implementation in my own namespace, when really it does belong
as part of boost::thread.

PreZ :)

> On Tue, 06 Nov 2007 10:21:26 +0000, Anthony Williams wrote:
>
>> "Preston A. Elder" <prez_at_[hidden]> writes: The interface is the
>> same as the "cancellation" interface in N2320
>> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2320.html),
>> except renamed to "interruption".
>>
>> The interruption points are:
>>
>> thread::join, thread::timed_join
>> this_thread::sleep
>> condition_variable::wait, condition_variable::timed_wait
>> condition_variable_any::wait, condition_variable_any::timed_wait
>> this_thread::interruption_point
> I appreciate that, however that does not mean you can't go a step
> further :)
>
>>> - but unfortunately it does this by doing a broadcast to that
>>> condition which means two things.
>>
>> Firstly, the broadcast only happens on POSIX platforms --- on Windows,
>> the thread is notified directly.
> My point was, on any single platform, it broadcasts - which in and of
> itself is not a bad thing, my interruptable_mutex used a broadcast too,
> however the lock the condition was waiting on was not the lock the end-
> user would acquire (it was a private lock).
>
>> Condition variables are allowed spurious wake-ups, so the user of the
>> condition variable has to allow for this possibility. Yes, it is less
>> than ideal that all the other threads are woken too, for the reasons
>> you outline. At the moment I'm not sure of a better scheme.
>
> See below.
>
>
>>> Third, interruption should also apply to locks - which would in part
>>> solve the above situation. The problem with this of course is that it
>>> is less efficient to use a mutex/condition crafted to work with
>>> interruptions at the lock level.
>>
>> Yes, and that's why it doesn't happen. All the interruption points are
>> where the thread is waiting for some event (e.g. a condition to be
>> notified, a thread to finish, or a time to elapse). Locks should only
>> be held for a short period, so blocking for a lock should not be a long
>> wait. I don't agree that it should be an interruption point.
>
> I agree, but lets face it, sometimes locks are, by necessity NOT held
> for short periods. For example when writing to an I/O device that you
> want to ensure that there is only one entity writing to it at a time.
> In this case, the writer may hold that lock for significant amounts of
> time (lets avoid the design issue of moving the I/O to its own thread,
> lets for arguments sake say this is not possible for some reason).
>
> In this case, of course, you could create a condition and get woken up
> when you are allowed to write, but why not just lock the mutex and wait
> until you get the lock? Thats all you really want to do. Besides - 1)
> to even enter the condition you have to acquire the lock so you can
> release it, and 2) as soon as the condition is signalled, you will wait
> on that lock anyway.
>
>> If a thread is waiting on a condition variable, a signal is not
>> guaranteed to abort the wait:
>>
>> "If a signal is delivered to a thread waiting for a condition variable,
>> upon return from the signal handler the thread resumes waiting for the
>> condition variable as if it was not interrupted, or it shall return
>> zero due to spurious wakeup."
>>
>> The only way I know to ensure that the thread being interrupted wakes
>> from the condition variable is to broadcast the condition variable. The
>> alternative is that boost::condition_variable does not actually use a
>> pthread_cond_t in the implementation, and I'm not keen on that idea at
>> all.
> But if you do exactly what you do inside your check - ie. throw an
> exception from within the signal, that should break you out of any wait.
>
>>> 2) Move interruption to its own series of structures. Like I did,
>>> with an interruptable_mutex. The advantage of this is twofold:
>>> 1) It can be implemented in a non-platform specific manner, using
>>> existing data structures, so you implement the interruptable version
>>> once (utilizing boost::mutex and boost::condition) and you don't have
>>> to care about the underlying platform. This means it doesn't require
>>> any OS specific support.
>>> 2) It works exactly as the user thinks. An interruptable_mutex can
>>> be
>>> interrupted while blocked on something to do with it (meaning when
>>> trying to acquire a lock on it, OR when waiting on a condition with
>>> it).
>>
>> I'm not keen on this approach, as it really does mean that the thread
>> can only be interrupted at the defined points.
>
> Fine, then keep the interrupt in the thread - in fact, it is NICE to be
> able to interrupt a sleep() and so forth. However, change the way mutex
> works. Change mutex::lock() to something like:
>
> IF (!interruption_enabled)
> do hard-lock;
> ELSE
> interruption_checker check(internal_cond); res = lock.trylock();
> WHILE (!res)
> {
> internal_cond.wait(internal_mutex);
> IF (check.interrupted)
> throw thread_interrupted;
> res = lock.trylock();
> }
>
> Then unlock() becomes:
> lock.unlock();
> internal_cond.notify_one();
>
> This is more or less the crux of my interruptable_mutex anyway. The key
> is that while it still uses a broadcast to interrupt, when the condition
> wakes up, it will acquire the internal lock (ie. a lock on the mutex's
> own meta-data, NOT a the lock the user wants to acquire). This gives
> you an interruption point, but also means that if you weren't the thread
> to be interrupted, you can go back to waiting for the mutex to be
> available.
>
> Passing these to conditions is also pretty easy, for example,
> condition::wait() becomes something like:
>
> IF (!interruption_enabled)
> cond.wait(lock);
> ELSE
> interruption_checker check(cond);
> lock.unlock();
> lock.internal_lock.lock();
> cond.wait(lock.internal_lock);
> lock.internal_lock.unlock();
> if (check.interrupted)
> throw thread_interrupted;
> lock.lock(); // uses the above mutex::lock(), so interruptable.
>
> This way the condition still has the same interruption semantics as you
> currently have, however if interruption is enabled, then you do your
> condition wait on the thread's internal ('meta-data') mutex, not on the
> 'main' mutex. Either way your condition ends by acquiring the mutex the
> user requested - and ends with something that will synchronize multiple
> threads and act as a mutex should. But in addition, you will have
> created an interruption point on mutex::lock(). This gives you a LOT of
> power and convenience.
>
> I still say that interruption semantics are incomplete without
> interruption of a mutex waiting to block. The above can be implemented
> in a platform non-specific manner without much trouble and just use the
> platform-specific version of mutex.
>
> Preston.
>
> If you can dream it, it can be done :)
>
> _______________________________________________ Unsubscribe & other
> changes: http://lists.boost.org/mailman/listinfo.cgi/boost


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk