Skip to content

Commit

Permalink
Fix issue #394 - crash when we cancel the file save operation
Browse files Browse the repository at this point in the history
There were problems calling a pure virtual function (IFileOpProgress
implemented by OpenFileJob) when we are already in ~Job() dtor. So we've
to wait the background thread (added Job::waitJob() function) to join
the thread so it can use IFileOpProgress safely.

Also the save process of .ase files now can be cancelled (it wasn't
possible before).
  • Loading branch information
dacap committed May 2, 2014
1 parent 33753cc commit 66564e3
Show file tree
Hide file tree
Showing 13 changed files with 70 additions and 20 deletions.
6 changes: 5 additions & 1 deletion src/app/commands/cmd_open_file.cpp
Expand Up @@ -69,7 +69,11 @@ class OpenFileJob : public Job, public IFileOpProgress

void showProgressWindow() {
startJob();
fop_stop(m_fop);

if (isCanceled())
fop_stop(m_fop);

waitJob();
}

private:
Expand Down
1 change: 1 addition & 0 deletions src/app/commands/cmd_rotate_canvas.cpp
Expand Up @@ -204,6 +204,7 @@ void RotateCanvasCommand::onExecute(Context* context)
{
RotateCanvasJob job(reader, m_angle);
job.startJob();
job.waitJob();
}
reader.document()->generateMaskBoundaries();
update_screen_for_document(reader.document());
Expand Down
11 changes: 10 additions & 1 deletion src/app/commands/cmd_save_file.cpp
Expand Up @@ -53,7 +53,12 @@ class SaveFileJob : public Job, public IFileOpProgress {

void showProgressWindow() {
startJob();
fop_stop(m_fop);

if (isCanceled()) {
fop_stop(m_fop);
}

waitJob();
}

private:
Expand Down Expand Up @@ -89,6 +94,10 @@ static void save_document_in_background(Document* document, bool mark_as_saved)
Console console;
console.printf(fop->error.c_str());
}
// If the job was cancelled, mark the document as modified.
else if (fop_is_stop(fop)) {
document->impossibleToBackToSavedState();
}
else {
App::instance()->getRecentFiles()->addRecentFile(document->getFilename().c_str());
if (mark_as_saved)
Expand Down
1 change: 1 addition & 0 deletions src/app/commands/cmd_sprite_size.cpp
Expand Up @@ -243,6 +243,7 @@ void SpriteSizeCommand::onExecute(Context* context)
{
SpriteSizeJob job(reader, new_width, new_height, resize_method);
job.startJob();
job.waitJob();
}

ContextWriter writer(reader);
Expand Down
5 changes: 5 additions & 0 deletions src/app/document.cpp
Expand Up @@ -210,6 +210,11 @@ void Document::markAsSaved()
m_associated_to_file = true;
}

void Document::impossibleToBackToSavedState()
{
m_undo->impossibleToBackToSavedState();
}

//////////////////////////////////////////////////////////////////////
// Loaded options from file

Expand Down
6 changes: 6 additions & 0 deletions src/app/document.h
Expand Up @@ -130,6 +130,12 @@ namespace app {
bool isAssociatedToFile() const;
void markAsSaved();

// You can use this to indicate that we've destroyed (or we cannot
// trust) the file associated with the document (e.g. when we
// cancel a Save operation in the middle). So it's impossible to
// back to the saved state using the UndoHistory.
void impossibleToBackToSavedState();

//////////////////////////////////////////////////////////////////////
// Loaded options from file

Expand Down
5 changes: 5 additions & 0 deletions src/app/document_undo.cpp
Expand Up @@ -74,6 +74,11 @@ void DocumentUndo::markSavedState()
return m_undoHistory->markSavedState();
}

void DocumentUndo::impossibleToBackToSavedState()
{
m_undoHistory->impossibleToBackToSavedState();
}

