mukul-rathi / bolt

Bolt is a language with in-built data-race freedom!
MIT License
564 stars 51 forks source link

Refactor lock LLVM IR generation code #108

Open mukul-rathi opened 4 years ago

mukul-rathi commented 4 years ago

Create a struct containing locking info and have a pointer in the class

class Foo{
  struct *lockingInfo

}
struct{
    bool isLockBeingUpdated;
    pthread_t *ownerThread;
    int writerCount;
    int readerCount;
}

And then we refactor so calling lock / unlock will call a function rather than generating IR for each instance of lock/unlock.

_lock(struct x, locktype){

if(lock is being updated){
  spin;
}
currThreadOwnsLock = check current pthread with the lock's pthread
noWritersPresentOnOtherThreads = numWriters ==0 || currThreadOwnsLock

 if( type == Reader){
       canAcquireLock = noWritersPresentOnOtherThreads; 
   }
else{ // mode is writer
   canAcquireLock = noWritersPresentOnOtherThreads && noReadersPresent;
}

CAS( lockUpdated, oldval=0, newVal=1);
// if fail then loop to lock update

// now we're in critical section to update lock info (meta lock within a lock)
if reader{
   readerCount++
}

if writer { 
update owning thread to this thread
writerCount++
}

mem fence

store lockUpdated -> 0 // release update lock
}

And for the unlock operation

unlock( struct, lock type){

    spin on LockUpdate mutex 

    if (reader lock){
           readerCount--
      }

     if writer { 
             writerCount--
    }
mem fence

store lockUpdated -> 0 // release update lock

}
mukul-rathi commented 4 years ago

A better solution: See https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java

Store reader + writer lock asshorts (16 bits) each - so the upper bits of a int32 refers to reader count, the lower bits refer to reader.

Then update = a single CAS instead of updating reader & writer count individually.

This means you don't need all this faff of another bool field.

mukul-rathi commented 4 years ago

the lock / unlock functions should take in two arguments - the memory addresses of the pthread id and lock counter respectively. (llvm::PointerType ?)

mukul-rathi commented 4 years ago

when releasing writer lock need to set threadID to null (to prevent other threads reading a stale copy of the owning thread).

mukul-rathi commented 4 years ago

fix #122 whilst at it

mukul-rathi commented 4 years ago

Could instead try to use a pthread_rw_mutex() with a custom field that checks owner thread set - so don't try to acquire lock if thread has already acquired it. This simplifies the custom lock and also makes sure we have reentrancy as pthread_rw_mutex()deadlocks on reentrancy

mukul-rathi commented 4 years ago

(Bug - not updating field)

let (i = 0; i< 10; i++){
x.f := x.f + i 
}

x.f is set to 0 if using a locked capability, rather than 55 (1+2+...+10).

mukul-rathi commented 4 years ago

Just write the lock in C and link in the function, and use C atomics / insert memory fence using LLVM.

mukul-rathi commented 4 years ago

Right now the lock is taken on the whole object, we should instead have a lock per capability, covering only those fields touched by the capability.