diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 04e8ed72ab..7ba801bf45 100644 --- a/lib/Slic3r.pm +++ b/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); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index c6a9f3cd59..a3e2af6c08 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/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(); } }