{"action":"created","issue":{"url":"https://api.github.com/repos/Xudong-Huang/may/issues/6","repository_url":"https://api.github.com/repos/Xudong-Huang/may","labels_url":"https://api.github.com/repos/Xudong-Huang/may/issues/6/labels{/name}","comments_url":"https://api.github.com/repos/Xudong-Huang/may/issues/6/comments","events_url":"https://api.github.com/repos/Xudong-Huang/may/issues/6/events","html_url":"https://github.com/Xudong-Huang/may/issues/6","id":284326218,"number":6,"title":"Undefined behavior invoked by moving stacks between threads","user":{"login":"vorner","id":11783500,"avatar_url":"https://avatars2.githubusercontent.com/u/11783500?v=4","gravatar_id":"","url":"https://api.github.com/users/vorner","html_url":"https://github.com/vorner","followers_url":"https://api.github.com/users/vorner/followers","following_url":"https://api.github.com/users/vorner/following{/other_user}","gists_url":"https://api.github.com/users/vorner/gists{/gist_id}","starred_url":"https://api.github.com/users/vorner/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vorner/subscriptions","organizations_url":"https://api.github.com/users/vorner/orgs","repos_url":"https://api.github.com/users/vorner/repos","events_url":"https://api.github.com/users/vorner/events{/privacy}","received_events_url":"https://api.github.com/users/vorner/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":17,"created_at":"2017-12-23T20:53:47Z","updated_at":"2017-12-26T13:31:01Z","closed_at":null,"author_association":"NONE","body":"A coroutine's stack can be moved from one thread to another. However, a stack may always contain things that are not `Send`, breaking things.\r\n\r\nThis is one example:\r\n\r\n```rust\r\nextern crate may;\r\n\r\nuse std::cell::RefCell;\r\nuse std::rc::Rc;\r\nuse std::thread::ThreadId;\r\n\r\nuse may::coroutine;\r\nuse coroutine::yield_now;\r\n\r\nthread_local!(static ID: RefCell<Option<Rc<ThreadId>>> = RefCell::new(None));\r\n\r\nfn main() {\r\n    may::config().set_io_workers(60);\r\n    may::config().set_workers(60);\r\n    let h = coroutine::spawn(move || {\r\n        let v = (0..10000)\r\n            .map(|i| {\r\n                coroutine::spawn(|| {\r\n                    let handle = Rc::new(std::thread::current().id());\r\n                    ID.with(|id| {\r\n                        *id.borrow_mut() = Some(Rc::clone(&handle));\r\n                    });\r\n                    for _ in 0..10000 {\r\n                        if *handle != std::thread::current().id() {\r\n                            println!(\"Access to Rc content without a mutex from a different thread, {:?} vs {:?}\",\r\n                                     *handle, std::thread::current().id());\r\n                        }\r\n                        yield_now();\r\n                    }\r\n                })\r\n            })\r\n            .collect::<Vec<_>>();\r\n        for i in v {\r\n            i.join().unwrap();\r\n        }\r\n    });\r\n    h.join().unwrap();\r\n}\r\n```\r\n\r\nIf something else was accessing the `Rc` (like making copis of it) from the original thread (which it could, because it is accessible in the thread local storage), it would be undefined behaviour ‒ both the coroutine, that moved, and the thing in that thread (possibly other coroutine) could be accessing the counters in the Rc at the same time, or the data inside, which could be for example a `RefCell`.\r\n\r\nNow, suggesting not to use thread local storage doesn't solve anything, because:\r\n\r\n* I might not use thread local storage myself, but I can't certainly be expected to audit all the libraries I use not to use thread local storage. Even the standard library uses thread local storage internally.\r\n* There are things that are not `Send` for other reasons. One example might be Zero-MQ sockets, which explode the whole application if ever touched from a different thread then they were created in.\r\n\r\nI believe this problem is fundamental to *any* attempt to move stacks between threads in Rust. Such thing just breaks the Rust contract.\r\n\r\nSo my only suggestion is to create the coroutine in one thread (it is possible to check nothing `Send` crosses a closure boundary, so the closure can be safely sent to another thread) and then pin it there to that thread."},"comment":{"url":"https://api.github.com/repos/Xudong-Huang/may/issues/comments/353969139","html_url":"https://github.com/Xudong-Huang/may/issues/6#issuecomment-353969139","issue_url":"https://api.github.com/repos/Xudong-Huang/may/issues/6","id":353969139,"user":{"login":"Xudong-Huang","id":14049451,"avatar_url":"https://avatars2.githubusercontent.com/u/14049451?v=4","gravatar_id":"","url":"https://api.github.com/users/Xudong-Huang","html_url":"https://github.com/Xudong-Huang","followers_url":"https://api.github.com/users/Xudong-Huang/followers","following_url":"https://api.github.com/users/Xudong-Huang/following{/other_user}","gists_url":"https://api.github.com/users/Xudong-Huang/gists{/gist_id}","starred_url":"https://api.github.com/users/Xudong-Huang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Xudong-Huang/subscriptions","organizations_url":"https://api.github.com/users/Xudong-Huang/orgs","repos_url":"https://api.github.com/users/Xudong-Huang/repos","events_url":"https://api.github.com/users/Xudong-Huang/events{/privacy}","received_events_url":"https://api.github.com/users/Xudong-Huang/received_events","type":"User","site_admin":false},"created_at":"2017-12-26T13:31:01Z","updated_at":"2017-12-26T13:31:01Z","author_association":"OWNER","body":"thanks all, I'm considering change the api spec to add `unsafe` declaration for spawning a coroutine, since it's in an early stage, it's a good time to do that.  Apply the rust unsafe rules is important for users."}}
{"actor":{"display_login":"Xudong-Huang"}}