class TestCase::ThreadSafe {
  use Point;
  use TestCase::Util::Thread;
  use TestCase::Util::Thread::ID;
  use TestCase::Util::Thread::ThisThread;
  use TestCase::Operator::Data::Weaken;
  
  static method assign : int () {
    
    # undef
    {
      my $point_ref = [(Point)undef];
      
      my $thread = TestCase::Util::Thread->new([$point_ref : Point[]] method : void () {
        
        for (my $i = 0; $i < 10000; $i++) {
          my $point = Point->new;
          $point_ref->[0] = $point;
        }
      });
      
      my $thread2 = TestCase::Util::Thread->new([$point_ref : Point[]] method : void () {
        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([$point_ref : Point[]] method : void () {
        
        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([$point_ref : Point[]] method : void () {
        
        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 weaken : int () {

    my $object1 = new TestCase::Operator::Data::Weaken;
    $object1->{x} =1;
    
    my $object2 = new TestCase::Operator::Data::Weaken;
    $object2->{x} = 2;
    
    # Circular reference
    $object2->{data_weaken} = $object1;
    $object1->{data_weaken} = $object2;
    
    {
      my $object_ref = [(TestCase::Operator::Data::Weaken)$object1, $object2];
      
      my $thread = TestCase::Util::Thread->new([$object_ref : TestCase::Operator::Data::Weaken[]] method : void () {
        
        for (my $i = 0; $i < 10000; $i++) {
          my $object = $object_ref->[0];
          weaken $object->{data_weaken};
          $object->{data_weaken} = $object->{data_weaken};
          unweaken $object->{data_weaken};
          $object->{data_weaken} = $object->{data_weaken};
          isweak $object->{data_weaken};;
        }
      });
      
      my $thread2 = TestCase::Util::Thread->new([$object_ref : TestCase::Operator::Data::Weaken[]] method : void () {
        
        for (my $i = 0; $i < 10000; $i++) {
          my $object = $object_ref->[0];
          weaken $object->{data_weaken};
          $object->{data_weaken} = $object->{data_weaken};
          unweaken $object->{data_weaken};
          $object->{data_weaken} = $object->{data_weaken};
          isweak $object->{data_weaken};;
        }
      });
      
      $thread->join;
      $thread2->join;
      
      unless ($object1->{x} == 1 && $object2->{x} == 2) {
        return 0;
      }
      
      weaken $object1->{data_weaken};
    }
    
    return 1;
  }
}