void DocumentUndo::pushUndoer(undo::Undoer* undoer)
{
return m_undoHistory->pushUndoer(undoer);
Expand Down
1 change: 1 addition & 0 deletions src/app/document_undo.h
Expand Up @@ -55,6 +55,7 @@ namespace app {

bool isSavedState() const;
void markSavedState();
void impossibleToBackToSavedState();

// UndoHistoryDelegate implementation.
undo::ObjectsContainer* getObjects() const OVERRIDE { return m_objects; }
Expand Down
13 changes: 8 additions & 5 deletions src/app/file/ase_format.cpp
Expand Up @@ -321,25 +321,25 @@ bool AseFormat::onSave(FileOp* fop)
// Frame duration
frame_header.duration = sprite->getFrameDuration(frame);

/* the sprite is indexed and the palette changes? (or is the first frame) */
// The sprite is indexed and the palette changes? (or is the first frame)
if (sprite->getPixelFormat() == IMAGE_INDEXED &&
(frame == 0 ||
sprite->getPalette(frame.previous())->countDiff(sprite->getPalette(frame), NULL, NULL) > 0)) {
/* write the color chunk */
// Write the color chunk
ase_file_write_color2_chunk(f, &frame_header, sprite->getPalette(frame));
}

/* write extra chunks in the first frame */
// Write extra chunks in the first frame
if (frame == 0) {
LayerIterator it = sprite->getFolder()->getLayerBegin();
LayerIterator end = sprite->getFolder()->getLayerEnd();

/* write layer chunks */
// Write layer chunks
for (; it != end; ++it)
ase_file_write_layers(f, &frame_header, *it);
}

/* write cel chunks */
// Write cel chunks
ase_file_write_cels(f, &frame_header, sprite, sprite->getFolder(), frame);

// Write the frame header
Expand All @@ -348,6 +348,9 @@ bool AseFormat::onSave(FileOp* fop)
// Progress
if (sprite->getTotalFrames() > 1)
fop_progress(fop, (float)(frame.next()) / (float)(sprite->getTotalFrames()));

if (fop_is_stop(fop))
break;
}

// Write the missing field (filesize) of the header.
Expand Down
31 changes: 19 additions & 12 deletions src/app/job.cpp
Expand Up @@ -54,35 +54,42 @@ Job::Job(const char* job_name)

Job::~Job()
{
ASSERT(!m_timer->isRunning());
ASSERT(m_thread == NULL);

if (m_alert_window != NULL)
m_alert_window->closeWindow(NULL);

if (m_progress)
delete m_progress;

if (m_mutex)
delete m_mutex;
}

void Job::startJob()
{
m_thread = new base::thread(&Job::thread_proc, this);
m_alert_window->openWindowInForeground();

// The job was canceled by the user?
{
base::scoped_lock hold(*m_mutex);
if (!m_done_flag)
m_canceled_flag = true;
}
}

void Job::waitJob()
{
if (m_timer->isRunning())
m_timer->stop();

if (m_thread) {
m_thread->join();
delete m_thread;
m_thread = NULL;
}

if (m_progress)
delete m_progress;

if (m_mutex)
delete m_mutex;
}

void Job::startJob()
{
m_thread = new base::thread(&Job::thread_proc, this);
m_alert_window->openWindowInForeground();
}

void Job::jobProgress(double f)
Expand Down
2 changes: 2 additions & 0 deletions src/app/job.h
Expand Up @@ -41,6 +41,8 @@ namespace app {
// monitoring the progress with onMonitorTick() event.
void startJob();

void waitJob();

// The onJob() can use this function to report progress of the
// background job being done. 1.0 is completed.
void jobProgress(double f);
Expand Down
7 changes: 6 additions & 1 deletion src/undo/undo_history.cpp
Expand Up @@ -71,7 +71,7 @@ void UndoHistory::clearRedo()
// that state again, so we have to put a value in m_diffSaved
// impossible to be equal to m_diffCount.
if (m_diffCount < m_diffSaved)
m_diffSaved = -1;
impossibleToBackToSavedState();
}

Undoer* UndoHistory::getNextUndoer()
Expand Down Expand Up @@ -100,6 +100,11 @@ void UndoHistory::markSavedState()
m_diffSaved = m_diffCount;
}

void UndoHistory::impossibleToBackToSavedState()
{
m_diffSaved = -1;
}

void UndoHistory::runUndo(Direction direction)
{
UndoersStack* undoers = ((direction == UndoDirection)? m_undoers: m_redoers);
Expand Down
1 change: 1 addition & 0 deletions src/undo/undo_history.h
Expand Up @@ -48,6 +48,7 @@ namespace undo {

bool isSavedState() const;
void markSavedState();
void impossibleToBackToSavedState();

ObjectsContainer* getObjects() const { return m_delegate->getObjects(); }

Expand Down

0 comments on commit 66564e3

Please sign in to comment.