-
Notifications
You must be signed in to change notification settings - Fork 558
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spawning threads with open directory handles causes a crash #10387
Comments
From alexford@live.comCreated by alexford@live.comQuick summary: if you do an opendir() and then spawn threads, perl will crash. Perl Info
|
From @tseeHi Alex, Alexander Ford wrote:
I only had a system perl 5.10.0 with a recent threads.pm from CPAN Best regards, |
The RT System itself - Status changed from 'new' to 'open' |
From @jdheddenSteffen, Alex, What version of threads are you using? perl -Mthreads -e 'print $threads::VERSION, "\n"' I tried your test with threads 1.77 on 5.8.8/9, 5.10.0/1, 5.12.0/1 and If you can upgrade to threads 1.77 and test again, it may be that this Thanks, On Tue, May 18, 2010 at 05:52, Steffen Mueller <smueller@cpan.org> wrote:
|
From @cpansproutThis is the result of this function in sv.c: DIR * Unfortunately, I don’t know how to duplicate a directory handle The attached patch (not to be applied; just an example) makes a copy I can see three ultimate solutions (in order of preference): a) Properly duplicate the dir handle so we have a new one with its own If b or c happens, I think this needs to be documented in threads.pm. |
From @cpansproutInline Patch--- blead/sv.c 2010-05-21 12:02:12.000000000 -0700
+++ blead-75154/sv.c 2010-05-23 15:47:28.000000000 -0700
@@ -10658,11 +10611,22 @@ Perl_fp_dup(pTHX_ PerlIO *const fp, cons
DIR *
Perl_dirp_dup(pTHX_ DIR *const dp)
{
+ DIR *ret;
+
PERL_UNUSED_CONTEXT;
if (!dp)
return (DIR*)NULL;
- /* XXX TODO */
- return dp;
+
+ /* look for it in the table first */
+ ret = (DIR*)ptr_table_fetch(PL_ptr_table, dp);
+ if (ret)
+ return ret;
+
+ /* create anew and remember what it is */
+ ret = PerlDir_open(".");
+ PerlLIO_dup2(my_dirfd(dp), my_dirfd(ret));
+ ptr_table_store(PL_ptr_table, dp, ret);
+ return ret;
}
/* duplicate a typeglob */ |
From @tseeHi Jerry, Jerry D. Hedden wrote:
It was threads 1.75. Installing threads 1.77 from CPAN into the ubuntu #0 0x00007ffff6ba04b5 in *__GI_raise (sig=<value optimized out>) at Sorry for the delayed reply! Cheers, |
From sinantrop@gmail.comThis is a bug report for perl from sinantrop@gmail.com, This program crashes perl on windows with those messages: sub ListDir local *DH; ListDir(".", sub fork; Flags: Site configuration information for perl 5.12.1: Configured by SYSTEM at Fri May 14 00:24:46 2010. Summary of my perl5 (revision 5 version 12 subversion 1) configuration: Locally applied patches: @INC for perl 5.12.1: Environment for perl 5.12.1: |
From @chornyOn Wed Sep 01 10:03:57 2010, strop wrote:
I'm not sure that it is a good idea to mix dirhandles and fork/threads. opendir(my $dh,$dir); ListDir(".", sub fork; It's result are meaningless though - for last file $proc will be -- |
The RT System itself - Status changed from 'new' to 'open' |
From @jdheddenOn Wed Sep 01 10:03:57 2010, strop wrote:
PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;c:\perl\bin
|
From @jdhedden(Oops. Sorry about the previous message. Hit the wrong button.) This bug report is really a duplicate of #75154 fork() under Windows uses threads. And the duplication of dir handles On Wed Sep 01 10:03:57 2010, strop wrote:
|
From @cpansproutOn Tue May 25 01:42:10 2010, sprout wrote:
Attached is a patch to fix this. On systems with fchdir, it dwims (a). |
From @cpansproutFrom: Father Chrysostomos <sprout@cpan.org> [perl #75174] Clone dir handles On systems that support fchdir, use it to clone dir handles. On other systems, at least for now, don’t give the new thread a copy Inline Patchdiff -up blead/MANIFEST blead-75154-dirdup/MANIFEST
--- blead/MANIFEST 2010-09-11 14:19:12.000000000 -0700
+++ blead-75154-dirdup/MANIFEST 2010-09-17 12:49:00.000000000 -0700
@@ -4635,6 +4635,7 @@ t/op/symbolcache.t See if undef/delete
t/op/sysio.t See if sysread and syswrite work
t/op/taint.t See if tainting works
t/op/threads_create.pl Ancillary file for t/op/threads.t
+t/op/threads-dirh.t Test interaction of threads and dir handles
t/op/threads.t Misc. tests for perl features with threads
t/op/tiearray.t See if tie for arrays works
t/op/tie_fetch_count.t See if FETCH is only called once on tied variables
diff -up blead/sv.c blead-75154-dirdup/sv.c
--- blead/sv.c 2010-09-09 12:00:10.000000000 -0700
+++ blead-75154-dirdup/sv.c 2010-09-18 06:21:19.000000000 -0700
@@ -10849,11 +10849,95 @@ Perl_fp_dup(pTHX_ PerlIO *const fp, cons
DIR *
Perl_dirp_dup(pTHX_ DIR *const dp)
{
+#ifdef HAS_FCHDIR
+ DIR *ret;
+ DIR *pwd;
+ register const Direntry_t *dirent;
+ char smallbuf[256];
+ char *name = NULL;
+ STRLEN len;
+ long pos;
+#endif
+
PERL_UNUSED_CONTEXT;
+
+#ifdef HAS_FCHDIR
if (!dp)
return (DIR*)NULL;
- /* XXX TODO */
- return dp;
+ /* look for it in the table first */
+ ret = (DIR*)ptr_table_fetch(PL_ptr_table, dp);
+ if (ret)
+ return ret;
+
+ /* create anew */
+
+ /* open the current directory (so we can switch back) */
+ if (!(pwd = PerlDir_open("."))) return (DIR *)NULL;
+
+ /* chdir to our dir handle and open the present working directory */
+ if (fchdir(my_dirfd(dp)) < 0 || !(ret = PerlDir_open("."))) {
+ PerlDir_close(pwd);
+ return (DIR *)NULL;
+ }
+ /* Now we should have two dir handles pointing to the same dir. */
+
+ /* Be nice to the calling code and chdir back to where we were. */
+ fchdir(my_dirfd(pwd)); /* If this fails, then what? */
+
+ /* We have no need of the pwd handle any more. */
+ PerlDir_close(pwd);
+
+ /* Iterate once through dp, to get the file name at the current posi-
+ tion. Then step back. */
+ pos = PerlDir_tell(dp);
+ if ((dirent = PerlDir_read(dp))) {
+ len = dirent->d_namlen;
+ if (len <= sizeof smallbuf) name = smallbuf;
+ else Newx(name, len, char);
+ Move(dirent->d_name, name, len, char);
+ }
+ PerlDir_seek(dp, pos);
+
+ /* Iterate through the new dir handle, till we find a file with the
+ right name. */
+ if (!dirent) /* just before the end */
+ for(;;) {
+ pos = PerlDir_tell(ret);
+ if (PerlDir_read(ret)) continue; /* not there yet */
+ PerlDir_seek(ret, pos); /* step back */
+ break;
+ }
+ else {
+ const long pos0 = PerlDir_tell(ret);
+ for(;;) {
+ pos = PerlDir_tell(ret);
+ if ((dirent = PerlDir_read(ret))) {
+ if (len == dirent->d_namlen
+ && memEQ(name, dirent->d_name, len)) {
+ /* found it */
+ PerlDir_seek(ret, pos); /* step back */
+ break;
+ }
+ /* else we are not there yet; keep iterating */
+ }
+ else { /* This is not meant to happen. The best we can do is
+ reset the iterator to the beginning. */
+ PerlDir_seek(ret, pos0);
+ break;
+ }
+ }
+ }
+
+ if (name && name != smallbuf)
+ Safefree(name);
+
+ /* pop it in the pointer table */
+ ptr_table_store(PL_ptr_table, dp, ret);
+
+ return ret;
+#else
+ return (DIR*)NULL;
+#endif
}
/* duplicate a typeglob */
diff -up blead/dist/threads/lib/threads.pm blead-75154-dirdup/dist/threads/lib/threads.pm
--- blead/dist/threads/lib/threads.pm 2010-07-07 07:22:10.000000000 -0700
+++ blead-75154-dirdup/dist/threads/lib/threads.pm 2010-09-17 01:12:59.000000000 -0700
@@ -1005,6 +1005,16 @@ mutexes that are needed to control funct
For this reason, the use of C<END> blocks in threads is B<strongly>
discouraged.
+=item Directory handles
+
+In perl 5.14.0 and higher, if your system does not support the C<fchdir> C
+function, directory handles will not be copied to new threads. You can use
+the C<d_fchdir> variable in L<Config.pm|Config> to determine whether your
+system supports it.
+
+In prior perl versions, leaving directory handles open when threads were
+created could result in crashes or memory corruption.
+
=item Perl Bugs and the CPAN Version of L<threads>
Support for threads extends beyond the code in this module (i.e.,
diff -rNup blead/t/op/threads-dirh.t blead-75154-dirdup/t/op/threads-dirh.t
--- blead/t/op/threads-dirh.t 1969-12-31 16:00:00.000000000 -0800
+++ blead-75154-dirdup/t/op/threads-dirh.t 2010-09-18 06:17:53.000000000 -0700
@@ -0,0 +1,131 @@
+#!perl
+
+# Test interaction of threads and directory handles.
+
+BEGIN {
+ chdir 't' if -d 't';
+ @INC = '../lib';
+ require './test.pl';
+ $| = 1;
+
+ require Config;
+ if (!$Config::Config{useithreads}) {
+ print "1..0 # Skip: no ithreads\n";
+ exit 0;
+ }
+ if ($ENV{PERL_CORE_MINITEST}) {
+ print "1..0 # Skip: no dynamic loading on miniperl, no threads\n";
+ exit 0;
+ }
+
+ plan(6);
+}
+
+use strict;
+use warnings;
+use threads;
+use threads::shared;
+use File::Path;
+use File::Spec::Functions qw 'updir catdir';
+use Cwd 'getcwd';
+
+# Basic sanity check: make sure this does not crash
+fresh_perl_is <<'# this is no comment', 'ok', {}, 'crash when duping dirh';
+ use threads;
+ opendir dir, 'op';
+ async{}->join for 1..2;
+ print "ok";
+# this is no comment
+
+my $dir;
+SKIP: {
+ my $skip = sub {
+ chdir($dir);
+ chdir updir;
+ skip $_[0], 5
+ };
+
+ if(!$Config::Config{d_fchdir}) {
+ $::TODO = 'dir handle cloning currently requires fchdir';
+ }
+
+ my @w :shared; # warnings accumulator
+ local $SIG{__WARN__} = sub { push @w, $_[0] };
+
+ $dir = catdir getcwd(), "thrext$$" . int rand() * 100000;
+
+ rmtree($dir);
+ mkdir($dir);
+
+ # Create a dir structure like this:
+ # $dir
+ # |
+ # `- toberead
+ # |
+ # +---- thrit
+ # |
+ # +---- rile
+ # |
+ # `---- zor
+
+ chdir($dir);
+ mkdir 'toberead';
+ chdir 'toberead';
+ {open my $fh, ">thrit" or &$skip("Cannot create file thrit")}
+ {open my $fh, ">rile" or &$skip("Cannot create file rile")}
+ {open my $fh, ">zor" or &$skip("Cannot create file zor")}
+ chdir updir;
+
+ # Then test that dir iterators are cloned correctly.
+
+ opendir my $toberead, 'toberead';
+ my $start_pos = telldir $toberead;
+ my @first_2 = (scalar readdir $toberead, scalar readdir $toberead);
+ my @from_thread = @{; async { [readdir $toberead ] } ->join };
+ my @from_main = readdir $toberead;
+ is join('-', sort @from_thread), join('-', sort @from_main),
+ 'dir iterator is copied from one thread to another';
+ like
+ join('-', "", sort(@first_2, @from_thread), ""),
+ qr/(?<!-rile)-rile-thrit-zor-(?!zor-)/i,
+ 'cloned iterator iterates exactly once over everything not already seen';
+
+ seekdir $toberead, $start_pos;
+ readdir $toberead for 1 .. @first_2+@from_thread;
+ is
+ async { readdir $toberead // 'undef' } ->join, 'undef',
+ 'cloned dir iterator that points to the end of the directory'
+ ;
+
+ # Make sure the cloning code can handle file names longer than 255 chars
+ SKIP: {
+ chdir 'toberead';
+ open my $fh,
+ ">floccipaucinihilopilification-"
+ . "pneumonoultramicroscopicsilicovolcanoconiosis-"
+ . "lopadotemachoselachogaleokranioleipsanodrimypotrimmatosilphiokarabo"
+ . "melitokatakechymenokichlepikossyphophattoperisteralektryonoptokephal"
+ . "liokinklopeleiolagoiosiraibaphetraganopterygon"
+ or
+ chdir updir,
+ skip("OS does not support long file names (and I mean *long*)", 1);
+ chdir updir;
+ opendir my $dirh, "toberead";
+ my $test_name
+ = "dir iterators can be cloned when the next fn > 255 chars";
+ while() {
+ my $pos = telldir $dirh;
+ my $fn = readdir($dirh);
+ if(!defined $fn) { fail($test_name); last SKIP; }
+ if($fn =~ 'lagoio') {
+ seekdir $dirh, $pos;
+ last;
+ }
+ }
+ is length async { scalar readdir $dirh } ->join, 257, $test_name;
+ }
+
+ is scalar @w, 0, 'no warnings during all that' or diag @w;
+ chdir updir;
+}
+rmtree($dir); |
From @jdheddenThis patch is written to rely on dirent->d_namlen which doesn't always Also, there's a build warning about 'len' possibly being On Sun, Sep 19, 2010 at 16:10, Father Chrysostomos via RT
|
From @cpansproutOn Tue Sep 21 10:59:20 2010, jdhedden wrote:
Why +1? d_namlen does not include the null. Does this new patch work for you? |
From @cpansproutFrom: Father Chrysostomos <sprout@cpan.org> [perl #75174] Clone dir handles On systems that support fchdir, use it to clone dir handles. On other systems, at least for now, don’t give the new thread a copy Inline Patchdiff -Nup blead/MANIFEST blead-75154-dirdup2/MANIFEST
--- blead/MANIFEST 2010-09-19 16:19:10.000000000 -0700
+++ blead-75154-dirdup2/MANIFEST 2010-09-21 11:16:37.000000000 -0700
@@ -4637,6 +4637,7 @@ t/op/symbolcache.t See if undef/delete
t/op/sysio.t See if sysread and syswrite work
t/op/taint.t See if tainting works
t/op/threads_create.pl Ancillary file for t/op/threads.t
+t/op/threads-dirh.t Test interaction of threads and dir handles
t/op/threads.t Misc. tests for perl features with threads
t/op/tiearray.t See if tie for arrays works
t/op/tie_fetch_count.t See if FETCH is only called once on tied variables
diff -Nup blead/sv.c blead-75154-dirdup2/sv.c
--- blead/sv.c 2010-09-16 07:47:14.000000000 -0700
+++ blead-75154-dirdup2/sv.c 2010-09-21 15:35:57.000000000 -0700
@@ -10730,11 +10730,101 @@ Perl_fp_dup(pTHX_ PerlIO *const fp, cons
DIR *
Perl_dirp_dup(pTHX_ DIR *const dp)
{
+#ifdef HAS_FCHDIR
+ DIR *ret;
+ DIR *pwd;
+ register const Direntry_t *dirent;
+ char smallbuf[256];
+ char *name = NULL;
+ STRLEN len = -1;
+ long pos;
+#endif
+
PERL_UNUSED_CONTEXT;
+
+#ifdef HAS_FCHDIR
if (!dp)
return (DIR*)NULL;
- /* XXX TODO */
- return dp;
+ /* look for it in the table first */
+ ret = (DIR*)ptr_table_fetch(PL_ptr_table, dp);
+ if (ret)
+ return ret;
+
+ /* create anew */
+
+ /* open the current directory (so we can switch back) */
+ if (!(pwd = PerlDir_open("."))) return (DIR *)NULL;
+
+ /* chdir to our dir handle and open the present working directory */
+ if (fchdir(my_dirfd(dp)) < 0 || !(ret = PerlDir_open("."))) {
+ PerlDir_close(pwd);
+ return (DIR *)NULL;
+ }
+ /* Now we should have two dir handles pointing to the same dir. */
+
+ /* Be nice to the calling code and chdir back to where we were. */
+ fchdir(my_dirfd(pwd)); /* If this fails, then what? */
+
+ /* We have no need of the pwd handle any more. */
+ PerlDir_close(pwd);
+
+#ifdef DIRNAMLEN
+# define d_namlen(d) (d)->d_namlen
+#else
+# define d_namlen(d) strlen((d)->d_name)
+#endif
+ /* Iterate once through dp, to get the file name at the current posi-
+ tion. Then step back. */
+ pos = PerlDir_tell(dp);
+ if ((dirent = PerlDir_read(dp))) {
+ len = d_namlen(dirent);
+ if (len <= sizeof smallbuf) name = smallbuf;
+ else Newx(name, len, char);
+ Move(dirent->d_name, name, len, char);
+ }
+ PerlDir_seek(dp, pos);
+
+ /* Iterate through the new dir handle, till we find a file with the
+ right name. */
+ if (!dirent) /* just before the end */
+ for(;;) {
+ pos = PerlDir_tell(ret);
+ if (PerlDir_read(ret)) continue; /* not there yet */
+ PerlDir_seek(ret, pos); /* step back */
+ break;
+ }
+ else {
+ const long pos0 = PerlDir_tell(ret);
+ for(;;) {
+ pos = PerlDir_tell(ret);
+ if ((dirent = PerlDir_read(ret))) {
+ if (len == d_namlen(dirent)
+ && memEQ(name, dirent->d_name, len)) {
+ /* found it */
+ PerlDir_seek(ret, pos); /* step back */
+ break;
+ }
+ /* else we are not there yet; keep iterating */
+ }
+ else { /* This is not meant to happen. The best we can do is
+ reset the iterator to the beginning. */
+ PerlDir_seek(ret, pos0);
+ break;
+ }
+ }
+ }
+#undef d_namlen
+
+ if (name && name != smallbuf)
+ Safefree(name);
+
+ /* pop it in the pointer table */
+ ptr_table_store(PL_ptr_table, dp, ret);
+
+ return ret;
+#else
+ return (DIR*)NULL;
+#endif
}
/* duplicate a typeglob */
diff -Nurp blead/t/op/threads-dirh.t blead-75154-dirdup2/t/op/threads-dirh.t
--- blead/t/op/threads-dirh.t 1969-12-31 16:00:00.000000000 -0800
+++ blead-75154-dirdup2/t/op/threads-dirh.t 2010-09-21 11:16:37.000000000 -0700
@@ -0,0 +1,131 @@
+#!perl
+
+# Test interaction of threads and directory handles.
+
+BEGIN {
+ chdir 't' if -d 't';
+ @INC = '../lib';
+ require './test.pl';
+ $| = 1;
+
+ require Config;
+ if (!$Config::Config{useithreads}) {
+ print "1..0 # Skip: no ithreads\n";
+ exit 0;
+ }
+ if ($ENV{PERL_CORE_MINITEST}) {
+ print "1..0 # Skip: no dynamic loading on miniperl, no threads\n";
+ exit 0;
+ }
+
+ plan(6);
+}
+
+use strict;
+use warnings;
+use threads;
+use threads::shared;
+use File::Path;
+use File::Spec::Functions qw 'updir catdir';
+use Cwd 'getcwd';
+
+# Basic sanity check: make sure this does not crash
+fresh_perl_is <<'# this is no comment', 'ok', {}, 'crash when duping dirh';
+ use threads;
+ opendir dir, 'op';
+ async{}->join for 1..2;
+ print "ok";
+# this is no comment
+
+my $dir;
+SKIP: {
+ my $skip = sub {
+ chdir($dir);
+ chdir updir;
+ skip $_[0], 5
+ };
+
+ if(!$Config::Config{d_fchdir}) {
+ $::TODO = 'dir handle cloning currently requires fchdir';
+ }
+
+ my @w :shared; # warnings accumulator
+ local $SIG{__WARN__} = sub { push @w, $_[0] };
+
+ $dir = catdir getcwd(), "thrext$$" . int rand() * 100000;
+
+ rmtree($dir);
+ mkdir($dir);
+
+ # Create a dir structure like this:
+ # $dir
+ # |
+ # `- toberead
+ # |
+ # +---- thrit
+ # |
+ # +---- rile
+ # |
+ # `---- zor
+
+ chdir($dir);
+ mkdir 'toberead';
+ chdir 'toberead';
+ {open my $fh, ">thrit" or &$skip("Cannot create file thrit")}
+ {open my $fh, ">rile" or &$skip("Cannot create file rile")}
+ {open my $fh, ">zor" or &$skip("Cannot create file zor")}
+ chdir updir;
+
+ # Then test that dir iterators are cloned correctly.
+
+ opendir my $toberead, 'toberead';
+ my $start_pos = telldir $toberead;
+ my @first_2 = (scalar readdir $toberead, scalar readdir $toberead);
+ my @from_thread = @{; async { [readdir $toberead ] } ->join };
+ my @from_main = readdir $toberead;
+ is join('-', sort @from_thread), join('-', sort @from_main),
+ 'dir iterator is copied from one thread to another';
+ like
+ join('-', "", sort(@first_2, @from_thread), ""),
+ qr/(?<!-rile)-rile-thrit-zor-(?!zor-)/i,
+ 'cloned iterator iterates exactly once over everything not already seen';
+
+ seekdir $toberead, $start_pos;
+ readdir $toberead for 1 .. @first_2+@from_thread;
+ is
+ async { readdir $toberead // 'undef' } ->join, 'undef',
+ 'cloned dir iterator that points to the end of the directory'
+ ;
+
+ # Make sure the cloning code can handle file names longer than 255 chars
+ SKIP: {
+ chdir 'toberead';
+ open my $fh,
+ ">floccipaucinihilopilification-"
+ . "pneumonoultramicroscopicsilicovolcanoconiosis-"
+ . "lopadotemachoselachogaleokranioleipsanodrimypotrimmatosilphiokarabo"
+ . "melitokatakechymenokichlepikossyphophattoperisteralektryonoptokephal"
+ . "liokinklopeleiolagoiosiraibaphetraganopterygon"
+ or
+ chdir updir,
+ skip("OS does not support long file names (and I mean *long*)", 1);
+ chdir updir;
+ opendir my $dirh, "toberead";
+ my $test_name
+ = "dir iterators can be cloned when the next fn > 255 chars";
+ while() {
+ my $pos = telldir $dirh;
+ my $fn = readdir($dirh);
+ if(!defined $fn) { fail($test_name); last SKIP; }
+ if($fn =~ 'lagoio') {
+ seekdir $dirh, $pos;
+ last;
+ }
+ }
+ is length async { scalar readdir $dirh } ->join, 257, $test_name;
+ }
+
+ is scalar @w, 0, 'no warnings during all that' or diag @w;
+ chdir updir;
+}
+rmtree($dir); |
From @jdheddenOn Sun, Sep 26, 2010 at 19:42, Father Chrysostomos via RT
Yes, your new patch compiles and tests successfully. |
From @cpansproutOn Mon Sep 27 13:01:46 2010, jdhedden wrote:
Applied as 11a11e. |
@cpansprout - Status changed from 'open' to 'resolved' |
Migrated from rt.perl.org#75154 (status was 'resolved')
Searchable as RT75154$
The text was updated successfully, but these errors were encountered: