Sync transfers for realistic progress bar updates - #2819
Conversation
|
I see the progress bar dialog is not "modal" so if I click the unmount button while the transfer is in progress Files crashes - but this is a different issue that already exists in the current main branch. |
|
The value of |
The dialog is intended to be modal so we will have to block unmounting of any volume being written to (I am surprised the system does not do that) and show a warning if necessary. Doesn't the system show a "this volume is busy" warning? |
|
I wonder whether this constant can be removed altogether or reduced to a small value 🤔 How fast can a reasonably accurate transfer rate be known? Maybe better to use a minimum number of bytes transferred rather than a fixed time? |
No warning, the app quits (crashes) if I click unmount while transfer is in progress. Should I open another PR to force the transfer dialog to be modal and not allow interacting with the app? |
I've reduced the const down from 15 seconds to 3 seconds - seems reasonable for both slow and fast disks/hardware... if the hardware is fast the copy dialog completes and closes quickly anyway, if the hardware is slow then about 3 seconds seems okay for the transfer speed indication to show up? |
Yep I thought as much haha. Thanks for looking at this PR. I think we can leave this reduced value in place for now, dont want to mess with further code changes to use a different way of calculating the duration before transfer speeds show up. |
I have had the same issue. Another app failed to unmount one of my hard drive volume saying “This volume is busy”. So I tried with Files and it just crashed. |
|
@flodavid Could you open an issue regarding your crash please? I'll try and look into file transfers again soon. |
@jeremypw do you want me to make the transfer dialog modal thus preventing user interaction with the main app while it's visible? i can do that in this PR itself presuming it's a "one liner" change? |
|
@Vishal No, I think how Files deals with trying to unmount a busy volume is better addressed with a separate issue/PR. File transfers should be non-modal and occur in the background if possible. Not sure how to stop people pulling out a USB stick prematurely We need to get a true (i.e. unaffected by caching) indication of whether a file transfer is ongoing/complete and show a warning and/or style the sidebar item approriately. |
@jeremypw okay fair enough - so is this PR acceptable as an interim solution to make it more evident to users that a transfer is still in progress and to not unplug or unmount while so? further fixes/improvements in future PRs? |
|
@vjr Yes. I wasn't able to test this today. Should do it tomorrow. |
jeremypw
left a comment
There was a problem hiding this comment.
This appears to slow file transfers significantly. Maybe because the progress is more realistic? But it also seems to slow internal transfers between SSDs noticeably. I wonder if the syncing can be done less often and/or only when transferring to an external drive? Or should that wait for a future PR?
I noticed several areas that need cleaning up and reviewing in the file operations area (and it is still awaiting complete transfer to Vala) anyway.
I experimented with only syncing when a certain number of bytes had been transferred which seemed to reduce the impact on speed.
|
Yes I see the impact of the Plus, it might be that the global posix Marking this PR as draft while I come up with a better approach. |
I tested on a slow antique MTP player tranferring many small (100 byte) files and the reported transfer rate was about half with this PR. I must admit I did not manually time the complete transfer but cancelled before it finished. The situation is complicated by the fact that when transferring many small files the transfer slows down so the predicted time left is not very helpful - it often goes up instead of down. This was reported long ago but has never really been diagnosed. I think it may be to do with the "undo" processing taking longer and longer but I haven't dug into file operations nitty-gritty for a long time. |
Replace g_file_copy () and g_file_move () with custom copy_move_with_sync () method which conditionally calls fsync () periodically.
|
@jeremypw I've added a (WIP) custom Yes, the periodic I just tested individual file transfers of various sizes on two usb storage I have, a fast ssd and a slow flash stick, also tried multiple small files. At the moment this new approach needs work to fix crashes/errors trying to copy directories, haven't yet tried other copy/move styles, folders, overwrite, recurse whatever. Will continue working on this, see if the original goal of the issue (avoid users inadvertently unplugging their usb devices) can be resolved. Thanks for looking into this. |
|
@vjr Thanks for working on this! At the end of the day a (modest) reduction in speed is worth it to fix serious issues like crashes and data loss. I'll have another look at this tomorrow. |
|
If you are interested in doing more work on file operations there is a long outstanding issue #137 which it would be good to fix! No pressure though. |
|
One thing I am not too sure about: what happens when an error is thrown e.g. at line 127 in CopyMoveJob.vala? Are streams left open? Would it be better to handle errors internally and have |
IINM (might have read this on the vala.dev website) it auto closes the stream when going out of scope aka final ref released, regardless of success or failure or error thrown/returned? |
Yep I think I can give this a shot, see if I can fix. |
If you point out the code style issues (hopefully not too many) I'm happy to address in this PR itself, is it the initial new method return value/signature being on separate line? Also, maybe the first few lines where I check If you don't mind retesting I would like to fix/address the MTP issue as I mentioned in #2819 (comment) I think that might work. Other than that, please point out any remaining issues in this PR that I can address to get it approved and merged... I'm really motivated to have this fix done! Thanks! |
|
Marking as draft while I push some more pending commits and address any review feedback... |
This reverts commit a737e58. Was left over from attempt to fix MTP device not being detected as can_unplug.
|
@vjr fyi elementary code style info here https://docs.elementary.io/develop/writing-apps/code-style. I'll go through and point out any remaining issues later today. Note that old code (particularly C) does not necessary fully comply but new code should. |
|
@jeremypw apologies for the repeated drafting/undrafting but could you take another look at the code and maybe redo a few of your tests, especially with your MTP device? thank you! |
|
A convention I use (which I do not think is mentioned in the guide) is to use only one indent for the parameters of functions if there are too many to fit on one line and all parameters are indented the same. Closing bracket groups should be on the next line: The same applies to function calls that do not fit within the 120 char line liimit except one more indent that the current line indent is used. The scope and type of functions etc are always inline with the name. In general I do not like using whitespace between lines that are logically related e.g. initializing parameters for the same clause. But whitespace is put after clauses ending in |
@jeremypw i've hopefully understood and properly applied your advice in cc54180 ? |
| } | ||
| } | ||
|
|
||
| var overwrite = (flags & FileCopyFlags.OVERWRITE) != 0; |
There was a problem hiding this comment.
This variable is not use 'til later so can be moved to before the clause in which it is used, and whitespace reduced to a single line.
| cancellable, | ||
| progress_callback | ||
| ); | ||
| } finally { |
There was a problem hiding this comment.
Shouldn't need finally here as we did not setup anything that needs to be undone at this point.
|
|
||
| if (src_is_dir) { | ||
| if (dest_is_dir) { | ||
| error = overwrite ? IOError.WOULD_MERGE : IOError.EXISTS; |
There was a problem hiding this comment.
Can't we throw an error straightaway here and in similar other places in this clause? You can use the simpler form:
throw new IO.Error.EXISTS ("Put a suitable error message here if needed");
for example.
| } | ||
| } | ||
|
|
||
| if (error >= 0) { |
There was a problem hiding this comment.
If errors are thrown straightaway this clauses would not be needed.
| var dest_is_dir = FileUtils.file_is_dir (dest); | ||
| var dest_exists = dest.query_exists (); | ||
|
|
||
| var error = -1; |
There was a problem hiding this comment.
If errors are thrown straightaway this variable would not be needed here.
| fd = out.get_fd (); | ||
| } | ||
|
|
||
| var success = false; |
There was a problem hiding this comment.
Whitespace between here and the related while clause can be removed
| int64 last_sync_time = 0; | ||
|
|
||
| while (true) { | ||
| var read = in.read (buffer, cancellable); |
There was a problem hiding this comment.
This can throw and error and immediately return without closing streams. Do we need to put this in a try/catch/finally clause to ensure streams are closed?
| var now = get_monotonic_time (); | ||
| if (last_sync_time == 0 || (now - last_sync_time).abs () >= SYNC_INTERVAL_MICROS) { | ||
| if (fd >= 0) { | ||
| Posix.fsync (fd); |
There was a problem hiding this comment.
Have you considered using flush method of GLib.OutputStream instead of the Posix function. We would then not need fd. I am not sure whether they are equivalent but its worth a try.
| } | ||
| } | ||
|
|
||
| if (fd >= 0) { |
There was a problem hiding this comment.
Closing the stream will flush its data.
Apparently streams are automatically closed when the last reference is dropped but it might be better to close them explicitly and handle any errors.
jeremypw
left a comment
There was a problem hiding this comment.
With an unpluggable MTP device, it is identified as unpluggable but fails to copy or move files onto it. This is because creating the output stream fails and the code does not handle that - it should fall back to a "normal" copy/move.
Fixes #2818
BEFORE:
files-progress-before.webm
AFTER:
files-progress-after.webm