Save a copy of the image
AbandonedPublic

Authored by rempt on Nov 4 2016, 9:46 AM.

Details

Reviewers
dkazakov
Summary

This should fix bug 370566

This gets rid of both the delayed save dialog in KisMainWindow (which offered a cancel button that didn't work to cancel the running strokes), and the locking in KisDocument (which dead-locked Krita and caused dataloss).

Instead, a shallow clone of the image is created that can be safely used to save. The strokes will run on the original image, which means that the result of still-running strokes is not saved. The user is warned about that. (That means a new message has been added.)

Test Plan

Load a kra file and save to all file formats, then create a big gradient and while the gradient is running, save to all file formats. No deadlocks should occur.

Diff Detail

Repository
R37 Krita
Lint
Lint Skipped
Unit
Unit Tests Skipped
rempt updated this revision to Diff 7886.Nov 4 2016, 9:46 AM
rempt retitled this revision from to Save a copy of the image.
rempt updated this object.
rempt edited the test plan for this revision. (Show Details)
rempt added a reviewer: dkazakov.
rempt set the repository for this revision to R37 Krita.
rempt added a subscriber: Krita.
rempt updated this revision to Diff 7888.Nov 4 2016, 9:50 AM
rempt updated this object.

The export filters shouldn't check whether the image is locked.

I don't know the code to say anything qualified, I just added a few random thoughts glancing over the change. I do think it's an improvement over the old state.

libs/image/kis_image.cc
285

Is the thing really called Dumb? I guess it should be Dummy, assuming that it is an undo store that doesn't really allow undoing anything. Unrelated to this patch though, so discard this comment.

libs/image/kis_node_facade.h
140

Nice :) Using scoped pointer makes a lot of sense here. To make review easier, this could be a separate patch since it's not directly related to the rest of the change.

libs/ui/KisDocument.cpp
439

I guess this forward declaration can also be removed?

plugins/impex/brush/kis_brush_export.cpp
87

Is this block disabled intentionally? I don't know what it did before, so I cannot judge it. Maybe it should have a FIXME: reenable? Of if it's dead code it could be removed.

plugins/impex/psd/psd_resource_block.h
78

I'd prefer just c++ 11 override, unless you want to support c++ 98 compilers.

rempt added a comment.Nov 5 2016, 9:35 AM

Note that there is different, but similar commit ready for the 3.1 branch.

INLINE COMMENTS

> kis_image.cc:285
> + rhs.colorSpace(),
> + undoStore ? undoStore : new KisDumbUndoStore()))
> +{

Is the thing really called Dumb? I guess it should be Dummy, assuming that it is an undo store that doesn't really allow undoing anything. Unrelated to this patch though, so discard this comment.

Yes -- it's really called that. Dummy probably would be a better name, since it's indeed an undo store that doesn't save commands.

> kis_node_facade.h:140
> struct Private;
> - Private * const m_d;
> + QScopedPointer<Private> m_d;
> };

Nice :) Using scoped pointer makes a lot of sense here. To make review easier, this could be a separate patch since it's not directly related to the rest of the change.

We probably should review all our d-pointers at one point and make them into QScopedPointers. We don't _need_ d-pointers -- we're not providing a stable abi, but it saves some rebuild time when adding member variables. That's why it was initially introduced in 2014 or so...

> KisDocument.cpp:439
> class SafeSavingLocker;
> };

I guess this forward declaration can also be removed?

Yes.

> kis_brush_export.cpp:87
> + buf.close();
> +
}
>

Is this block disabled intentionally? I don't know what it did before, so I cannot judge it. Maybe it should have a FIXME: reenable? Of if it's dead code it could be removed.

There is a comment in the KisBrushExport::convert class about this block of code.

> psd_resource_block.h:78
>
> + KisAnnotation* clone() const Q_DECL_OVERRIDE {
> + // HACK ALERT: we are evil! use notmal copying instead!

I'd prefer just c++ 11 override, unless you want to support c++ 98 compilers.o

Okay.

dkazakov edited edge metadata.EditedNov 7 2016, 6:55 AM
In D3258#60774, @rempt wrote:

Note that there is different, but similar commit ready for the 3.1 branch.

INLINE COMMENTS

> kis_image.cc:285
> + rhs.colorSpace(),
> + undoStore ? undoStore : new KisDumbUndoStore()))
> +{

Is the thing really called Dumb? I guess it should be Dummy, assuming that it is an undo store that doesn't really allow undoing anything. Unrelated to this patch though, so discard this comment.

Yes -- it's really called that. Dummy probably would be a better name, since it's indeed an undo store that doesn't save commands.

The reason why it was not called Dummy is that there are two variants of "dummy" undo adapter: KisDumbUndoStore and KisSurrogateUndoStore. And the latter one is much more "dummy" is a sense that is used in programming world (mostly in unittesting). That is it does its job, can undo/redo, but has no dependencies on KisDocument and stuff. Therefore I explicitly decided not to use that well-known name "dummy" to not mess up things even more. If you have better ideas for both the classes, I'm ok with renaming them.

dkazakov requested changes to this revision.Nov 7 2016, 7:39 AM
dkazakov edited edge metadata.

Hi, @rempt!

I have a feeling that your changes are a bit too severe :)

  1. image->clone(true) should still be called under the lock held. The lock might be not a barrier, but there should still be a lock. Otherwise there are crashes possible in KisNode's constructor.
  1. Before doing any saving and locking one should do

d->image->requestStrokeEnd();
QApplication::processEvents();

To notify the continued strokes like move and transform to finish.

  1. These two points mean that SafeSavingLocker or at least something like that is still necessary.

Right now you just let the user to save garbage instead of his file, even when there is no emergency (deadlock) situation is present.

The safe saving algorithm is a bit complicated, but we already have most of it implemented. As far as I can tell, the only thing you should do is to add a button "Save anyway" to KisDelayedSaveDialog which make a clone of the image and saves it separately. That will perfectly work for 3.1, and for the latter release we will able to adjust the complicated algorithm in a correct way.

The full saving algorithm should be like that:

Autosaving:

  1. Try barrier lock the image (like in SafeSavingLocker).
  2. If locking successful withing some sane timeout, clone the image, unlock the image
  3. If barrier locking is not successful, try a couple of times with emergencyAutosaveTimeout and after a couple of attempt just do normal image->lock() and clone the image, unlock the image
  4. Save the cloned image, preferably in a separate thread

Normal saving:

  1. Try barrier lock.
  2. If successful, clone the image, unlock and save in a separate thread
  3. If not successful, show the waiting dialog and provide an additional button like "save anyway"
  4. If "save anyway" is pressed, lock(), clone(), unlock() and save the image in a separate or current thread.

Just to be clear, saving/cloning the image without barrierLock() held can cause garbage in a file, which is a dataloss, because due to race conditions setModified() can reach the document during the image is saved, therefore the user will not even know that his image is not saved correctly. Saving/cloning the image under usual lock() held, should be just a last resort, when the user has no other option left (like in autosave or "Save anyway").

This revision now requires changes to proceed.Nov 7 2016, 7:39 AM
rempt abandoned this revision.Nov 2 2017, 8:47 AM

Saving in the background has landed.