in reply to Re^4: RFC: Using 'threads' common aspects
in thread RFC: Using 'threads' common aspects

So, here is how I would write a program to do what I believe you intend your program to do.

Stylistically, it is written in my (infinitely preferable and superior :) style, but essentially does everything yours attempts to do. But it does correctly and in a simple, clear, easily understood and maintainable way.

#! perl -slw use strict; use threads; use threads::shared; use Thread::Queue; use constant { RANDOM => 4, THREADS => 4, JOBS => 20, }; my $semSTD :shared; sub tprint { my $tid = threads->tid; lock $semSTD; print "[$tid] ", @_; } my $die_early :shared = 0; $SIG{ INT } = sub { tprint "Early termination requested"; $die_early = 1; }; sub worker { tprint "worker started"; my( $Q ) = @_; while( !$die_early and defined( my $job = $Q->dequeue ) ) { tprint "processing job:$job"; my $pid = open my $PIPE, '-|', "sleep " . int( rand RANDOM ) o +r die $!; tprint "waiting for pid: $pid"; waitpid $pid, 0; tprint "pid: $pid done"; } tprint "Worker ending"; return 1; } my $Q = new Thread::Queue; $Q->enqueue( map "JOB-$_", 1 .. JOBS ); $Q->enqueue( (undef) x THREADS ); tprint "Queue populated"; my @threads = map threads->new( \&worker, $Q ), 1 .. THREADS; tprint "Workers started; waiting..."; $_->join for @threads; print "Program complete"; __END__ C:\test>881217 [0] Queue populated [1] worker started [1] processing job:JOB-1 [1] waiting for pid: 1740 [2] worker started [2] processing job:JOB-2 [1] waiting for pid: 1056 [2] pid: 2684 done [2] processing job:JOB-4 [1] waiting for pid: 2116 [3] worker started [3] processing job:JOB-6 [3] waiting for pid: 3244 [0] Workers started; waiting... [4] worker started [4] processing job:JOB-7 ... [3] pid: 1112 done [3] processing job:JOB-20 [3] waiting for pid: 4048 [2] pid: 1728 done [1] pid: 3432 done [2] Worker ending [1] Worker ending [4] pid: 3924 done [4] Worker ending [3] pid: 4048 done [3] Worker ending Program complete

Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re^6: RFC: Using 'threads' common aspects
by afoken (Chancellor) on Jan 10, 2011 at 15:38 UTC
    local $SIG{'KILL'} = sub { threads->exit(); };

    Why? If all you are going to do is terminate, why not just let that happen?

    Well, it will "just happen". SIGKILL can't be caught, blocked or ignored. The process will simply be forced to exit when a SIGKILL is sent, the sub will never be invoked. Unless, of course, you run that code on a very strange non-POSIX system with a different signals implementation.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      SIGKILL can't be caught, blocked or ignored.

      All of which makes you wonder why it is a signal at all.

      Unless, you run that code on a non-POSIX system

      Well that's a possibility. But "non-POSIX" doesn't equate to "very strange".


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        SIGKILL can't be caught, blocked or ignored.

        All of which makes you wonder why it is a signal at all.

        My guess is that it was far easier to add this little special case to a working mechanism than to create a completely different "shoot that process and this time I really mean it" API. Signals already have permission checks, and the signals API also can handle process groups. Using a different API would have meant to re-implement or at least re-use all of this. Ignoring all attempts to catch, block or ignore SIGKILL can be done with a few lines of code, perhaps something simple as if ((signum==SIGKILL) || (signum==SIGSTOP)) { return SIG_ERR } inside signal() (at least in ancient UNIX versions).

        Unless, you run that code on a non-POSIX system

        Well that's a possibility. But "non-POSIX" doesn't equate to "very strange".

        I would call a system that exactly re-implements POSIX signals except for the special case handling of SIGKILL a very strange system - or just broken.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re^6: RFC: Using 'threads' common aspects
by DeadPoet (Scribe) on Jan 09, 2011 at 19:35 UTC
    Wow, a lot to digest....

    Let me try to address a couple of your questions.

    Let's start with "Why open3?"

    Open3 provides me an easily method for splitting IO communication. Most of the stuff I deal with is over SecureShell and frankly I like the way the output is available via the appropriate handles. Moreover, the need to time out a process is highly needed no matter if the command is local or remote. Take the example of the 'df' command and HP-UX, what happens if one run the command and it hits a hung NFS file system? The entire command is now hung thus the thread, and eventually the program. The point is not that one could exclude NFS from the command, but more to the point that the unexpected happens when dealing with 3000 server. Just because one terminates the thread or breaks the pipe does not mean the process will not remain. SecureShell is such a great example of this.

    The DEFAULTS, yes I can flag it as READONLY or CONSTANT, it is just a place holder for program defaults. However, if I expanded such a program to leverage getopts to specify maximum, minimum, etc... values the constant/readonly aspect becomes a moot point.

    Central printing, just an example... Yes, I totally understand that each thread can manage its own printing aspect with a simple lock variable and that is without a doubt the more efficient manner. Thus, should have been my first choice.

    Timestamp(): This is used by the print_manager. In my business, time stamp is everything, but in this example just an indicator of program progress.

    sub print_manager { lock $l_PRINT; while ( ! $f_TERM_PRINT ) { usleep(100_000); print STDOUT '[' . timestamp() . ']: ' . $q_PRINT->dequeue() . "\n"; } threads->exit(); } ## end print_manager.
    For the remaining topics, I need to digest and think about everything. I think I see where you are going, but I also think you may be missing the importance of ensuring that bad process do not remain in the process table.

    Once again, I must give thanks for your time and thoughts.

    --Poet
      Let's start with "Why open3?" Open3 provides me an easily method for splitting IO communication. Most of the stuff I deal with is over SecureShell and frankly I like the way the output is available via the appropriate handles. Moreover, the need to time out a process is highly needed no matter if the command is local or remote. Take the example of the 'df' command and HP-UX, what happens if one run the command and it hits a hung NFS file system?

      None of this was mentioned in either of your two threads. You offered this as an RFC:tutorial, not a chopped down version of a real application with an onerous set of very specific (and till now, unmentioned) technical requirements.

      I hope you realise that I cannot read your mind.

      I also think you may be missing the importance of ensuring that bad process do not remain in the process table.

      In my sample, I only attempted to meet the functionality of your latest code. Your latest code dropped the timeout and kill the process part, so I didn't implement it.

      But adding that functionality back to my sample code would require maybe 5 more lines. (For *nix, maybe 10 for Win32.)


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        BrowserUK I must admit very succinct code and definitely provides me with a different method to view this endeavour.

        In the original post, I did provide a copious amount of code documentation, and was immediately shot down with flames in trail. I understand that that there are those that care little for code comments. Moreover, I have also read your write-up related to documentation--which I mainly disagree. However, that is my perspective, and I do not impose my style on others.

        This is exactly why I removed nearly all comments in secondary postings. But, this did seem to create a breakdown in the conversation, and that is definitely mine to own.

        The topic that I would like to address is not to solve a specific engineering problem, but to aid others that deal with common system administration style tasks. Such tasks normally cover hundreds, if not thousands, of servers of all operating system flavours and states. So, the criteria that I established for myself was:

        * Create a script to leverage Perl 'threads'

        * Display functionality that uses a pool of common threads to perform a series of tasks.

        * Display functionality that runs external commands with timed limitations.

        * Display functionality that will terminate external commands, but does not kill the thread stack.

        * Display functionality that allows for the sharing of data and reporting of collected information.

        Although each of these items were address with the initial functionality of this posting, it was not addresses in the most optimal manner. Evaluating your design, I agree that is highly efficient and effective. Moreover, its memory footprint is about 1/3 the size of mine--very nice indeed.

        Finally, I never intended for you to write for me. However, you did teach to me. I still wish to pursue a composite example for others to leverage as an example of using the common aspect of Perl threads. I honestly believe that such an example should not leverage modules which do-it-for-you because I feel that the learning, if not the understanding, is lost.

        --Poet