in reply to Re: Using forks and threads
in thread Using forks and threads

Many thanks for your reply, BrowserUk.

My project is neither urgent (I've been procrastinating for months now), nor mission critical, so I'm free to experiment and fiddle.

The project (named 'pancakes' due to the number of pancakes I consumed when I first had the idea) is a playlist framework 'thingy'. I'm still not sure exactly how pancakes will work, since I'm only in the early design phase, but I'm assuming that there will be forking/threading going on (typical setup of 1 thread for user interface, and 1 thread for doing the work).

I'm probably going to interact with mplayer (or perhaps XMMS2, if (stable) perl bindings ever get released for it). Not too keen on the alternatives (mpg123, etc), since they seem to only play mp3s/oggs (my playlist has all sorts of file formats in it). Interaction with mplayer will probably be done via system() or similar.

That's all I can really think of pancakes-wise (if anyone is interested and/or wants to help, feel very free to reply :-) ). It'll all be released under GPL, so whatever solution I find will be free for all. I'll probably also blog/wiki it (and if I make a good article, will post at the monastry).

From what has been said so far, it sounds like forking in a thread is unsafe, unportable, and unwise. I think the best thing in the given situation would be:

If I come up with any better ideas, I'll post another message :-)

Thanks to all who replied

Replies are listed 'Best First'.
Re^3: Using forks and threads
by BrowserUk (Patriarch) on Jul 21, 2006 at 14:42 UTC

    You really should read up on the pthreads libraries for your system, or perform a few experiments, to determine what happens when you fork a multi-threaded process. Personally I favour the latter approach as it generally yields quicker results.

    A quick scan around suggests that POSIX/Solaris pthreads libraries map fork(2) to forkl(2) which only clones the calling thread, in which case it *ought* to be safe to use fork and threads in the same process which would greatly simplify what you describe. However, sucking and seeing is the only way to be sure.

    If Perl's fork isn't compatible with threads on your system and you need to resort to forking early and running your system calls in a separate process, then I don't see much value in using threads. Using one process to run the user interface, and another to run the background processing would seem to be sufficient. Adding a second thread in the first process would simply complicate things as far as I can see.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      I'd just finished writing threads::system when I read your reply.

      It's good news that fork seems to only clone the current thread, but, like you say, it sounds very system-dependant, and inconsistent.

      May I ask for criticism on the following module? If enough people think it's worthwhile, I'll tweak it a bit, and then upload to CPAN (although, I may change the name to something less dominant first):

      #!/usr/bin/perl package threads::system; require Exporter; @ISA = qw{Exporter}; @EXPORT_OK = qw{system}; use threads::shared; use IO::Handle; use strict; use warnings; pipe my ($crh, $cwh) or die "Could not pipe: $!"; # Command string re +ad/write handles pipe my ($erh, $ewh) or die "Could not pipe: $!"; # Exit status read/ +write handles $cwh->autoflush(1); $ewh->autoflush(1); my $sync : shared; my $child = fork; die "Could not fork: $!" if $child < 0; unless ($child) { # We are the child (daemon) - we handle system() ca +lls for our parent close $erh; close $cwh; while (<$crh>) { # Read commands to exec my $child = fork; die "Could not fork: $!" if $child < 0; unless ($child) { # We are the child - we exec() for our pare +nt exec $_; exit $!; } else { # Parent - communicate our child's failure with our +own parent while (my $pid = waitpid $child, 0) { # Is all this reall +y needed? if ($pid < 0) { die "Error: waitpid: $!" } elsif ($pid == 0) { next } else { print $ewh $?, "\n"; last } # Tell parent! } } } # Decide: is the following a feature or a bug? exit; # Should never happen, unless client does system(undef), or + something } else { # Parent close $ewh; close $crh; } sub system { lock($sync); my $string = shift; # TODO: Accept a full list print $cwh $string, "\n" or die "Could not write: $!"; return <$erh>; } 1
      Example of usage:
      #!/usr/bin/perl use threads::system qw{system}; use strict; use warnings; my $code = system("ls -al /etc"); print "Exited with code: $code\n";
      Output is as expected. Thanks
        t's good news that fork seems to only clone the current thread, but, like you say, it sounds very system-dependant, and inconsistent.

        Well, your module definitely wouldn't work on Win32, and given the problems some other, non-win32 people have using the Expect module, it seems unlikely that your delightfully simple module will be sufficiently robust for widespread portability.

        Given that

        • (as you said in your OP), system in a threads appears to work on your system;
        • that the docs for POSIX/Solaris indicate that it should work on those systems;
        • and it does work on Win32;
        • Up-to-date builds of Linux (post 2.6 kernel?) offer NTPL (Native POSIX Threads Libraries) with much closer POSIX conformance than the older LinuxThreads;

        it seems to me that rather than writing a potentially complex module (see Expect.pm) for this, the right solution would be to verify that it does work correctly on POSIX/Solaris systems and remove the dire warning you quoted in your OP, from perlthrtut.

        It might need to be replaced with a list of systems on which it definitely doesn't work correctly, or maybe just a few limitations on specific systems.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.