Skip to content
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

Make sure ShardSenders are dropped before ShardWriter #13

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

sreenathkrishnan
Copy link
Collaborator

@sreenathkrishnan sreenathkrishnan commented Jul 15, 2019

Changes

  1. Include an immutable reference to theShardWriter inside ShardSender
  2. ShardSender<T> is now ShardSender<'a, T, S>
  3. This required adding the trait bound S: Send, but that should not be restrictive at all
  4. Update the doc example

Illegal code

API usage of the form:

{
    let mut writer: ShardWriter<...> = ShardWriter::new(...);
    let mut sender = writer.get_sender();
    ...
    writer.finish()?;
}

will not compile with a clear message

error[E0502]: cannot borrow `writer` as mutable because it is also borrowed as immutable
  --> src/lib.rs:45:9
   |
31 |         let mut sender = writer.get_sender();
   |                          ------ immutable borrow occurs here
...
39 |         writer.finish()?;
   |         ^^^^^^^^^^^^^^^ mutable borrow occurs here
40 |     }
   |     - immutable borrow might be used here, when `sender` is dropped and runs the `Drop` code for type `shardio::ShardSender`

Legal code

You can either not call finish():

{
    let writer: ShardWriter<...> = ShardWriter::new(...);
    let mut sender = writer.get_sender();
    ...
}

or explicitly scope the sender and call finish

{
    let mut writer: ShardWriter<...> = ShardWriter::new(...);
    {
        let mut sender = writer.get_sender();
        ...
    }
    writer.finish()
}

calling finish on ShardWriter or dropping ShardWriter
@coveralls
Copy link

coveralls commented Jul 15, 2019

Pull Request Test Coverage Report for Build 59

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 36 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.005%) to 95.233%

Files with Coverage Reduction New Missed Lines %
lib.rs 36 94.88%
Totals Coverage Status
Change from base Build 57: 0.005%
Covered Lines: 879
Relevant Lines: 923

💛 - Coveralls

@pmarks
Copy link
Contributor

pmarks commented Jul 15, 2019

Interesting! I think this means we can't send the ShardSender to a new thread, because there's no way to ensure that a thread finishes with a static lifetime. scoped threads are an option: https://docs.rs/crossbeam/0.6.0/crossbeam/thread/index.html
but I'm not sure they're sufficiently general. I need to think a bit about whether we care about the multi-threaded case

@nlhepler
Copy link
Contributor

We could just mediate that with an Arc instead of a bare reference. The get_sender convenience API may need to go away though..

@nlhepler
Copy link
Contributor

Actually, we could implement a trait to carry get_sender and implement it for Arc.

@pmarks
Copy link
Contributor

pmarks commented Jul 15, 2019

I think Sreenath's idea is to force the lifetime of the sender to be less than the lifetime of the writer, so that when you drop / call finish() on the writer, you're guaranteed to be done sending. If we put the writer in Arc<> then I think we have the same problem as now: you can close the writer before the senders.

(As an aside, we should see if we can do impl ShardWriter { fn finish(self) -> Result<> } so that finish() consume the writer.

@pmarks
Copy link
Contributor

pmarks commented Jul 15, 2019

@nlhepler - sorry - if the sender is inside Arc<>, then you can't call finish() on it. You'd need something like 'join' that could wait until all the other handles are gone, then gives you back exclusive ownership. That could deadlock on a single thread if you don't drop the senders before you 'join'.

@sreenathkrishnan
Copy link
Collaborator Author

Yeah, I don't see a way to work around both lifetime order and 'static requirement using Arc. Scoped threads seem to be the right primitive to use if you want multi-threaded support for senders. This works as expected:

let writer = ...
crossbeam_utils::thread::scope(|scope| {
    let mut sender = writer.get_sender();
    scope.spawn(move |_| {
        for i in 0..(2 << 16) {
            sender.send(DataStruct { a: (i%25) as u64, b: (i%100) as u32 });
        }
    });
}).unwrap();

Is this reasonable?

@@ -1,2 +1,3 @@
target
Cargo.lock
test.shardio

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an end-of-line character to this file. Best to configure your text editor to ensure that text files end with an end-of-line character.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants