Browse Source

Fixed deadlocks in background processing

Alessandro Ranellucci 10 years ago
parent
commit
f428888dd9
2 changed files with 42 additions and 49 deletions
  1. 38 8
      lib/Slic3r.pm
  2. 4 41
      lib/Slic3r/GUI/Plater.pm

+ 38 - 8
lib/Slic3r.pm

@@ -74,6 +74,7 @@ use Slic3r::Print::SupportMaterial;
 use Slic3r::Surface;
 use Slic3r::TriangleMesh;
 our $build = eval "use Slic3r::Build; 1";
+use Thread::Semaphore;
 
 use constant SCALING_FACTOR         => 0.000001;
 use constant RESOLUTION             => 0.0125;
@@ -86,11 +87,25 @@ use constant INSET_OVERLAP_TOLERANCE => 0.2;
 
 # keep track of threads we created
 my @threads : shared = ();
+my $sema = Thread::Semaphore->new;
+my $paused = 0;
 
 sub spawn_thread {
     my ($cb) = @_;
     
-    my $thread = threads->create($cb);
+    @_ = ();
+    my $thread = threads->create(sub {
+        local $SIG{'KILL'} = sub {
+            Slic3r::debugf "Exiting thread...\n";
+            Slic3r::thread_cleanup();
+            threads->exit();
+        };
+        local $SIG{'STOP'} = sub {
+            $sema->down;
+            $sema->up;
+        };
+        $cb->();
+    });
     push @threads, $thread->tid;
     return $thread;
 }
@@ -104,12 +119,6 @@ sub parallelize {
         $q->enqueue(@items, (map undef, 1..$params{threads}));
         
         my $thread_cb = sub {
-            local $SIG{'KILL'} = sub {
-                Slic3r::debugf "Exiting child thread...\n";
-                Slic3r::thread_cleanup();
-                threads->exit;
-            };
-            
             # execute thread callback
             $params{thread_cb}->($q);
             
@@ -188,17 +197,38 @@ sub thread_cleanup {
     return undef;  # this prevents a "Scalars leaked" warning
 }
 
+sub get_running_threads {
+    return grep defined($_), map threads->object($_), @threads;
+}
+
 sub kill_all_threads {
     # detach any running thread created in the current one
     my @killed = ();
-    foreach my $thread (grep defined($_), map threads->object($_), @threads) {
+    foreach my $thread (get_running_threads()) {
         $thread->kill('KILL');
         push @killed, $thread;
     }
+    
+    # unlock semaphore before we block on wait
+    # otherwise we'd get a deadlock if threads were paused
+    resume_threads();
     $_->join for @killed;  # block until threads are killed
     @threads = ();
 }
 
+sub pause_threads {
+    return if $paused;
+    $paused = 1;
+    $sema->down;
+    $_->kill('STOP') for get_running_threads();
+}
+
+sub resume_threads {
+    return unless $paused;
+    $paused = 0;
+    $sema->up;
+}
+
 sub encode_path {
     my ($filename) = @_;
     return encode('locale_fs', $filename);

+ 4 - 41
lib/Slic3r/GUI/Plater.pm

@@ -7,7 +7,6 @@ use File::Basename qw(basename dirname);
 use List::Util qw(sum first);
 use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad);
 use threads::shared qw(shared_clone);
-use Thread::Semaphore;
 use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc 
     :panel :sizer :toolbar :window wxTheApp);
 use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED 
@@ -43,8 +42,6 @@ use constant PROCESS_DELAY => 0.5 * 1000; # milliseconds
 
 my $PreventListEvents = 0;
 
-my $sema = Thread::Semaphore->new;
-
 sub new {
     my $class = shift;
     my ($parent) = @_;
@@ -810,7 +807,7 @@ sub async_apply_config {
     
     # pause process thread before applying new config
     # since we don't want to touch data that is being used by the threads
-    $self->suspend_background_process;
+    Slic3r::pause_threads();
     
     # apply new config
     my $invalidated = $self->{print}->apply_config($self->GetFrame->config);
@@ -820,9 +817,8 @@ sub async_apply_config {
     if ($invalidated) {
         # kill current thread if any
         $self->stop_background_process;
-        $self->resume_background_process;
     } else {
-        $self->resume_background_process;
+        Slic3r::resume_threads();
     }
     
     # schedule a new process thread in case it wasn't running
@@ -857,16 +853,6 @@ sub start_background_process {
     # start thread
     @_ = ();
     $self->{process_thread} = Slic3r::spawn_thread(sub {
-        local $SIG{'KILL'} = sub {
-            Slic3r::debugf "Background process cancelled; exiting thread...\n";
-            Slic3r::thread_cleanup();
-            threads->exit();
-        };
-        local $SIG{'STOP'} = sub {
-            $sema->down;
-            $sema->up;
-        };
-        
         eval {
             $self->{print}->process;
         };
@@ -906,18 +892,6 @@ sub stop_background_process {
     }
 }
 
-sub suspend_background_process {
-    my ($self) = @_;
-    
-    $sema->down;
-    $_->kill('STOP') for grep $_, $self->{process_thread}, $self->{export_thread};
-}
-
-sub resume_background_process {
-    my ($self) = @_;
-    $sema->up;
-}
-
 sub export_gcode {
     my $self = shift;
     
@@ -1011,16 +985,6 @@ sub on_process_completed {
         our $_thread_self = $self;
         
         $self->{export_thread} = Slic3r::spawn_thread(sub {
-            local $SIG{'KILL'} = sub {
-                Slic3r::debugf "Export process cancelled; exiting thread...\n";
-                Slic3r::thread_cleanup();
-                threads->exit();
-            };
-            local $SIG{'STOP'} = sub {
-                $sema->down;
-                $sema->up;
-            };
-        
             eval {
                 $_thread_self->{print}->export_gcode(output_file => $_thread_self->{export_gcode_output_file});
             };
@@ -1278,7 +1242,7 @@ sub object_settings_dialog {
 		object          => $self->{objects}[$obj_idx],
 		model_object    => $model_object,
 	);
-	$self->suspend_background_process;
+	Slic3r::pause_threads();
 	$dlg->ShowModal;
 	
 	# update thumbnail since parts may have changed
@@ -1289,11 +1253,10 @@ sub object_settings_dialog {
 	# update print
 	if ($dlg->PartsChanged || $dlg->PartSettingsChanged) {
 	    $self->stop_background_process;
-        $self->resume_background_process;
         $self->{print}->reload_object($obj_idx);
         $self->schedule_background_process;
     } else {
-        $self->resume_background_process;
+        Slic3r::resume_threads();
     }
 }