|
Boost : |
From: William Kempf (sirwillard_at_[hidden])
Date: 2000-09-07 10:13:03
--- In boost_at_[hidden], Levente Farkas <lfarkas_at_m...> wrote:
> William Kempf wrote:
> > Nope. C++ isn't Java. If we absolutely had to have overlapping
>
> that's the main point. we have to be better performance, with wider
> features.
Uhmm... that's debatable. In any event, we do.
> > locks like that we could place the locks on the heap.
Thus 'm1.lock
> > ()' becomes 'plk1 = new mutex::lock(m1)' and 'm1.unlock()'
> > becomes 'delete plk1'. Ugly? Yes, but that could be considered a
> > good thing, since it puts up a red flag that what we're doing is
> > unsafe.
>
> this isn't likes to me. if we alread have a change to lock and
unlock
> explicitly why we make it so difficult.
The same reason why you have c_str() instead of operator const char*
() for basic_string. The added difficulty discourages the use, but
doesn't prevent it when needed. In our case, there's an additional
reason. The lock class allows us to do compile time checking on the
lock status for things such as condition::wait(). This is a HUGE
improvement on the interface.
> those who read the manual and
> know what is the guard for probably always will use it (if
possible),
> and those who don't know what they are doing will be in trauble
anyway.
I've never liked arguments like this. If you can make it harder to
do things wrong you should do so, even if it means some cases are a
little harder to program. This is especially true if the performance
and functionality remains constant, as it does here (give or take a
few clock cycles).
> > If that's not good enough, the better solution than exposing
> > lock/unlock on the mutex is to add lock/unlock functions to the
lock
> > objects, though I wouldn't recommend it (this reduces the safety
of
> > the condition::wait() since we're no longer assured that the
mutex is
> > properly locked).
>
> condition can be a friend (as it's now AFAI remember) so condition
can
> check the mutex state.
This is a runtime check. We want a compile time check. Makes it
much easier to write conforming and correct code.
> > The simple fact, to me at least, is that the example you give
here is
> > EXTREMELY rare, and in fact should be avoided when possible for
> > numerous reasons. How often have you really had a need to use
> > overlapping locks?
>
> eg in may last project, the above was a read example. yes of course
if
> we've got privmitives which are equivalent with another set than we
can
> do the same think over both set, but one of them can be very
difficult.
> so I sure I can reorganize my code to use just "java style" blocked
guard,
> but I have to use more mutex and/or condition and the code wouldn't
be
> so easy to read.
Uhmmm... I'd have to see a concrete example. Other than a slight
performance hit I can think of no reason why the locks must overlap
this way. The call to m1.unlock() could probably be safely moved to
after the call to m2.unlock(), which allows nested locking constructs
and simple use of the lock class. The cases where the locks
absolutely must overlap in this manner are rare, at least in my own
experience. Rare enough to not warrant a change here.
> > > those user who would like to write safe code will always use the
> > > guard/auto_lock, but if the problem/solution requires he can
lock
> > manualy.
> > > Sometimes it's a must that lock in one function and unlock in
> > another.
> >
> > Another very rare need indeed! Also "solvable" by placing the
lock
> > on the heap (and probably using an auto_ptr to pass it around!).
>
> everything is solvable, but the price ...
Is very small. Especially when weighed against the benefits of
safety.
> anyway in an oop word, it obious to me if I've an object which can
be
> lock nad unlock than it have two function a lock nad an unlock.
> why the stream classes wasn't designed to be a helper class to open
> and close it ?
The comparison is apples to oranges. Fail to open the stream and you
get obvious runtime errors. Fail to close the stream and it'll be
closed for you at the time of destruction. The impact is small and
obvious. Not so with mutex locks. Fail to lock a mutex and call
condition::wait() and you've got undefined behavior (with a more
complex requirement on this we could have this result in a runtime
error, but this is dangerous because of my next point). Fail to
unlock the mutex and you get a runtime error... a deadlock! This
sort of runtime error is extremely difficult to diagnose and correct
because of the sheer nature of threads. The cost of making mistakes
is high enough that we MUST attempt to prevent as many mistakes as
possible at COMPILE time, even if this means corner cases become
slightly more difficult to deal with.
If the work around for your problem wasn't intuitively obvious,
simple and efficient, then I'd say you have an argument. However,
since the work around is obvious, simple and efficient and the number
of times you'll truly have to use the work around is so small I will
not advocate a different design here.
> > We don't have to work around the problem here, we just have to use
> > the lock in a different manner. Portable, relatively safe and
> > strongly illustrates the dangers when used.
>
> I think it has nothing to do with portable. and the mt stuff is
always
> dangerous nad hard to debug and ...
Which is precisely why a good mt interface will go out of its way to
catch mistakes at compile time!
> > > my naming convention come from the above statement, if the the
> > lock/unlock
> > > function used it's easier to remember as a function and guard
as an
> > > auto-lock.
> >
> > Well, I still don't think you want to expose the function so I'm
> > still in favor of lock as the name for the "auto lock".
>
> I hope I expose now a bit more:-)
Nope. I still don't think exposing the lock/unlock methods is a good
idea.
Bill Kempf
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk