File Coverage

blib/lib/Thread/Isolate/Thread.pm
Criterion Covered Total %
statement 13 19 68.4
branch 0 2 0.0
condition n/a
subroutine 5 7 71.4
pod n/a
total 18 28 64.2


line stmt bran cond sub pod time code
1             #############################################################################
2             ## Name: Thread.pm
3             ## Purpose: Thread::Isolate::Thread
4             ## Author: Graciliano M. P.
5             ## Modified by:
6             ## Created: 2005-01-29
7             ## RCS-ID:
8             ## Copyright: (c) 2005 Graciliano M. P.
9             ## Licence: This program is free software; you can redistribute it and/or
10             ## modify it under the same terms as Perl itself
11             #############################################################################
12            
13             package Thread::Isolate::Thread::EVAL ;
14            
15             sub job_EVAL {
16             package main ;
17 6     6   34 no warnings ;
  6         14  
  6         2373  
18 0     0     local( $SIG{__WARN__} ) = sub {} ;
  0     0      
19 0 0         local($_) = $#_ >= 2 ? [@_[2..$#_]] : [] ;
20 0           return eval('package main ; @_ = @{$_} ; $_ = "" ; ' . "\n#line 1\n" . $_[1]) ;
21             }
22            
23             ###########################
24             # THREAD::ISOLATE::THREAD #
25             ###########################
26            
27             package Thread::Isolate::Thread ;
28 6     6   184 use 5.008003 ;
  6         22  
  6         261  
29            
30 6     6   41 use strict qw(vars);
  6         12  
  6         242  
31 6     6   83 no warnings ;
  6         18  
  6         225  
32            
33             ###########
34             # REQUIRE #
35             ###########
36            
37 6     6   28935 use threads ;
  0            
  0            
