{"action":"created","comment":{"url":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/29526496","id":29526496,"diff_hunk":"@@ -0,0 +1,261 @@\n+- Feature Name: scoped\n+- Start Date: 2015-04-16\n+- RFC PR: (leave this empty)\n+- Rust Issue: (leave this empty)\n+\n+# Summary\n+\n+This RFC proposes an alternative to the `thread::scoped` API that does not rely\n+on RAII, and is therefore memory safe even if destructors can be leaked.\n+\n+*This RFC was inspired by ideas from @nikomatsakis and @arielb1.*\n+\n+# Motivation\n+\n+The `thread::scoped` API allows parent threads to launch child threads that can\n+have references to *stack data* owned by the parent. This style of API makes\n+fork-join programming easy and efficient. It is also safe, *as long as the\n+parent's stack frame outlives the children threads*.\n+\n+In the initial version of the API, safety was \"guaranteed\" through an RAII-style\n+guard which, upon destruction, would join (wait for) any still-living children\n+threads. This join guard is connected to the lifetime of any borrows by children\n+threads, thereby (we thought) preventing the related parent stack frames from\n+being popped until the joins were complete.\n+\n+Unfortunately,\n+[it is possible to avoid the guard destructor in safe Rust](https://github.com/rust-lang/rust/issues/24292). Rust\n+does not guarantee that the destructor for a value that escapes will be run.\n+\n+Note that this does *not* mean that RAII should be avoided in general. **RAII is\n+still a safe, idiomatic, and ergonomic style of programming for Rust.** It just\n+means that you cannot write `unsafe` code whose safety relies on RAII to run\n+destructors.\n+\n+It may be possible to finesse this issue by locating every possible source of\n+destructor leakage (such as `Rc` cycles) and tying it to some kind of \"`Leak`\"\n+marker trait, which would be implemented by default for all Rust types (and\n+basically means \"safe to leak\").  Ideas along these lines have been discussed\n+extensively [elsewhere](https://github.com/rust-lang/rfcs/pull/1066), but they\n+represent longer-term language changes that may or may not work out.\n+\n+This RFC, by contrast, proposes a general API that works in today's Rust, and\n+can:\n+\n+* Guarantee execution of a piece of code prior to a scope being exited, even in\n+  the presence of unwinding, `Rc` cycles, and so on.\n+\n+* Thereby be used to recover a safe `thread::scoped` API.\n+\n+While the API proposed here is slightly less ergonomic than the RAII-style\n+`scoped` API, **it has the benefit of much more clearly marking the point at\n+which children threads will be joined, which was left more implicit in the old\n+API.**\n+\n+# Detailed design\n+\n+The proposal has two pieces: a general mechanism for \"deferred computation\",\n+and a new `thread::scoped` API that takes advantage of it.\n+\n+## Deferred computation\n+\n+Here's a very simple API for deferring computation:\n+\n+```\n+// in std:\n+\n+mod thread {\n+    pub struct Scope<'a> { ... }\n+\n+    pub fn scope<'a, F, R>(f: F) -> R where F: FnOnce(&Scope<'a>) -> R;\n+\n+    impl<'a> Scope<'a> {\n+        pub fn defer<F>(&self, f: F) where F: FnOnce() + 'a;\n+    }\n+}\n+```\n+\n+(This is put in the `std::thread` module because (1) scopes are per-thread\n+concepts (2) it fits nicely with other functionality like `panicking` and\n+`catch_panic`.)\n+\n+You call `scope` to introduce a new `Scope` value, which is passed into a\n+closure that is immediately invoked.\n+\n+The closure can use the `defer` method to register callbacks to invoke upon\n+*any* exit from the callback, including unwinding. Unlike RAII guards, there is\n+no way to leak these callbacks, and the implementation shown below works around\n+[known cases](https://github.com/rust-lang/rust/issues/14875) of destructor\n+leakage.\n+\n+This is a generally useful mechanism that can avoid the need to create custom\n+RAII guards for situations where you might use `try`/`finally` in other\n+languages. But it is also just the support needed for scoped threads.\n+\n+## Scoped threads\n+\n+To recover scoped threads, we extend the `Scope` type with a method for spawning\n+threads:\n+\n+```rust\n+impl<'a> Scope<'a> {\n+    pub fn spawn<F, T>(&self, f: F) -> thread::JoinHandle<T> where\n+        F: FnOnce() -> T + Send + 'a,\n+        T: Send + 'a;\n+}\n+```\n+\n+Like the original `thread::scoped` API, this allows for the child thread's\n+closure to borrow data (with lifetime `'body`) from the parent thread. These\n+borrows are bounded by the lifetime of the `Scope` value, and the implementation\n+uses the `defer` method to add a callback that will join on (wait for completion\n+of) the child thread on exit from the scope -- thus restoring memory safety.\n+\n+Note that, while previously one might return `JoinGuard`s outward to expand the\n+scope of joining, the pattern here is reversed: you call `scope` at the\n+outer-most scope, and then pass a reference to the `Scoped` value inward.\n+\n+Putting it all together, here's an example from TRPL, and a version adjust to the new API:\n+\n+```rust\n+// using the thread::scoped API\n+fn old_trpl_example() {\n+    let data = Mutex::new(vec![1u32, 2, 3]);\n+\n+    let guards: Vec<_> = (0..2).iter().map(|_| {\n+        thread::scoped(|| {\n+            let mut data = data.lock().unwrap();\n+            data[i] += 1;\n+        })\n+    }).collect();\n+\n+    // threads implicitly joined here, when `guards` goes out of scope and drops\n+    // its contents\n+}\n+```\n+\n+```rust\n+// using the proposed thread::scope API\n+fn new_trpl_example() {\n+    let data = Mutex::new(vec![1u32, 2, 3]);\n+\n+    thread::scope(|s| {\n+        for i in 0..2 {\n+            s.spawn(|| {\n+                let mut data = data.lock().unwrap();\n+                data[i] += 1;\n+            });","path":"text/0000-scoped-take-2.md","position":147,"original_position":147,"commit_id":"75db90de40849d7cd28e334388ffa74b9e7a9bcf","original_commit_id":"75db90de40849d7cd28e334388ffa74b9e7a9bcf","user":{"login":"kballard","id":714,"avatar_url":"https://avatars.githubusercontent.com/u/714?v=3","gravatar_id":"","url":"https://api.github.com/users/kballard","html_url":"https://github.com/kballard","followers_url":"https://api.github.com/users/kballard/followers","following_url":"https://api.github.com/users/kballard/following{/other_user}","gists_url":"https://api.github.com/users/kballard/gists{/gist_id}","starred_url":"https://api.github.com/users/kballard/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kballard/subscriptions","organizations_url":"https://api.github.com/users/kballard/orgs","repos_url":"https://api.github.com/users/kballard/repos","events_url":"https://api.github.com/users/kballard/events{/privacy}","received_events_url":"https://api.github.com/users/kballard/received_events","type":"User","site_admin":false},"body":"> There is a bug related to lifetimes not being fully constrained that @nikomatsakis has been looking into already\r\n\r\nAh hah. I *thought* the current API should have been correct (see the last sentence of my first comment in this thread), so I was quite confused as to why it didn't work. I'm glad to have confirmation that I'm not slowly going crazy :wink: ","created_at":"2015-05-01T19:20:53Z","updated_at":"2015-05-01T19:20:53Z","html_url":"https://github.com/rust-lang/rfcs/pull/1084#discussion_r29526496","pull_request_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/1084","_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/29526496"},"html":{"href":"https://github.com/rust-lang/rfcs/pull/1084#discussion_r29526496"},"pull_request":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/1084"}}},"pull_request":{"url":"https://api.github.com/repos/rust-lang/rfcs/pulls/1084","id":33913715,"html_url":"https://github.com/rust-lang/rfcs/pull/1084","diff_url":"https://github.com/rust-lang/rfcs/pull/1084.diff","patch_url":"https://github.com/rust-lang/rfcs/pull/1084.patch","issue_url":"https://api.github.com/repos/rust-lang/rfcs/issues/1084","number":1084,"state":"open","locked":false,"title":"RFC: Scoped threads, take 2","user":{"login":"aturon","id":709807,"avatar_url":"https://avatars.githubusercontent.com/u/709807?v=3","gravatar_id":"","url":"https://api.github.com/users/aturon","html_url":"https://github.com/aturon","followers_url":"https://api.github.com/users/aturon/followers","following_url":"https://api.github.com/users/aturon/following{/other_user}","gists_url":"https://api.github.com/users/aturon/gists{/gist_id}","starred_url":"https://api.github.com/users/aturon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aturon/subscriptions","organizations_url":"https://api.github.com/users/aturon/orgs","repos_url":"https://api.github.com/users/aturon/repos","events_url":"https://api.github.com/users/aturon/events{/privacy}","received_events_url":"https://api.github.com/users/aturon/received_events","type":"User","site_admin":false},"body":"This RFC proposes an alternative to the `thread::scoped` API that does not rely\r\non RAII, and is therefore memory safe even if destructors can be leaked.\r\n\r\n*This RFC was inspired by ideas from @nikomatsakis and @arielb1.*\r\n\r\n[Rendered](https://github.com/aturon/rfcs/blob/scoped-take-2/text/0000-scoped-take-2.md)","created_at":"2015-04-23T00:12:48Z","updated_at":"2015-05-01T19:20:53Z","closed_at":null,"merged_at":null,"merge_commit_sha":"63316f643c8197df095ff7bc04b3586b62db4ce6","assignee":{"login":"aturon","id":709807,"avatar_url":"https://avatars.githubusercontent.com/u/709807?v=3","gravatar_id":"","url":"https://api.github.com/users/aturon","html_url":"https://github.com/aturon","followers_url":"https://api.github.com/users/aturon/followers","following_url":"https://api.github.com/users/aturon/following{/other_user}","gists_url":"https://api.github.com/users/aturon/gists{/gist_id}","starred_url":"https://api.github.com/users/aturon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aturon/subscriptions","organizations_url":"https://api.github.com/users/aturon/orgs","repos_url":"https://api.github.com/users/aturon/repos","events_url":"https://api.github.com/users/aturon/events{/privacy}","received_events_url":"https://api.github.com/users/aturon/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/1084/commits","review_comments_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/1084/comments","review_comment_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rust-lang/rfcs/issues/1084/comments","statuses_url":"https://api.github.com/repos/rust-lang/rfcs/statuses/75db90de40849d7cd28e334388ffa74b9e7a9bcf","head":{"label":"aturon:scoped-take-2","ref":"scoped-take-2","sha":"75db90de40849d7cd28e334388ffa74b9e7a9bcf","user":{"login":"aturon","id":709807,"avatar_url":"https://avatars.githubusercontent.com/u/709807?v=3","gravatar_id":"","url":"https://api.github.com/users/aturon","html_url":"https://github.com/aturon","followers_url":"https://api.github.com/users/aturon/followers","following_url":"https://api.github.com/users/aturon/following{/other_user}","gists_url":"https://api.github.com/users/aturon/gists{/gist_id}","starred_url":"https://api.github.com/users/aturon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aturon/subscriptions","organizations_url":"https://api.github.com/users/aturon/orgs","repos_url":"https://api.github.com/users/aturon/repos","events_url":"https://api.github.com/users/aturon/events{/privacy}","received_events_url":"https://api.github.com/users/aturon/received_events","type":"User","site_admin":false},"repo":{"id":20423396,"name":"rfcs","full_name":"aturon/rfcs","owner":{"login":"aturon","id":709807,"avatar_url":"https://avatars.githubusercontent.com/u/709807?v=3","gravatar_id":"","url":"https://api.github.com/users/aturon","html_url":"https://github.com/aturon","followers_url":"https://api.github.com/users/aturon/followers","following_url":"https://api.github.com/users/aturon/following{/other_user}","gists_url":"https://api.github.com/users/aturon/gists{/gist_id}","starred_url":"https://api.github.com/users/aturon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aturon/subscriptions","organizations_url":"https://api.github.com/users/aturon/orgs","repos_url":"https://api.github.com/users/aturon/repos","events_url":"https://api.github.com/users/aturon/events{/privacy}","received_events_url":"https://api.github.com/users/aturon/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/aturon/rfcs","description":"RFCs for changes to Rust","fork":true,"url":"https://api.github.com/repos/aturon/rfcs","forks_url":"https://api.github.com/repos/aturon/rfcs/forks","keys_url":"https://api.github.com/repos/aturon/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aturon/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aturon/rfcs/teams","hooks_url":"https://api.github.com/repos/aturon/rfcs/hooks","issue_events_url":"https://api.github.com/repos/aturon/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/aturon/rfcs/events","assignees_url":"https://api.github.com/repos/aturon/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/aturon/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/aturon/rfcs/tags","blobs_url":"https://api.github.com/repos/aturon/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aturon/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aturon/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/aturon/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aturon/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/aturon/rfcs/languages","stargazers_url":"https://api.github.com/repos/aturon/rfcs/stargazers","contributors_url":"https://api.github.com/repos/aturon/rfcs/contributors","subscribers_url":"https://api.github.com/repos/aturon/rfcs/subscribers","subscription_url":"https://api.github.com/repos/aturon/rfcs/subscription","commits_url":"https://api.github.com/repos/aturon/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/aturon/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/aturon/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/aturon/rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/aturon/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/aturon/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aturon/rfcs/merges","archive_url":"https://api.github.com/repos/aturon/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aturon/rfcs/downloads","issues_url":"https://api.github.com/repos/aturon/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/aturon/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/aturon/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/aturon/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aturon/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/aturon/rfcs/releases{/id}","created_at":"2014-06-02T23:35:56Z","updated_at":"2015-01-17T17:42:40Z","pushed_at":"2015-04-25T06:21:30Z","git_url":"git://github.com/aturon/rfcs.git","ssh_url":"git@github.com:aturon/rfcs.git","clone_url":"https://github.com/aturon/rfcs.git","svn_url":"https://github.com/aturon/rfcs","homepage":"","size":2893,"stargazers_count":4,"watchers_count":4,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":4,"default_branch":"master"}},"base":{"label":"rust-lang:master","ref":"master","sha":"5afeedf2609434e8ebf327cf98b54231adea6386","user":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars.githubusercontent.com/u/5430905?v=3","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"repo":{"id":17527989,"name":"rfcs","full_name":"rust-lang/rfcs","owner":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars.githubusercontent.com/u/5430905?v=3","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rust-lang/rfcs","description":"RFCs for changes to Rust","fork":false,"url":"https://api.github.com/repos/rust-lang/rfcs","forks_url":"https://api.github.com/repos/rust-lang/rfcs/forks","keys_url":"https://api.github.com/repos/rust-lang/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rust-lang/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rust-lang/rfcs/teams","hooks_url":"https://api.github.com/repos/rust-lang/rfcs/hooks","issue_events_url":"https://api.github.com/repos/rust-lang/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/rust-lang/rfcs/events","assignees_url":"https://api.github.com/repos/rust-lang/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/rust-lang/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/rust-lang/rfcs/tags","blobs_url":"https://api.github.com/repos/rust-lang/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rust-lang/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rust-lang/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/rust-lang/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rust-lang/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/rust-lang/rfcs/languages","stargazers_url":"https://api.github.com/repos/rust-lang/rfcs/stargazers","contributors_url":"https://api.github.com/repos/rust-lang/rfcs/contributors","subscribers_url":"https://api.github.com/repos/rust-lang/rfcs/subscribers","subscription_url":"https://api.github.com/repos/rust-lang/rfcs/subscription","commits_url":"https://api.github.com/repos/rust-lang/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/rust-lang/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/rust-lang/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/rust-lang/rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/rust-lang/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/rust-lang/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rust-lang/rfcs/merges","archive_url":"https://api.github.com/repos/rust-lang/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rust-lang/rfcs/downloads","issues_url":"https://api.github.com/repos/rust-lang/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/rust-lang/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/rust-lang/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/rust-lang/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rust-lang/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/rust-lang/rfcs/releases{/id}","created_at":"2014-03-07T21:29:00Z","updated_at":"2015-04-30T09:25:17Z","pushed_at":"2015-05-01T17:13:53Z","git_url":"git://github.com/rust-lang/rfcs.git","ssh_url":"git@github.com:rust-lang/rfcs.git","clone_url":"https://github.com/rust-lang/rfcs.git","svn_url":"https://github.com/rust-lang/rfcs","homepage":"","size":11289,"stargazers_count":326,"watchers_count":326,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":198,"mirror_url":null,"open_issues_count":502,"forks":198,"open_issues":502,"watchers":326,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/1084"},"html":{"href":"https://github.com/rust-lang/rfcs/pull/1084"},"issue":{"href":"https://api.github.com/repos/rust-lang/rfcs/issues/1084"},"comments":{"href":"https://api.github.com/repos/rust-lang/rfcs/issues/1084/comments"},"review_comments":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/1084/comments"},"review_comment":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/1084/commits"},"statuses":{"href":"https://api.github.com/repos/rust-lang/rfcs/statuses/75db90de40849d7cd28e334388ffa74b9e7a9bcf"}}}}