Fix failing test case missed during da9346a03
[dbsrgits/DBIx-Class.git] / t / 51threads.t
1 BEGIN { do "./t/lib/ANFANG.pm" or die ( $@ || $! ) }
2
3 use Config;
4 BEGIN {
5   unless ($Config{useithreads}) {
6     print "1..0 # SKIP your perl does not support ithreads\n";
7     exit 0;
8   }
9
10   if ($INC{'Devel/Cover.pm'}) {
11     print "1..0 # SKIP Devel::Cover does not work with threads yet\n";
12     exit 0;
13   }
14 }
15 use threads;
16
17 use DBIx::Class::Optional::Dependencies -skip_all_without => 'test_rdbms_pg';
18
19 use strict;
20 use warnings;
21
22 use Test::More;
23 use Test::Exception;
24 use Time::HiRes qw(time sleep);
25
26 plan skip_all => 'DBIC does not actively support threads before perl 5.8.5'
27   if "$]" < 5.008005;
28
29
30 use DBICTest;
31
32 # README: If you set the env var to a number greater than 5,
33 #   we will use that many children
34 my $num_children = $ENV{DBICTEST_THREAD_STRESS} || 1;
35 if($num_children !~ /^[0-9]+$/ || $num_children < 5) {
36    $num_children = 5;
37 }
38
39 my ($dsn, $user, $pass) = @ENV{map { "DBICTEST_PG_${_}" } qw/DSN USER PASS/};
40
41 my $schema = DBICTest::Schema->connect($dsn, $user, $pass, { AutoCommit => 1, RaiseError => 1, PrintError => 0 });
42
43 my $parent_rs;
44
45 lives_ok (sub {
46     my $dbh = $schema->storage->dbh;
47
48     {
49         local $SIG{__WARN__} = sub {};
50         eval { $dbh->do("DROP TABLE cd") };
51         $dbh->do("CREATE TABLE cd (cdid serial PRIMARY KEY, artist INTEGER NOT NULL UNIQUE, title VARCHAR(100) NOT NULL UNIQUE, year VARCHAR(100) NOT NULL, genreid INTEGER, single_track INTEGER);");
52     }
53
54     $schema->resultset('CD')->create({ title => 'vacation in antarctica', artist => 123, year => 1901 });
55     $schema->resultset('CD')->create({ title => 'vacation in antarctica part 2', artist => 456, year => 1901 });
56
57     $parent_rs = $schema->resultset('CD')->search({ year => 1901 });
58     is ($parent_rs->count, 2);
59 }, 'populate successfull');
60
61 # basic tests
62 {
63   ok ($schema->storage->connected(), 'Parent is connected');
64   is ($parent_rs->next->id, 1, 'Cursor advanced');
65   my $ct_num = Test::More->builder->current_test;
66
67   my $newthread = async {
68     my $out = '';
69
70     #simulate a  subtest to not confuse the parent TAP emission
71     my $tb = Test::More->builder;
72     $tb->reset;
73     for (qw/output failure_output todo_output/) {
74       close $tb->$_;
75       open ($tb->$_, '>', \$out);
76     }
77
78     ok(!$schema->storage->connected, "storage->connected() false in child");
79     for (1,2) {
80       throws_ok { $parent_rs->next } qr/\QMulti-thread access attempted while cursor in progress (position 1)/;
81     }
82
83     $parent_rs->reset;
84     is($parent_rs->next->id, 1, 'Resetting cursor reprepares it within child environment');
85
86     done_testing;
87
88     close $tb->$_ for (qw/output failure_output todo_output/);
89     sleep (0.2); # tasty crashes without this
90
91     $out;
92   };
93   die "Thread creation failed: $! $@" if !defined $newthread;
94
95   my $out = $newthread->join;
96   $out =~ s/^/   /gm;
97   print $out;
98
99   # workaround for older Test::More confusing the plan under threads
100   Test::More->builder->current_test($ct_num);
101
102   is ($parent_rs->next->id, 2, 'Cursor still intact in parent');
103   is ($parent_rs->next, undef, 'Cursor exhausted');
104 }
105
106 $parent_rs->reset;
107
108 # sleep until this spot so everything starts simultaneously
109 # add "until turn of second" for prettier display
110 my $t = int( time() ) + 4;
111
112 my @children;
113 while(@children < $num_children) {
114
115     my $newthread = async {
116         my $tid = threads->tid;
117
118         sleep ($t - time);
119
120         # FIXME if we do not stagger the threads, sparks fly due to CXSA
121         sleep ( $tid / 10 ) if "$]" < 5.012;
122
123         note ("Thread $tid starting work at " . time() );
124
125         my $child_rs = $schema->resultset('CD')->search({ year => 1901 });
126         my $row = $parent_rs->next;
127         if($row && $row->get_column('artist') =~ /^(?:123|456)$/) {
128             $schema->resultset('CD')->create({ title => "test success $tid", artist => $tid, year => scalar(@children) });
129         }
130
131         sleep (0.2); # without this many tasty crashes even on latest perls
132     };
133     die "Thread creation failed: $! $@" if !defined $newthread;
134     push(@children, $newthread);
135 }
136
137 ok(1, "past spawning");
138
139 my @tids;
140 for (@children) {
141   push @tids, $_->tid;
142   $_->join;
143 }
144
145 ok(1, "past joining");
146
147 while (@tids) {
148     my $tid = pop @tids;
149     my $rs = $schema->resultset('CD')->search({ title => "test success $tid", artist => $tid, year => scalar(@tids) });
150     is($rs->next->get_column('artist'), $tid, "Child $tid successful");
151 }
152
153 ok(1, "Made it to the end");
154 undef $parent_rs;
155
156 $schema->storage->dbh->do("DROP TABLE cd");
157
158 # Too many threading bugs on exit, none of which have anything to do with
159 # the actual stuff we test
160 $ENV{DBICTEST_DIRTY_EXIT} = 1
161   if "$]" < 5.012;
162
163 done_testing;