38             use threads::shared ;
39             use Thread::Isolate::Job ;
40            
41             ########
42             # VARS #
43             ########
44            
45             my ( $sub_THREAD_ISOLATE ) ;
46            
47             my $THI_ID : shared ;
48            
49             use vars qw($MOTHER_THREAD %THI_SHARE_TABLE %THI_THREAD_TABLE %GLOBAL_ATTRS) ;
50            
51             share($MOTHER_THREAD) ;
52            
53             share(%THI_SHARE_TABLE) ;
54             $THI_SHARE_TABLE{id} = share_new_ref('%') ;
55             $THI_SHARE_TABLE{tid} = share_new_ref('%') ;
56             $THI_SHARE_TABLE{thread} = share_new_ref('%') ;
57             $THI_SHARE_TABLE{creator} = share_new_ref('%') ;
58            
59             share(%GLOBAL_ATTRS) ;
60            
61             #######################
62             # START_MOTHER_THREAD #
63             #######################
64            
65             sub start_mother_thread {
66             return $MOTHER_THREAD if $MOTHER_THREAD ;
67            
68             my $thim = Thread::Isolate->new() ;
69             $thim->{clone} = 1 ;
70            
71             $MOTHER_THREAD = $thim->id ;
72             }
73            
74             #######
75             # NEW #
76             #######
77            
78             sub new {
79             my $this = shift ;
80             return( $this ) if ref($this) ;
81             my $class = $this || __PACKAGE__ ;
82            
83             if ( $#_ <= 1 && $_[0] =~ /^\d+$/ ) {
84             return $class->new_from_id($_[0]) ;
85             }
86            
87             my ($internal_level , %args) ;
88            
89             if ( caller eq __PACKAGE__ ) {
90             if ( !( $#_ == 1 && !defined $_[0] && $_[1] ) && Thread::Isolate->self ) {
91             die("Can't create an internal Thread::Isolate with an internal call to new()! Please call \$THI->new_internal() and use the returned id.\n") ;
92             return ;
93             }
94            
95             $internal_level = $_[1] * 1 ;
96             }
97             elsif ( @_ ) { %args = @_ ;}
98            
99             my $mother_thread = $args{mother_thread} || $MOTHER_THREAD ;
100            
101             $mother_thread = $mother_thread->{id} if ref($mother_thread) && UNIVERSAL::isa($mother_thread , 'Thread::Isolate') ;
102            
103             if ( $mother_thread && !$args{no_mother_thread} && !defined $internal_level ) {
104             my $thim = Thread::Isolate->new_from_id($mother_thread) ;
105             my $thi = $thim->new_internal ;
106             $thi->{clone} = undef if $mother_thread == $MOTHER_THREAD ;
107             return $thi ;
108             }
109            
110             $this = bless(share_new_ref('%') , $class) ;
111            
112             $this->{jobs} = share_new_ref('@') ;
113             $this->{jobs_sz} = share_new_ref('$') ;
114             $this->{jobs_standby} = share_new_ref('@') ;
115             $this->{job_id} = share_new_ref('$') ;
116             $this->{job_now} = share_new_ref('$') ;
117             $this->{status} = share_new_ref('$') ;
118             $this->{err} = share_new_ref('$') ;
119            
120             $this->{attrs} = share_new_ref('%') ;
121            
122             $this->{id} = ++$THI_ID ;
123            
124             my $shares = share_new_ref('@') ;
125            
126             $THI_SHARE_TABLE{ $this->{id} } = $shares ;
127            
128             $this->{internal_level} = $internal_level + 1 ;
129            
130             my $sub_thread = 'THREAD_ISOLATE' . $this->{internal_level} ;
131            
132             if ( !defined &$sub_thread ) {
133             *{$sub_thread} = eval($sub_THREAD_ISOLATE) ;
134             }
135            
136             ##print "START THREAD> $this->{id}\n" ;
137            
138             @$shares = %$this ;
139            
140             my $thread = threads->new( \&{$sub_thread} , $this ) ;
141            
142             $this->{tid} = $thread->tid ;
143            
144             $THI_SHARE_TABLE{id}{$this->{tid}} = $this->{id} ;
145             $THI_SHARE_TABLE{tid}{$this->{id}} = $this->{tid} ;
146             $THI_SHARE_TABLE{creator}{$this->{id}} = threads->self->tid ;
147            
148             $THI_THREAD_TABLE{ $this->{tid} } = $thread ;
149            
150             ## hold tid:
151             @$shares = %$this ;
152            
153             threads->yield while !defined ${ $this->{status} } ;
154            
155             return $this ;
156             }
157            
158             #################
159             # SHARE_NEW_REF #
160             #################
161            
162             sub share_new_ref {
163             if ( $_[0] eq '$' ) {
164             my $tmp_ref = eval('my $tmp ; \$tmp') ;
165             return share($$tmp_ref) ;
166             }
167             elsif ( $_[0] eq '@' ) {
168             my $tmp_ref = [] ;
169             return share(@$tmp_ref) ;
170             }
171             elsif ( $_[0] eq '%' ) {
172             my $tmp_ref = {} ;
173             return share(%$tmp_ref) ;
174             }
175             }
176            
177             #########
178             # CLONE #
179             #########
180            
181             sub clone {
182             my $this = shift ;
183             return $this->new_from_id( $this->{id} ) ;
184             }
185            
186             ###############
187             # NEW_FROM_ID #
188             ###############
189            
190             sub new_from_id {
191             my $this = UNIVERSAL::isa($_[0] , 'Thread::Isolate::Thread') ? shift(@_) : undef ;
192             my $id = shift ;
193             my $no_clone = shift ;
194            
195             return if !$THI_SHARE_TABLE{$id} || ref $THI_SHARE_TABLE{$id} ne 'ARRAY' ;
196            
197             my $shares = share_new_ref('%') ;
198            
199             %$shares = @{ $THI_SHARE_TABLE{$id} } ;
200             $$shares{clone} = 1 if !$no_clone ;
201            
202             my $class = ref($this) || $this || __PACKAGE__ ;
203            
204             my $new = bless($shares , $class) ;
205            
206             return $new ;
207             }
208            
209             ################
210             # NEW_INTERNAL #
211             ################
212            
213             sub new_internal {
214             my $this = shift ;
215             my $thi_id = $this->run_job('NEW_INTERNAL' , $this->{internal_level}) ;
216            
217             my $new_int = ref($this)->new_from_id($thi_id) ;
218            
219             $new_int->{internal_level} = $this->{internal_level} + 1 ;
220            
221             return $new_int ;
222             }
223            
224             sub copy { &new_internal ;}
225            
226             ###############
227             # MAP_PACKAGE #
228             ###############
229            
230             sub map_package {
231             my $this = shift ;
232            
233             my $target_thi = pop(@_) ;
234            
235             $this->eval( q`
236             use Thread::Isolate::Map ;
237             my $target_thi = Thread::Isolate->new( shift(@_) ) ;
238             Thread::Isolate::Map->new(@_ , $target_thi) ;
239             return 1 ;
240             `
241             , $target_thi->id , @_ ) ;
242            
243             warn( $this->err ) if $this->err ;
244            
245             return 1 if !$this->err ;
246             return ;
247             }
248            
249             ########
250             # SELF #
251             ########
252            
253             sub self {
254             my $id = $THI_SHARE_TABLE{id}{ threads->self->tid } ;
255             return if !$id ;
256             return new_from_id($_[0],$id) ;
257             }
258            
259             ##########
260             # EXISTS #
261             ##########
262            
263             sub exists {
264             my $this = shift ;
265             return if !threads->object( $this->tid ) ;
266            
267             my ($status) = @$this{qw(status)} ;
268            
269             my $exists ;
270            
271             { lock( $$status ) ;
272             $exists = 1 if $$status ;
273             }
274            
275             $status = undef ;
276            
277             return $exists ;
278             }
279            
280             #######
281             # ERR #
282             #######
283            
284             sub err {
285             my $this = shift ;
286            
287             my $err ;
288            
289             { lock( ${$this->{err}} ) ;
290             $err = ${$this->{err}} ;
291             }
292            
293             return $err ;
294             }
295            
296             #######
297             # TID #
298             #######
299            
300             sub tid {
301             my $this = shift ;
302             return $this->{tid} ;
303             }
304            
305             ######
306             # ID #
307             ######
308            
309             sub id {
310             my $this = shift ;
311             return $this->{id} ;
312             }
313            
314             ############
315             # SET_ATTR #
316             ############
317            
318             sub set_attr {
319             my $this = shift ;
320             my $key = shift ;
321             my $val = shift ;
322            
323             share_ref_tree($val) if ref $val ;
324            
325             lock( %{ $this->{attrs} } ) ;
326            
327             return $this->{attrs}{$key} = $val ;
328             }
329            
330             ############
331             # GET_ATTR #
332             ############
333            
334             sub get_attr {
335             my $this = shift ;
336             my $key = shift ;
337            
338             lock( %{ $this->{attrs} } ) ;
339            
340             return $this->{attrs}{$key} ;
341             }
342            
343             ##############
344             # SET_GLOBAL #
345             ##############
346            
347             sub set_global {
348             my $this = shift ;
349             my $key = shift ;
350             my $val = shift ;
351            
352             share_ref_tree($val) if ref $val ;
353            
354             lock( %GLOBAL_ATTRS ) ;
355             return $GLOBAL_ATTRS{$key} = $val ;
356             }
357            
358             ##############
359             # GET_GLOBAL #
360             ##############
361            
362             sub get_global {
363             my $this = shift ;
364             my $key = shift ;
365            
366             lock( %GLOBAL_ATTRS ) ;
367             return $GLOBAL_ATTRS{$key} ;
368             }
369            
370             ##################
371             # SHARE_REF_TREE #
372             ##################
373            
374             sub share_ref_tree {
375             my $ref = shift ;
376            
377             my $ref_type = ref $ref ;
378            
379             if ( $ref_type !~ /^(?:ARRAY|HASH|SCALAR|)$/) {
380             if ( UNIVERSAL::isa($ref , 'ARRAY') ) { $ref_type = 'ARRAY' ;}
381             elsif ( UNIVERSAL::isa($ref , 'HASH') ) { $ref_type = 'HASH' ;}
382             elsif ( UNIVERSAL::isa($ref , 'SCALAR') ) { $ref_type = 'SCALAR' ;}
383             else { return ;}
384             }
385            
386             if ( $ref_type eq 'ARRAY' ) {
387             {
388             eval { lock( @$ref ) } ;
389             share(@$ref) if $@ ;
390             }
391             foreach my $ref_i ( @$ref ) {
392             share_ref_tree($ref_i) if ref $ref_i ;
393             }
394             }
395             elsif ( $ref_type eq 'HASH' ) {
396             {
397             eval { lock( %$ref ) } ;
398             share(%$ref) if $@ ;
399             }
400             foreach my $Key ( keys %$ref ) {
401             share_ref_tree( $$ref{$Key} ) if ref $$ref{$Key} ;
402             }
403             }
404             elsif ( $ref_type eq 'SCALAR' ) {
405             {
406             eval { lock( $$ref ) } ;
407             share($$ref) if $@ ;
408             }
409             share_ref_tree( $$ref ) if ref $$ref ;
410             }
411            
412             $@ = undef ;
413            
414             return $ref ;
415             }
416            
417             ######################
418             # IS_RUNNING_ANY_JOB #
419             ######################
420            
421             sub is_running_any_job {
422             my $this = shift ;
423            
424             return if !$this->exists ;
425            
426             my ($jobs , $job_now) = @$this{qw(jobs job_now)} ;
427            
428             {
429             lock( $$job_now ) ;
430             return 1 if defined $$job_now ;
431             }
432            
433             ## Creates deadlock!!!
434             # {
435             # lock( @$jobs ) ;
436             # return 1 if join('',@$jobs) ;
437             # }
438            
439             return 1 if ${$this->{jobs_sz}} ;
440            
441             return ;
442             }
443            
444             ###################
445             # HAS_JOB_WAITING #
446             ###################
447            
448             sub has_job_waiting {
449             my $this = shift ;
450            
451             return if !$this->exists ;
452            
453             my ($jobs) = @$this{qw(jobs)} ;
454            
455             {
456             lock( @$jobs ) ;
457             return 1 if ${$this->{jobs_sz}} ;
458             }
459            
460             return ;
461             }
462            
463             ##################
464             # IS_JOB_STARTED #
465             ##################
466            
467             sub is_job_started {
468             my $this = shift ;
469             my ( $the_job ) = @_ ;
470            
471             return if !UNIVERSAL::isa($the_job , 'Thread::Isolate::Job') ;
472             return if ${$this->{status}} <= 0 ;
473            
474             {
475             lock( @$the_job ) if !$the_job->is_no_lock ;
476             return 1 if $$the_job[2] >= 1 ;
477             }
478            
479             return ;
480             }
481            
482             ##################
483             # IS_JOB_RUNNING #
484             ##################
485            
486             sub is_job_running {
487             my $this = shift ;
488             my ( $the_job ) = @_ ;
489            
490             return if !UNIVERSAL::isa($the_job , 'Thread::Isolate::Job') ;
491             return if ${$this->{status}} <= 0 ;
492            
493             {
494             lock( @$the_job ) if !$the_job->is_no_lock ;
495             return 1 if $$the_job[2] == 1 ;
496             }
497            
498             return ;
499             }
500            
501             ###################
502             # IS_JOB_FINISHED #
503             ###################
504            
505             sub is_job_finished {
506             my $this = shift ;
507             my ( $the_job ) = @_ ;
508            
509             return if !UNIVERSAL::isa($the_job , 'Thread::Isolate::Job') ;
510             return if ${$this->{status}} <= 0 ;
511            
512             {
513             lock( @$the_job ) if !$the_job->is_no_lock ;
514             return 1 if $$the_job[2] == 2 ;
515             }
516            
517             return ;
518             }
519            
520             ###########
521             # ADD_JOB #
522             ###########
523            
524             sub add_job {
525             my $this = shift ;
526             my $job_type = shift ;
527            
528             return if !$this->exists ;
529            
530             my ($jobs) = @$this{qw(jobs)} ;
531            
532             my $the_job ;
533            
534             {
535             select(undef , undef , undef , 0.1) while @$jobs >= 200 ;
536            
537             $the_job = Thread::Isolate::Job->new( $this , $job_type , @_ ) ;
538            
539             ##print "ADD>> ". $the_job->dump ."\n" ;
540            
541             lock( @$jobs ) ;
542            
543             ##push(@$jobs , $the_job) ;
544             $this->_jobs_push($the_job) ;
545            
546             cond_signal( @$jobs ) ;
547             }
548            
549             return $the_job ;
550             }
551            
552             ###################
553             # ADD_STANDBY_JOB #
554             ###################
555            
556             sub add_standby_job {
557             my $this = shift ;
558             my $job_type = shift ;
559            
560             return if !$this->exists ;
561            
562             my ($jobs_standby) = @$this{qw(jobs_standby)} ;
563            
564             my $the_job ;
565            
566             {
567             my $wantarray = shift(@_) ;
568             my $delay = $_[0] =~ /^\d+$/s? shift(@_) : '*' ;
569            
570             $the_job = Thread::Isolate::Job->new( $this , $job_type , $wantarray , @_ ) ;
571             $the_job->detach($delay) ;
572            
573             lock( @$jobs_standby ) ;
574             push(@$jobs_standby , $the_job) ;
575             }
576            
577             return $the_job ;
578             }
579            
580             ###################
581             # ADD_JOB_NO_LOCK #
582             ###################
583            
584             sub add_job_no_lock {
585             my $this = shift ;
586             my $job_type = shift ;
587            
588             return if !$this->exists ;
589            
590             my ($jobs) = @$this{qw(jobs)} ;
591            
592             my $the_job = Thread::Isolate::Job->new( $this , $job_type , @_ ) ;
593             $the_job->set_no_lock ;
594            
595             ##push(@$jobs , $the_job) ;
596             $this->_jobs_push($the_job) ;
597            
598             return $the_job ;
599             }
600            
601             ###############
602             # _JOBS_SHIFT #
603             ###############
604            
605             sub _jobs_shift {
606             my $this = shift ;
607             my ($jobs) = @$this{qw(jobs)} ;
608            
609             my $job ;
610             { lock( @$jobs ) ;
611            
612             my $i = -1 ;
613             $job = $$jobs[++$i] while !$job && $i <= $#{$jobs} ;
614             $$jobs[$i] = undef ;
615            
616             if ( $i >= 100 ) {
617             my @jobs = () ;
618             push(@jobs , @$jobs) ;
619            
620             @$jobs = map { ($_ ? $_ : ()) } @jobs if @jobs ;
621             }
622            
623             --${$this->{jobs_sz}} if $job ;
624            
625             ##print "SHIFT>> ${$this->{jobs_sz}} [". join('',@$jobs) ."]\n" ;
626             }
627            
628             return $job ;
629             }
630            
631             ##############
632             # _JOBS_PUSH #
633             ##############
634            
635             sub _jobs_push {
636             my $this = shift ;
637             my ( $the_job ) = @_ ;
638            
639             return if !$the_job ;
640            
641             my ($jobs) = @$this{qw(jobs)} ;
642            
643             { lock( @$jobs ) ;
644             $$jobs[ $#{$jobs}+1 ] = $the_job ;
645            
646             ++${$this->{jobs_sz}} ;
647            
648             #print "PUSH>> ${$this->{jobs_sz}} [". join('',@$jobs) ."]\n" ;
649             }
650            
651             return ;
652             }
653            
654             #####################
655             # WAIT_JOB_TO_START #
656             #####################
657            
658             sub wait_job_to_start {
659             my $this = shift ;
660             my ( $the_job ) = @_ ;
661            
662             return if !UNIVERSAL::isa($the_job , 'Thread::Isolate::Job') ;
663             return if $this->tid == threads->self->tid || !$this->exists ;
664            
665             { lock( @$the_job ) ;
666            
667             return 1 if $$the_job[2] >= 1 ;
668            
669             #cond_wait( @$the_job ) ;
670            
671             #while( $$the_job[2] < 1 && ${$this->{status}} > 0 ) {
672             # select(undef,undef,undef , 0.1);
673             #}
674            
675             while( !cond_timedwait( @$the_job , time+1 ) ) {
676             last if $$the_job[2] >= 1 || ${$this->{status}} <= 0 ;
677             }
678             }
679            
680             threads->yield while $$the_job[2] < 1 && ${$this->{status}} > 0 ;
681             return 1 ;
682             }
683            
684             ############
685             # WAIT_JOB #
686             ############
687            
688             sub wait_job {
689             my $this = shift ;
690             my ( $the_job ) = @_ ;
691            
692             return if !UNIVERSAL::isa($the_job , 'Thread::Isolate::Job') ;
693             return if $this->tid == threads->self->tid || !$this->exists ;
694            
695             { lock( @$the_job ) ;
696            
697             return Thread::Isolate::thaw( $$the_job[4] ) if $$the_job[2] == 2 ;
698             cond_wait( @$the_job ) ;
699             #while( !cond_timedwait( @$the_job , time+2 ) ) {
700             # last if $$the_job[2] == 2 || ${$this->{status}} <= 0 ;
701             #}
702             }
703            
704             threads->yield while $$the_job[2] != 2 && ${$this->{status}} > 0 ;
705             return Thread::Isolate::thaw( $$the_job[4] ) ;
706             }
707            
708             sub wait_job_to_finish { &wait_job ;}
709             sub job_returned { &wait_job ;}
710            
711             ###########
712             # RUN_JOB #
713             ###########
714            
715             sub run_job {
716             my $this = shift ;
717             my $job = $this->add_job(@_) ;
718             $this->wait_job($job) ;
719             }
720            
721             #######
722             # USE #
723             #######
724            
725             sub use {
726             my $this = shift ;
727             my $module = shift ;
728            
729             if ( @_ ) {
730             $this->run_job('EVAL', (wantarray? 1 : 0) , "use $module qw\0". join(" ", @_) ."\0 ;") ;
731             }
732             else {
733             $this->run_job('EVAL', (wantarray? 1 : 0) , "use $module ;") ;
734             }
735             }
736            
737             ########
738             # CALL #
739             ########
740            
741             sub call_detached {
742             my $this = shift ;
743             return $this->add_job('CALL', (wantarray? 1 : 0) , @_) ;
744             }
745            
746             sub call {
747             my $this = shift ;
748             $this->wait_job( ( wantarray ? $this->call_detached(@_) : scalar $this->call_detached(@_) ) ) ;
749             }
750            
751             sub call_detached_no_lock {
752             my $this = shift ;
753             return $this->add_job_no_lock('CALL', (wantarray? 1 : 0) , @_) ;
754             }
755            
756             sub call_no_lock {
757             my $this = shift ;
758             $this->wait_job( ( wantarray ? $this->call_detached_no_lock(@_) : scalar $this->call_detached_no_lock(@_) ) ) ;
759             }
760            
761             sub pack_call_detached {
762             my $this = shift ;
763             @_ = ( _caller_pack() . "::$_[0]" , @_[1..$#_] ) if $_[0] !~ /::/ ;
764             return $this->call_detached(@_) ;
765             }
766            
767             sub pack_call {
768             my $this = shift ;
769             @_ = ( _caller_pack() . "::$_[0]" , @_[1..$#_] ) if $_[0] !~ /::/ ;
770             return $this->call(@_) ;
771             }
772            
773             ########
774             # EVAL #
775             ########
776            
777             sub eval_detached {
778             my $this = shift ;
779             return $this->add_job('EVAL', (wantarray? 1 : 0) , @_) ;
780             }
781            
782             sub eval {
783             my $this = shift ;
784             $this->wait_job( ( wantarray ? $this->eval_detached(@_) : scalar $this->eval_detached(@_) ) ) ;
785             }
786            
787             sub eval_detached_no_lock {
788             my $this = shift ;
789             return $this->add_job_no_lock('EVAL', (wantarray? 1 : 0) , @_) ;
790             }
791            
792             sub eval_no_lock {
793             my $this = shift ;
794             $this->wait_job( ( wantarray ? $this->eval_detached_no_lock(@_) : scalar $this->eval_detached(@_) ) ) ;
795             }
796            
797             sub pack_eval_detached {
798             my $this = shift ;
799             @_ = ( "package " . _caller_pack() . " ;\n#line1\n$_[0]" , @_[1..$#_] ) if $_[0] !~ /::/ ;
800             return $this->eval_detached(@_) ;
801             }
802            
803             sub pack_eval {
804             my $this = shift ;
805             @_ = ( "package " . _caller_pack() . " ;\n#line1\n$_[0]" , @_[1..$#_] ) if $_[0] !~ /::/ ;
806             return $this->eval(@_) ;
807             }
808            
809             ####################
810             # ADD_STANDBY_EVAL #
811             ####################
812            
813             sub add_standby_eval {
814             my $this = shift ;
815             return $this->add_standby_job('EVAL', (wantarray? 1 : 0) , @_) ;
816             }
817            
818             sub pack_add_standby_eval {
819             my $this = shift ;
820             @_ = ( "package " . _caller_pack() . " ;\n#line1\n$_[0]" , @_[1..$#_] ) if $_[0] !~ /::/ ;
821             return $this->add_standby_eval(@_) ;
822             }
823            
824             ####################
825             # ADD_STANDBY_CALL #
826             ####################
827            
828             sub add_standby_call {
829             my $this = shift ;
830             return $this->add_standby_job('CALL', (wantarray? 1 : 0) , @_) ;
831             }
832            
833             sub pack_add_standby_call {
834             my $this = shift ;
835             @_ = ( _caller_pack() . "::$_[0]" , @_[1..$#_] ) if $_[0] !~ /::/ ;
836             return $this->add_standby_call(@_) ;
837             }
838            
839             ################
840             # _CALLER_PACK #
841             ################
842            
843             sub _caller_pack {
844             my ($i , $pack) = -1 ;
845             $pack = caller(++$i) while $i == -1 || ($pack =~ /^Thread::Isolate(?:::|$)/ && $pack) ;
846             $pack ||= caller || 'main' ;
847             return $pack ;
848             }
849            
850             ############
851             # SHUTDOWN #
852             ############
853            
854             sub shutdown {
855             my $this = shift ;
856             my $tid = $this->tid ;
857            
858             my $thread ;
859             if ( $tid ) {
860             $thread = $THI_THREAD_TABLE{$tid} || threads->object( $tid ) ;
861             }
862            
863             $this->add_job('SHUTDOWN') ;
864            
865             $thread->join if UNIVERSAL::isa($thread , 'threads') ;
866            
867             $thread = undef ;
868            
869             return 1 ;
870             }
871            
872             ########
873             # KILL #
874             ########
875            
876             sub kill {
877             my $this = shift ;
878            
879             my ($status) = @$this{qw(status)} ;
880            
881             { lock( $$status ) ;
882             $$status = -1 ;
883             }
884            
885             $this->shutdown if !$this->{clone} ;
886             }
887            
888             ############
889             # JOB_LIST #
890             ############
891            
892             sub job_list {
893             my $this = shift ;
894            
895             my ($jobs) = @$this{qw(jobs)} ;
896            
897             my @list ;
898            
899             { lock( @$jobs ) ;
900             push(@list , @$jobs) ;
901             }
902            
903             return @list ;
904             }
905            
906             ###########
907             # DESTROY #
908             ###########
909            
910             sub DESTROY {
911             my $this = shift ;
912             return if $this->{clone} ;
913            
914             $this->shutdown if $this->tid != threads->self->tid && $this->exists ;
915            
916             delete $THI_SHARE_TABLE{ $this->{id} } ;
917             delete $THI_THREAD_TABLE{ $this->{tid} } ;
918            
919             %$this = () ;
920            
921             return 1 ;
922             }
923            
924             ##################
925             # THREAD_ISOLATE #
926             ##################
927            
928             $sub_THREAD_ISOLATE = q`
929             #line 883 Thread/Isolate/Thread.pm
930            
931             sub {
932             my $this = shift ;
933            
934             $this->{tid} = threads->self->tid ;
935             $THI_SHARE_TABLE{id}{$this->{tid}} = $this->{id} ;
936             $THI_SHARE_TABLE{tid}{$this->{id}} = $this->{tid} ;
937            
938             ##warn "NEW THR>> $this->{id}\n" ;
939            
940             my $is_mother_thread = $this->{id} == $MOTHER_THREAD ? 1 : undef ;
941            
942             my ($jobs , $jobs_standby , $status) = @$this{qw(jobs jobs_standby status)} ;
943            
944             my $jobs_standby_i = -1 ;
945            
946             $$status = 1 ;
947            
948             my $running = 1 ;
949             my $last_job_is_standby ;
950            
951             while( $running && $$status > 0 ) {
952             my ( $the_job , $standby_job , $standby_job_delay ) ;
953             #print "RUN...>>> $this->{id}\n" if $this->{id} == 2 ;
954            
955             { lock( @$jobs ) ;
956            
957             #print "WAIT...<<< $this->{id} [$last_job_is_standby] [". join('',@$jobs) ."] [${$this->{jobs_sz}}]\n" if $this->{id} == 2 ;
958             (!@$jobs_standby || $last_job_is_standby) && ${$this->{jobs_sz}} < 1 ? ($is_mother_thread ? cond_wait( @$jobs ) : cond_timedwait( @$jobs , time + 1 )) : undef ;
959             #print "WAIT...>>> $this->{id}\n" if $this->{id} == 2 ;
960            
961             #print "RUN...<<< $this->{id}\n" if $this->{id} == 2 ;
962            
963             last if $$status <= 0 ;
964            
965             ##print threads->self->tid . "> RUN> $this->{id} [$MOTHER_THREAD]\n" ;
966            
967             ## !join('',@$jobs)
968            
969             if ( !join('',@$jobs) && @$jobs_standby ) {
970             ++$jobs_standby_i ;
971             $jobs_standby_i = $#{$jobs_standby} if $jobs_standby_i > $#{$jobs_standby} ;
972             $standby_job = $$jobs_standby[$jobs_standby_i] ;
973            
974             if ( $standby_job ) {
975             my $last_time ;
976             if ( $$standby_job[7] =~ /^(\d+)(?::(\d+))?$/s ) {
977             ( $standby_job_delay , $last_time ) = ( $1 , $2 ) ;
978             $standby_job = undef if (time - $last_time) < $standby_job_delay ;
979             }
980             }
981             }
982            
983             if ( !$standby_job ) {
984             next if ${$this->{jobs_sz}} < 1 ;
985            
986             ## Fix memory leak on Perl-5.8.4 Win32. (shift on shared array only OK for 5.8.6)
987             ##my $the_job = shift(@$jobs) ;
988             $the_job = $this->_jobs_shift ;
989            
990             next if !defined $the_job ;
991             }
992            
993             }
994            
995             if ( $standby_job ) {
996             $this->process_job($standby_job , \$running , 1) ;
997             $$standby_job[7] = "$standby_job_delay:" . time() if $standby_job_delay ;
998             $last_job_is_standby = 1 ;
999             }
1000             elsif ( $the_job ) {
1001             ##print $the_job->dump ;
1002             $this->process_job($the_job , \$running) ;
1003             $last_job_is_standby = undef ;
1004             }
1005            
1006             }
1007            
1008             $$status = 0 ;
1009            
1010             @{$THI_SHARE_TABLE{ $this->{id} }} = () ;
1011             delete $THI_SHARE_TABLE{ $this->{id} } ;
1012            
1013             ##print "END> $this->{id} [$running , $$status]\n" ;
1014            
1015             return ;
1016             }
1017            
1018             `;
1019            
1020             ###############
1021             # PROCESS_JOB #
1022             ###############
1023            
1024             sub process_job {
1025             my $this = shift ;
1026             my $the_job = shift ;
1027             my $running = shift ;
1028             my $is_standby = shift ;
1029            
1030             return if !defined $the_job ;
1031            
1032             my ($jobs , $job_now , $err) = @$this{qw(jobs job_now err)} ;
1033            
1034             ##print $the_job->dump ;
1035            
1036             $$the_job[2] = 1 ;
1037            
1038             ## Hold only id since hold the object creates a strange memory leak!
1039             $$job_now = $$the_job[1] ;
1040            
1041             my $job_type = $$the_job[3] ;
1042             my @args = Thread::Isolate::thaw( $$the_job[4] ) ;
1043            
1044             my $hold_args = $is_standby ? $$the_job[4] : undef ;
1045            
1046             { lock( $$err ) ;
1047            
1048             if ($job_type eq 'SHUTDOWN') {
1049             @$jobs = () ;
1050             $$running = 0 if $running ;
1051             }
1052             elsif ($job_type eq 'EVAL') {
1053             my @ret ;
1054             if ( $args[0] ) { @ret = Thread::Isolate::Thread::EVAL::job_EVAL(@args) ;}
1055             else { $ret[0] = Thread::Isolate::Thread::EVAL::job_EVAL(@args) ;}
1056             $$err = $@ ;
1057             $$the_job[4] = Thread::Isolate::freeze(@ret) ;
1058             }
1059             elsif ($job_type eq 'CALL') {
1060             my @ret ;
1061             eval {
1062             if ( $args[0] ) { @ret = job_CALL(@args) ;}
1063             else { $ret[0] = job_CALL(@args) ;}
1064             };
1065             $$err = $@ ;
1066             $$the_job[4] = Thread::Isolate::freeze(@ret) ;
1067             }
1068             elsif ($job_type eq 'NEW_INTERNAL') {
1069             my $thi ;
1070             eval {
1071             $thi = Thread::Isolate->new( undef , $args[0] ) ;
1072             };
1073             $$err = $@ ;
1074             $$the_job[4] = Thread::Isolate::freeze( $thi->{id} ) ;
1075             $thi->{clone} = 1 ;
1076             }
1077             elsif ($job_type eq 'END') {
1078             $$running = 0 if $running ;
1079             }
1080             }
1081            
1082             {
1083             lock( @$the_job ) ;
1084             $$the_job[2] = 2 ;
1085             $$job_now = undef ;
1086            
1087             $$the_job[4] = $hold_args if $hold_args ;
1088            
1089             cond_signal( @$the_job ) ;
1090             }
1091            
1092             $the_job = undef ;
1093            
1094             return 1 ;
1095             }
1096            
1097             ############
1098             # JOB_CALL #
1099             ############
1100            
1101             sub job_CALL {
1102             package main ;
1103             return &{$_[1]}(@_[2..$#_]) ;
1104             }
1105            
1106             #######
1107             # END #
1108             #######
1109            
1110             sub END {
1111            
1112             my $tid = threads->self->tid ;
1113            
1114             foreach my $Key (sort keys %THI_SHARE_TABLE ) {
1115             next if $Key == $MOTHER_THREAD ;
1116             my $thi = new_from_id($Key) ;
1117             $thi->shutdown if $thi && $thi->exists ;
1118             }
1119            
1120             if ( $MOTHER_THREAD ) {
1121             my $thim = Thread::Isolate->new_from_id($MOTHER_THREAD) ;
1122            
1123             ## exit() from Mother Thread to avoid alerts on Win32:
1124             if ( $thim && $thim->exists ) {
1125             $thim->eval(' CORE::exit() ;') ;
1126             $thim->shutdown ;
1127             }
1128            
1129             $MOTHER_THREAD = undef ;
1130             }
1131            
1132             %THI_SHARE_TABLE = () ;
1133             %THI_THREAD_TABLE = () ;
1134            
1135             }
1136            
1137             #######
1138             # END #
1139             #######
1140            
1141             1;
1142            
1143            
1144