RE: Can a thread write-lock a rwlock that it already has read-locked?

2004-10-18 Thread David Barrett
 -Original Message-
 From: Branko ibej [mailto:[EMAIL PROTECTED]
 Subject: Re: Can a thread write-lock a rwlock that it already has read-
 locked?
 
 David Barrett wrote:
 
 Anyway, that's my goal, and while I'd love any feedback on my approach
 (especially to point out any gaping logic holes I've overlooked), I think
 I'm all set.
 
 I think your algorithm is a deadlock waiting to happen. 

Yikes, that's a good example you give.  I will definitely keep an eye on
that.  

Also, I realize I was a bit glowing and flippant in extolling the virtues of
my proposal.  But its measure shouldn't be can it deadlock, because all
locking schemes can.  Rather, it should be is it easier to code
non-deadlocking code with this?  That's a judgment call, and even I haven't
made up my mind yet.

But I totally agree, inventing new locking schemes is a dangerous game, and
I definitely hear your advice on sticking closer to the tried and true
approaches.

Thanks for the discussion.

-david



Re: Can a thread write-lock a rwlock that it already has read-locked?

2004-10-17 Thread Branko Čibej
David Barrett wrote:
-Original Message-
From: Branko ibej [mailto:[EMAIL PROTECTED]
Subject: Re: Can a thread write-lock a rwlock that it already has read-
locked?
David Barrett wrote:
   

I'm experimenting with the reader/writer locks and have a question:
Can a thread write-lock a thread on which it already holds the read
lock?
 

No. A write lock is always exclusive.
   

Well yes, I agree.  It's just that a write-lock seems to be a superset of
the read lock.  In my mind, to have the write lock means I also must hold a
read lock, too (because I can both read and write).  Thus from this
perspective, write-locking when I already have the read lock is akin to a
write lock + nested read-lock.  Nested read locks are allowed (at least I
think they are -- nested mutex locks are, at least), so I think this should
be too.
 

I think you're confused by the read and write in the names of the 
lock types. The read is a shared lock, which means that several 
threads/processes/whatever can hold it at the same time, while the 
write is an exclusive lock. Neither kind actually controls whether you 
can read from or write to the protected resource, but since the whole 
idea of locking is to prevent concurrent modifications to a resource, 
the terms read lock and write lock are used synonymously with 
shared and exclusive

Anyway, this is all moot.  I understand that APR disallows my desired
behavior (with a reasonable justification), and as a workaround I'm using
mutexes to create a read/write lock that works as I want.
But while I'm on the moot point, I might add I think my desired behavior is
*much* easier to work with.  Consider:
# get read lock
#result = long operation;
#if( result ) {
#get write lock
#   do something
#release write lock
#}
# release read lock
 

Lock promotions are very hard to do correctly, and are usually not needed.
Without the ability to write lock while I hold the read lock, I'd need to
write-lock during the long operation, even though it's not algorithmically
required.  This blocks other threads for an unnecessarily long period.
 

But you can change your algorithm like this:
   done = false
   while not done:
 get shared lock
  result = long operation
  if result:
*release shared lock
*get exclusive lock
 if result is valid:
do something
done = true
 end if
 release exclusive lock
  end if
   end while
There is of course a race condition between releasing the shared lock 
and acquiring the exclusive lock, which is why you must check that the 
result is still valid after acquiring the exclusive lock


(If I had an atomic upgrade to write lock command I could do that too.
But without that, I can't safely give up the read lock and then grab the
write lock, because a thread might come in and invalidate the results of my
long operation.)
 

Excatly. That's why you have to check the validity of the result. If the 
price of having to recalculate the result is too high, or it happens too 
often, then its better to do everything under the exclusive lock.

But regardless, I get what APR does, I have a workaround, and all is right
in the world.
 

I wonder though iif your lock promotion scheme really works. Yes, it's 
possible to implement it, but I've never seen an implementation that, 
even when correct, is fair to all participating threads. For example, 
what happens if two threads try to promote their lock at the same time 
(that is, two are waiting on the promotion at the same time)? If you 
grant both requests, the one that comes in second will do something 
with a result that's no longer valid. If you only grant the first 
request, the second thread will have to recalculate its result again, 
and you end up with the same algorithm I outlined above, plus the 
overhead of a complex lock promotion implementation.

-- Brane


RE: Can a thread write-lock a rwlock that it already has read-locked?

2004-10-17 Thread David Barrett
 -Original Message-
 From: Branko ibej [mailto:[EMAIL PROTECTED]
 Subject: Re: Can a thread write-lock a rwlock that it already has read-
 locked?
 
 I wonder though iif your lock promotion scheme really works. Yes, it's
 possible to implement it, but I've never seen an implementation that,
 even when correct, is fair to all participating threads. For example,
 what happens if two threads try to promote their lock at the same time
 (that is, two are waiting on the promotion at the same time)? If you
 grant both requests, the one that comes in second will do something
 with a result that's no longer valid. If you only grant the first
 request, the second thread will have to recalculate its result again,
 and you end up with the same algorithm I outlined above, plus the
 overhead of a complex lock promotion implementation.

I think we agree on the following points:

1) read/write locks are merely names for shared or exclusive locks,
respectively -- it doesn't actually allow or prevent reading or writing
(that's up to the application).

2) The existing design of the read/write lock cannot prevent a race
condition in the long read operation / do something example, unless the
write lock is held (non-optimally) throughout the long read operation.


I mentioned lock promotion as one possible solution to the write lock
required during long read operation dilemma, though it's not my preferred
option.  Rather, my preferred option is to allow a thread to write lock so
long as it holds all of the read locks.  Metaphorically this is equivalent
to nested read lock + write lock.

Specifically, I'm proposing the following algorithm:

# struct rwlock {
#   mutex outer, inner;
#   stack readerStack;
# }
# void readLock( rwlock* rw ) {
#   lock( outer, inner ); // Blocks here if another thread is writing
#   push( readerStack, currentThreadID( ) );
#   unlock( inner, outer );
# }
# void readUnlock( rwlock* rw ) {
#   lock( outer, inner );
#   pop( readerStack );
#   unlock( inner, outer );
# }
# void writeLock( rwlock* rw ) {
#   lock( outer, inner );
#   while( another thread is reading ) {
#   unlock( inner, outer );
#   yield thread; // loops here while other threads read
#   lock( outer, inner );
#   }
#   // do not unlock inner
#   unlock( outer );
# }
# void writeUnlock( rwlock* rw ) {
#   unlock( inner );
# }

I'm not saying this implementation is without flaws (I especially don't like
that inner loop but I'm not sure how to get rid of it), but it illustrates
the behavior I'd like to have.  With this sort of read/write lock, functions
can read or write lock without knowing or caring about the lock state of the
calling thread:

# int longOperation( ) { 
#   readLock( );
#   ... 
#   readUnlock( );
# }
# void doIt( int result ) {
#   writeLock( );
#   ...
#   writeUnlock( );
# }
# void testAndDo( ) {
#   readLock( );
#   int result;
#   if( result = longOperation( ) ) doIt( result );
#   readUnlock( );
# }
# void justDoIt( ) {
#   writeLock( );
#   doIt( longOperation( ) );
#   writeUnlock( );
# }

I think this locking behavior better encapsulates functionality as the
caller doesn't need to know the locking needs (or restrictions) of the
function, and the function doesn't need to know the locking state of the
caller.

I believe APR's mutex operations are sufficient for creating this
implementation.  It's a little tricky as I need to either lock or fail to
lock multiple mutexes together (ie, if I can't lock both I don't want to
lock either), but I think I can do this with the following:

# void lock2( lockA, lockB ) {
#   while( 1 ) {
#   lock( lockA );
#   if( trylock( lockB ) ) return;
#   else unlock( lockA );
#   }
# }

Anyway, that's my goal, and while I'd love any feedback on my approach
(especially to point out any gaping logic holes I've overlooked), I think
I'm all set.

-david 



Re: Can a thread write-lock a rwlock that it already has read-locked?

2004-10-17 Thread Branko Čibej
David Barrett wrote:
Anyway, that's my goal, and while I'd love any feedback on my approach
(especially to point out any gaping logic holes I've overlooked), I think
I'm all set.
 

I think your algorithm is a deadlock waiting to happen. Consider the 
following situation:

   thraad A acquires read lock
   thread B acquires read lock
   thread C acquires read lock
   thread A requests write lock (blocks)
   thread B requests write lock (blocks)
   thread C releases read lock
At this point, threads A and B are both waiting for other readers to 
release their locks, and there are no writers yet. So they're waiting 
for each other. Even if you allow a single thread to own a write and any 
number of read locks simultaneously, you can't avoid this kind of 
deadlock unless each thread releases all its read locks before acquiring 
a write lock. The best you can do is to fail a write-lock request if 
another thread is waiting for the write lock, but to avoid the deadlock 
the thread whose write-lock failed would still have to release all its 
read locks before retrying the request.

Again, you end up with the algorithm I showed in my previous post, 
except that the code gets even more complex.

All in all, I'd advise not trying to invent a better way of locking, 
but to use proven methods. Locking is notoriously tricky.

-- Brane


RE: Can a thread write-lock a rwlock that it already has read-locked?

2004-10-16 Thread David Barrett
 -Original Message-
 From: Branko ibej [mailto:[EMAIL PROTECTED]
 Subject: Re: Can a thread write-lock a rwlock that it already has read-
 locked?
 
 David Barrett wrote:
 
 I'm experimenting with the reader/writer locks and have a question:
 
  Can a thread write-lock a thread on which it already holds the read
 lock?
 

 No. A write lock is always exclusive.

Well yes, I agree.  It's just that a write-lock seems to be a superset of
the read lock.  In my mind, to have the write lock means I also must hold a
read lock, too (because I can both read and write).  Thus from this
perspective, write-locking when I already have the read lock is akin to a
write lock + nested read-lock.  Nested read locks are allowed (at least I
think they are -- nested mutex locks are, at least), so I think this should
be too.

Anyway, this is all moot.  I understand that APR disallows my desired
behavior (with a reasonable justification), and as a workaround I'm using
mutexes to create a read/write lock that works as I want.

But while I'm on the moot point, I might add I think my desired behavior is
*much* easier to work with.  Consider:

# get read lock
#result = long operation;
#if( result ) {
#get write lock
#   do something
#release write lock
#}
# release read lock

Without the ability to write lock while I hold the read lock, I'd need to
write-lock during the long operation, even though it's not algorithmically
required.  This blocks other threads for an unnecessarily long period.

(If I had an atomic upgrade to write lock command I could do that too.
But without that, I can't safely give up the read lock and then grab the
write lock, because a thread might come in and invalidate the results of my
long operation.)

But regardless, I get what APR does, I have a workaround, and all is right
in the world.

-david



Re: Can a thread write-lock a rwlock that it already has read-locked?

2004-10-16 Thread Branko Čibej
David Barrett wrote:
I'm experimenting with the reader/writer locks and have a question:
	Can a thread write-lock a thread on which it already holds the read
lock?
 

No. A write lock is always exclusive.
-- Brane


Can a thread write-lock a rwlock that it already has read-locked?

2004-10-15 Thread David Barrett
I'm experimenting with the reader/writer locks and have a question:

Can a thread write-lock a thread on which it already holds the read
lock?

My tests seem to indicate that this is not allowed, but I want to confirm
this is by design and not something I'm just doing wrong.  Specifically, I'm
using the following code:

# apr_pool_t* pool;
# apr_pool_create( pool, 0 );
# apr_thread_rwlock_t* lock;
# apr_thread_rwlock_create( lock, pool );
# apr_thread_rwlock_rdlock( lock );
# apr_thread_rwlock_wrlock( lock ); // Hangs on this line
# apr_thread_rwlock_unlock( lock );

Is this working as designed?  I'm running this test on Win32.

My followup question is, is there any way to override this behavior?  It
would seem to me that a thread could safely hold both a read and write lock,
so long as no other thread held either.  Furthermore, it'd be nice to give
up the write lock without giving up the read lock.  The pattern I'd like to
use is something like:

# get read lock
#do some tests to decide if I need to write anything
#   get the write lock
#  make some changes
#   give up the write lock
#do some more stuff just with the read lock
# give up the read lock

Is there something fundamentally wrong with this pattern that I'm not
seeing?  Is there some compelling reason why the rwlock must disallow a
thread from holding both a read and write lock?

-david




Re: Can a thread write-lock a rwlock that it already has read-locked?

2004-10-15 Thread Joe Orton
On Thu, Oct 14, 2004 at 06:17:39PM -0600, David Barrett wrote:
 I'm experimenting with the reader/writer locks and have a question:
 
   Can a thread write-lock a thread on which it already holds the read
 lock?

Hey, I answered that one already :) apr_thread_rwlock.h in 1.0 says:

 * Note: The following operations have undefined results: unlocking a
 * read-write lock which is not locked in the calling thread; write
 * locking a read-write lock which is already locked by the calling
 * thread; ...

 My followup question is, is there any way to override this behavior?  It
 would seem to me that a thread could safely hold both a read and write lock,
 so long as no other thread held either.  Furthermore, it'd be nice to give
 up the write lock without giving up the read lock.  The pattern I'd like to
 use is something like:

No, there's no way round it, APR just matches what the underlying rwlock
semantics are.  It's just the case that you have to take the write lock
up front if you're going to be making changes, I suppose.

joe