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

Spawn threads for requests in file manager #12268

Merged
merged 1 commit into from Jul 9, 2016
Merged

Conversation

@izgzhen
Copy link
Contributor

izgzhen commented Jul 5, 2016

r? @Manishearth

Part of #11131.

  • There are tests for these changes OR

This change is Reviewable

@highfive
Copy link

highfive commented Jul 5, 2016

Heads up! This PR modifies the following files:

  • @KiChjang: components/net/filemanager_thread.rs, components/net/blob_loader.rs
@highfive
Copy link

highfive commented Jul 5, 2016

warning Warning warning

  • These commits modify net code, but no tests are modified. Please consider adding a test!
@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 5, 2016

It won't build now, the problem:

servo/components/net/filemanager_thread.rs:131:25: 131:30 error: `store` does not live long enough
servo/components/net/filemanager_thread.rs:131                         store.write().unwrap().select_file(filter, sender, origin)
                                                                                                         ^~~~~
servo/components/net/filemanager_thread.rs:131:25: 131:83 note: reference must be valid for the destruction scope surrounding block at 131:24...
servo/components/net/filemanager_thread.rs:131                         store.write().unwrap().select_file(filter, sender, origin)
                                                                                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
servo/components/net/filemanager_thread.rs:131:25: 131:83 note: ...but borrowed value is only valid for the block at 131:24
servo/components/net/filemanager_thread.rs:131                         store.write().unwrap().select_file(filter, sender, origin)
                                                                                                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
