Fixed deadlocks in background processing

This commit is contained in:
Alessandro Ranellucci 2014-07-12 11:41:18 +02:00
parent 6a91b2fa52
commit f428888dd9
2 changed files with 42 additions and 49 deletions

View file

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

View file

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