
Howard Hinnant wrote:
I don't have strong feelings on the bool vs enum debate. But I do have some comments below...
On Jul 7, 2004, at 10:31 AM, Michael Glassford wrote:
Definitions ----------- M: appropriate mutex type
namespace lock_state { typedef enum { unlocked=0, locked=1 } lock_state; } //namespace lock_state
ok.
namespace read_write_lock_state { typedef enum { unlocked=0, read_locked=1, write_locked=2 } read_write_lock_state; } //namespace read_write_lock_state
I'm not clear how these would be used. A read_lock is either locked or not, same with write_lock. I'm not seeing a need, nor a desire for a lock that can both read lock and write lock.
Earlier in this discussion I gave a lot of examples of cases where separate read_lock and write_lock classes had problems. I'll have to go back over my examples with more recent discussion in mind to see if the problems have been addressed before I can comment on this.
namespace blocking_mode { typedef enum { non_blocking=0, blocking=1 } blocking_mode; } //namespace blocking_mode
ok.
ScopedLock / ScopedReadLock / ScopedWriteLock --------------------------------------------- lock(M) //always locking, blocking lock(M, lock_state) //blocking
ok.
ScopedTryLock / ScopedTryReadLock / ScopedTryWriteLock ------------------------------------------------------ try_lock(M) //always locking, BLOCKING try_lock(M, lock_state) //BLOCKING
ok.
try_lock(M, lock_state, blocking_mode) try_lock(M, blocking_mode) //always locking
try_lock tl(M, unlocked, blocking);
What does this mean? The same as:
try_lock tl(M, unlocked); ?
Yes. A while back I was working on a way to eliminate such degenerate cases that using structs instead of enums to select an appropriate constructor at compile time, but it had the disadvantage of being unable to select the appropriate behavior at run time, so I shelved the idea.
Similarly is not try_lock(M, locked, blocking_mode) the same as try_lock(M, blocking_mode)? If I'm interpreting this correctly, how about the following reduced list to eliminate redundancy:
try_lock(M) //always locking, BLOCKING try_lock(M, lock_state) //BLOCKING (if attempting to lock)
try_lock(M, blocking_mode) //always (attempt) locking
I wouldn't object to that. I think the reason I hadn't made this simplification already is that I originally thought try_lock(M, lockstate) should not block, but now that I've changed my mind about that it makes sense.
Btw, this would require enums over bool.
ScopedTimedLock / ScopedTimedReadLock / ScopedTimedWriteLock ------------------------------------------------------------ timed_lock(M) //always locking, blocking timed_lock(M, lock_state) //blocking
timed_lock(M, lock_state, blocking_mode) timed_lock(M, blocking_mode) //always locking
timed_lock(M, t) //always locking, blocking for time t
Note that the addition of the try_lock stuff to ScopedTimedLock is a change. I don't disagree with that change. Just hilighting it. It is essentially alternative syntax for a timed_lock specified with a 0 delta time.
Yes, it's a change; thanks for highlighting it. I think that either TimedLock should refine TryLock, or else that Lock, TryLock, and TimedLock should be completely separate (as Vladimir suggested), none being refinements of any of the others.
Speaking of which, I've never been happy with "t" in the ScopedTimedLock docs. I've always felt it was ambiguous. Does t represent an absolute time, or a change in time? Both interpretations are useful. In the Metrowerks::threads lib I have:
scoped_timed_lock(mutex_type& m, const universal_time& unv_time); scoped_timed_lock(mutex_type& m, const elapsed_time& elps_time);
Where universal_time and elapsed_time are simplistic structs (like the boost time), but represent absolute and delta times respectively. They can also be added and subtracted as makes sense (e.g. you can add a universal_time and an elapsed_time, or two elapsed_time's, but you can't add two universal_time's). I took the Posix route and defined universal_time as the number of seconds and nanoseconds since midnight Jan. 1, 1970, UTC. The universal_time default constructor returns the current time.
So how about:
timed_lock(M) //always locking, blocking timed_lock(M, lock_state) //blocking
timed_lock(M, blocking_mode) //always locking
timed_lock(M, universal_time u) //always locking, blocking until u timed_lock(M, elapsed_time t) //always locking, blocking for elapsed time t
Yes, something needs to be done about time. There was a discussion of it recently by people who care about time issues, but I wasn't able to devote enough time to it then, and haven't gotten back to it. Perhaps we can address that issue later? The issues we're already dealing with (lock concept taxonomies, constructors, read/write locks, promotion) are complex enough for me already. Mike