servo/components/net/filemanager_thread.rs:141:31: 141:36 error: `store` does not live long enough
servo/components/net/filemanager_thread.rs:141                         match store.read().unwrap().try_read_file(id, origin) {
                                                                                                               ^~~~~
servo/components/net/filemanager_thread.rs:140:65: 145:22 note: reference must be valid for the destruction scope surrounding block at 140:64...
servo/components/net/filemanager_thread.rs:140                     spawn_named("read file".to_owned(), move || {
                                                                                                                                                 ^
servo/components/net/filemanager_thread.rs:140:65: 145:22 note: ...but borrowed value is only valid for the block at 140:64
servo/components/net/filemanager_thread.rs:140                     spawn_named("read file".to_owned(), move || {

I am afraid there might be some compiler bug in this? I can avoid part of them by adding a pair of braces, but not all :(

@Manishearth
Copy link
Member

Manishearth commented Jul 5, 2016

You need to let store = store.write().unwrap() or something similar. That extends the borrow.servo/servo wrote:It won't build now, the problem:

servo/components/net/filemanager_thread.rs:131:25: 131:30 error: store does not live long enough
servo/components/net/filemanager_thread.rs:131 store.write().unwrap().select_file(filter, sender, origin)
^~~~~
servo/components/net/filemanager_thread.rs:131:25: 131:83 note: reference must be valid for the destruction scope surrounding block at 131:24...
servo/components/net/filemanager_thread.rs:131 store.write().unwrap().select_file(filter, sender, origin)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
servo/components/net/filemanager_thread.rs:131:25: 131:83 note: ...but borrowed value is only valid for the block at 131:24
servo/components/net/filemanager_thread.rs:131 store.write().unwrap().select_file(filter, sender, origin)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
servo/components/net/filemanager_thread.rs:141:31: 141:36 error: store does not live long enough
servo/components/net/filemanager_thread.rs:141 match store.read().unwrap().try_read_file(id, origin) {
^~~~~
servo/components/net/filemanager_thread.rs:140:65: 145:22 note: reference must be valid for the destruction scope surrounding block at 140:64...
servo/components/net/filemanager_thread.rs:140 spawn_named("read file".to_owned(), move || {
^
servo/components/net/filemanager_thread.rs:140:65: 145:22 note: ...but borrowed value is only valid for the block at 140:64
servo/components/net/filemanager_thread.rs:140 spawn_named("read file".to_owned(), move || {

I am afraid there might be some compiler bug in this? I can avoid part of them by adding a pair of braces, but not all :(

—You are receiving this because you were assigned.Reply to this email directly, view it on GitHub, or mute the thread.

@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 6, 2016

I see, thanks, I am bitten by this caveat again 😢

@izgzhen izgzhen force-pushed the izgzhen:fm-spawn branch from 37d79a0 to 431ed8c Jul 6, 2016
@Manishearth
Copy link
Member

Manishearth commented Jul 6, 2016

r? @jdm

I think you had some thoughts on how net should do pooling/threading.

@highfive highfive assigned jdm and unassigned Manishearth Jul 6, 2016
@izgzhen izgzhen force-pushed the izgzhen:fm-spawn branch from 431ed8c to b848bc3 Jul 6, 2016
@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 6, 2016

@jdm That would be good! Better plus some brief intro on current net's condition regarding this aspect, I suppose that pooling is to create some threads beforehand and re-send msg to available ones (rather than spawn_named every time).

@jdm
Copy link
Member

jdm commented Jul 6, 2016

We could either have a dedicated thread pool (perhaps reusing the worker thread setup that layout uses), or we could make network requests spawn threads on demand but limit the number of simultaneous requests and make a queue for any that would exceed the limit.

@Manishearth
Copy link
Member

Manishearth commented Jul 6, 2016

Should this pool be shared across all net factories? Note that this factory doesn't actually talk to the network.

Perhaps we can abstract it away as ResourceManager::queue_task(type, task_closure), have it call spawn for now, and in the future fiddle with it.

@Manishearth
Copy link
Member

Manishearth commented Jul 6, 2016

@highfive highfive assigned Manishearth and unassigned jdm Jul 6, 2016
})
}
FileManagerThreadMsg::TransferMemory(entry, sender, origin) => {
spawn_named("tranfer memory".to_owned(), move || {

This comment has been minimized.

Copy link
@Manishearth

Manishearth Jul 7, 2016

Member

spelling

}

struct FileManagerStore<UI: 'static + UIProvider> {
entries: HashMap<Uuid, FileStoreEntry>,

This comment has been minimized.

Copy link
@Manishearth

Manishearth Jul 7, 2016

Member

Eventually the entry itself should be hashmap protected so that we can have a downgrade operation. Ho.wever, this may need a separate implementation of HashMap that has atomic downgrade primitives built in, since I'm not sure this can be done directly by composing stdlib types.

FileManagerThreadMsg::SelectFiles(filter, sender, origin) => self.select_files(filter, sender, origin),
FileManagerThreadMsg::SelectFile(filter, sender, origin) => {
spawn_named("select file".to_owned(), move || {
store.write().unwrap().select_file(filter, sender, origin);

This comment has been minimized.

Copy link
@Manishearth

Manishearth Jul 7, 2016

Member

I don't like this, we're locking the store and blocking on the user clicking something. There should never be critical sections that block on user input.

I think we should pass the store in, and within these methods we should lock right where it is necessary, and not more.

This comment has been minimized.

Copy link
@izgzhen

izgzhen Jul 8, 2016

Author Contributor

+1


if *entry.origin == origin_in {
let mut bytes = vec![];
let mut handler = File::open(filepath).unwrap();

This comment has been minimized.

Copy link
@Manishearth

Manishearth Jul 7, 2016

Member

This should similarly be handled outside the lock.

The locked section perhaps should simply clone the entry, and then something out of it can handle this match.

}

struct FileManagerStore<UI: 'static + UIProvider> {
entries: RwLock<HashMap<Uuid, FileStoreEntry>>,

This comment has been minimized.

Copy link
@izgzhen

izgzhen Jul 8, 2016

Author Contributor

@Manishearth I moved the management of RwLock to store itself, and control the blocking caused by it locally in the following first few methods.

@izgzhen izgzhen force-pushed the izgzhen:fm-spawn branch from 03e2c94 to b73a684 Jul 8, 2016
let _ = sender.send(Err(BlobURLStoreError::InvalidFileID));
return;
if let Ok(parent_id) = Uuid::parse_str(&parent_id.0) {
match self.inc_ref(&parent_id, &origin_in) {

This comment has been minimized.

Copy link
@Manishearth

Manishearth Jul 8, 2016

Member

It might be better if the incref and insert use the same lock. Technically still safe if we don't (we need to be careful about this for decrefs though).

@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 8, 2016

Weird, the CI failed with the test (which means that the dec_ref failed to do what it is supposed to), while my mac machine tests fine.

.map_err(|_| BlobURLStoreError::InvalidEntry));
try!(handler.read_to_end(&mut buffer)
.map_err(|_| BlobURLStoreError::External));
Ok(buffer)
},
FileImpl::Memory(ref buffered) => {
Ok(buffered.bytes.clone())

This comment has been minimized.

Copy link
@Manishearth

Manishearth Jul 8, 2016

Member

This is a double-clone, get_impl clones already. No need for a ref here.

@izgzhen izgzhen force-pushed the izgzhen:fm-spawn branch from b73a684 to ee510ff Jul 8, 2016
@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 8, 2016

Regarding the failed test in CI, Is it possible that the DecRef sent from client raced with ReadFile?

@Manishearth
Copy link
Member

Manishearth commented Jul 8, 2016

Which test failed?

@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 8, 2016

@Manishearth tests/unit/net/filemanager_thread::test_filemanager, in which I intentionally read after delete and expect an error sent back from file manager, it is fine on my machine, but not on CI.

@Manishearth
Copy link
Member

Manishearth commented Jul 8, 2016

Ah, that test is racy then. You need to somehow wait for the delete to complete. Right now there isn't actually a way to do that, and I'm unsure if there should be -- we don't want to have additional IPC senders lying around for testing purposes only.

Perhaps you can make the test spinlock on the file being removed (by polling the actual file manager object), and only then ask it for a file?

@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 8, 2016

@Manishearth that is certainly a way to do it, but I am thinking if that would matter in production? (like revokeURL --> then load the page instantly after that).

@Manishearth
Copy link
Member

Manishearth commented Jul 8, 2016

Oh, no. You can't expect the revocation to happen immediately. Does the spec say that we should?

@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 8, 2016

No, it says "Revocation of a Blob URL decouples the Blob URL from the resource it refers to, and if it is dereferenced after it is revoked, user agents must act as if a network error has occurred". And I think by calling blob.Close after revokeURL on script side could prevent bad things as well.

@Manishearth
Copy link
Member

Manishearth commented Jul 8, 2016

Okay, so this touches on two issues.

Firstly, yes, we will need a way to block on DecRef messages, with a return sender.

However, that's not the whole story! We can still break the "revocation should work immediately" rule in the current design by having a slice as well. If the slice exists and has its own blob URI, the parent blob will not be cleaned up anyway, and further requests will still work. I think I mentioned this before, but when we implement revocation, we will actually need to keep track of blob URIs separately, not just in the refcount. So, if you create a blob URI, you should set the "blob_uri_exists" field on the entry to true, and it should be unset when you revoke a blob URI. Network requests to a revoked URI will fail even if the entry exists. Blobs should only be cleaned up when both the blob_uri_exists field and the refcount are 0; this prevents blob URIs from disappearing after their source blobs are closed.

Since we're not implementing revocation in this PR, I'm fine with making decref block for now, and addressing the above in the revocation PR.

I also suspect that in most browsers revocation won't be immediate anyway, but we should look into that more.

@izgzhen izgzhen force-pushed the izgzhen:fm-spawn branch from ee510ff to bf18225 Jul 8, 2016
@izgzhen
Copy link
Contributor Author

izgzhen commented Jul 9, 2016

DecRef ack is done.

@Manishearth
Copy link
Member

Manishearth commented Jul 9, 2016

@bors-servo
Copy link
Contributor

bors-servo commented Jul 9, 2016

📌 Commit bf18225 has been approved by Manishearth

@bors-servo
Copy link
Contributor

bors-servo commented Jul 9, 2016

Testing commit bf18225 with merge c2a22bd...

bors-servo added a commit that referenced this pull request Jul 9, 2016
Spawn threads for requests in file manager

r? @Manishearth

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors

<!-- Either: -->
- [x] There are tests for these changes OR

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12268)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

bors-servo commented Jul 9, 2016

@bors-servo bors-servo merged commit bf18225 into servo:master Jul 9, 2016
2 checks passed
2 checks passed
continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

5 participants
You can’t perform that action at this time.