-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for transferring multiple files #2
Conversation
I want to be able to transfer multiple files, and I want the sender to control the file name so the receiver does not have to. This adds a step in that direction by transferring the plan, but it breaks the actual transfer. I'll fix that up next.
For a long time I was puzzled by an "illegal seek" error. It turns out, the file was being closed becuase I dropped it immediately. The raw fd is like a borrow, I need an owned fd.
We still need to open a final connection to break the sender out of the accept() loop, but I'll do that in a follow-up commit.
It's a hack maybe, but I really want to avoid the async ecosystem.
Not sure if this is a good idea in the end or not, it harms debugging but creates cleaner files, which on SSDs probably does not matter but on spinning disks it can. But we use SSDs, so I am totally guilty of optimizing prematurely here.
It doubles the size of the application, in lines of code 😳 But much of that is comments and it’s still pretty small anyway. |
let file = std::fs::File::open(fname)?; | ||
let metadata = file.metadata()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a quick and dirty approach just propagating error is okay I guess, but those error messages can be very confusing for users not familiar with the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that’s a fair point, the io::Error
errors tend to be very unhelpful because they don’t contain any context about the file that failed. I opened #5 to fix this, but for now my intention is to be quick and dirty.
send_states.push(state); | ||
} | ||
|
||
plan.assert_paths_relative(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would info in help text, that paths need to be relative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, I added a proper usage string now that documents the parameters, including that paths need to be relative.
let count = (end - off) as usize; | ||
let n_written = unsafe { libc::sendfile(out_fd, self.in_fd, &mut off, count) }; | ||
let n_written = unsafe { libc::sendfile64(out_fd, in_fd, &mut off, count) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I guess it is a bit more performant than reading from file and writing to socket, but it makes it Linux only. Perhaps we could use something like nix
crate https://docs.rs/nix/0.29.0/nix/sys/sendfile/fn.sendfile.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to keep it Linux only, it would still be worth commenting that sendfile64
modifies the offset when returning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now I only care about Linux, all our servers are Linux after all. I added a comment and also a note in the readme.
Hmm, the use of Who cares? Well, I wanted to see if this was faster than Edit: I looked briefly into whether it is possible to avoid these dependencies, but there is no easy way to do so at least. I suspect that |
And that we target Linux only.
Overview
As the title says, enable transferring multiple files in one session.
Details
Why is it hard to properly stop the sender?
Because the sender does not know how many threads we’ll use for the transfer, it just listens on a socket, and for every incoming connection it spawns a new sender thread. But then when transfer is done, the sender still sits there, blocked on its
accept()
call. I was hoping there exists an accept with timeout, but there doesn’t. There exists a non-blocking accept, but it would make us busy-wait. We could add a sleep, but it adds a polling delay both to connecting and to the shutdown. We could uselibc::poll
, but that’s a bunch of bookkeeping and unsafe code that I think is overkill for this program, the entire application is less than 500 lines, I don’t want to add 100 lines just do do a clean shutdown. We could instead pull in Tokio and the async ecosystem to do the poll for us, but that brings in a lot of crates and requires changing everything to async everywhere.So instead I went for this
hacksolution: the sender, after accepting, if there is nothing to send, it exits. And then the receiver, after it is done receiving, does a final connect to break the sender out of itsaccept()
call. Tada, problem solved!