class TestCase::Module::Sync::Mutex {
  
  use Sync::Mutex;
  use TestCase::Util::Thread;
  use Point;
  
  our $VALUE : public int;
  
  our $VALUES : public int[];
  
  our $MUTEX : public Sync::Mutex;
  
  our $INCREMENT_VALUE : public int;
  
  INIT {
    $MUTEX = Sync::Mutex->new;
  }
  
  static method basic : int () {
    
    my $mutex = Sync::Mutex->new;
    
    $mutex->lock;
    
    $mutex->unlock;
    
    $mutex->reader_lock;
    
    $mutex->reader_unlock;
    
    return 1;
  }
  
  static method assign : int () {
    
    # undef
    {
      my $point_ref = [(Point)undef];
      
      my $thread = TestCase::Util::Thread->new([has point_ref : Point[] = $point_ref] method : void () {
        
        my $point_ref = $self->{point_ref};
        
        for (my $i = 0; $i < 10000; $i++) {
          my $point = Point->new;
          $point_ref->[0] = $point;
        }
      });
      
      my $thread2 = TestCase::Util::Thread->new([has point_ref : Point[] = $point_ref] method : void () {
        my $point_ref = $self->{point_ref};
        
        for (my $i = 0; $i < 10000; $i++) {
          $point_ref->[0] = undef;
        }
      });
      
      $thread->join;
      $thread2->join;
      
    }

    # All patterns
    {
      my $point_ref = [(Point)undef];
      
      my $thread = TestCase::Util::Thread->new([has point_ref : Point[] = $point_ref] method : void () {
        
        my $point_ref = $self->{point_ref};
        
        for (my $i = 0; $i < 10000; $i++) {
          my $point = Point->new;
          $point_ref->[0] = $point;
          $point_ref->[0] = $point;
          $point_ref->[0] = Point->new;
          $point_ref->[0] = undef;
          $point_ref->[0] = Point->new;
        }
      });
      
      my $thread2 = TestCase::Util::Thread->new([has point_ref : Point[] = $point_ref] method : void () {
        my $point_ref = $self->{point_ref};
        
        for (my $i = 0; $i < 10000; $i++) {
          my $point = Point->new;
          $point_ref->[0] = $point;
          $point_ref->[0] = $point;
          $point_ref->[0] = Point->new;
          $point_ref->[0] = undef;
          $point_ref->[0] = Point->new;
        }
      });
      
      $thread->join;
      $thread2->join;
      
      unless ($point_ref->[0] is_type Point) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method increment : int () {
    
    my $thread = TestCase::Util::Thread->new(method : void () {
      my $mutex = $TestCase::Module::Sync::Mutex::MUTEX;
      
      for (my $i = 0; $i < 10000; $i++) {
        $mutex->lock;
        
        $TestCase::Module::Sync::Mutex::INCREMENT_VALUE++;
        
        $mutex->unlock;
      }
    });
    
    my $thread2 = TestCase::Util::Thread->new(method : void () {
      my $mutex = $TestCase::Module::Sync::Mutex::MUTEX;
      
      for (my $i = 0; $i < 10000; $i++) {
        $mutex->lock;
        
        $TestCase::Module::Sync::Mutex::INCREMENT_VALUE++;
        
        $mutex->unlock;
      }
    });
    
    $thread->join;
    $thread2->join;
    
    unless ($TestCase::Module::Sync::Mutex::INCREMENT_VALUE == 20000) {
      return 0;
    }
    
    return 1;
  }
  
  static method thread : int () {
    
    $TestCase::Module::Sync::Mutex::VALUE = 0;
    
    $TestCase::Module::Sync::Mutex::VALUES = [0, 0];
    
    my $values = [0, 0];
    
    my $thread = TestCase::Util::Thread->new([has foo : int[] = $values] method : void () {
      my $mutex = $TestCase::Module::Sync::Mutex::MUTEX;
      
      $mutex->lock;
      
      $mutex->unlock;
      
      $mutex->reader_lock;
      
      $mutex->reader_unlock;
      
      $TestCase::Module::Sync::Mutex::VALUE = 1;
      
      $TestCase::Module::Sync::Mutex::VALUES->[0] = 2;
      
      $self->{foo}[0] = 5;
      
    });
    
    my $thread2 = TestCase::Util::Thread->new([has foo : int[] = $values] method : void () {
      my $mutex = $TestCase::Module::Sync::Mutex::MUTEX;
      
      $mutex->lock;
      
      $mutex->unlock;
      
      $mutex->reader_lock;
      
      $mutex->reader_unlock;
      
      $TestCase::Module::Sync::Mutex::VALUES->[1] = 3;
      
      $self->{foo}[1] = 7;
    });
    
    $thread->join;
    $thread2->join;
    
    unless ($VALUE == 1) {
      return 0;
    }
    
    unless ($VALUES->[0] == 2) {
      return 0;
    }
    
    unless ($VALUES->[1] == 3) {
      return 0;
    }
    
    unless ($values->[0] == 5) {
      return 0;
    }
    
    unless ($values->[1] == 7) {
      return 0;
    }
    
    $VALUES = undef;
    
    return 1;
  }
}
