{"action":"created","issue":{"url":"https://api.github.com/repos/ziglang/zig/issues/1363","repository_url":"https://api.github.com/repos/ziglang/zig","labels_url":"https://api.github.com/repos/ziglang/zig/issues/1363/labels{/name}","comments_url":"https://api.github.com/repos/ziglang/zig/issues/1363/comments","events_url":"https://api.github.com/repos/ziglang/zig/issues/1363/events","html_url":"https://github.com/ziglang/zig/issues/1363","id":349647614,"node_id":"MDU6SXNzdWUzNDk2NDc2MTQ=","number":1363,"title":"coroutine semantics are unsound. proposal to fix them","user":{"login":"andrewrk","id":106511,"node_id":"MDQ6VXNlcjEwNjUxMQ==","avatar_url":"https://avatars0.githubusercontent.com/u/106511?v=4","gravatar_id":"","url":"https://api.github.com/users/andrewrk","html_url":"https://github.com/andrewrk","followers_url":"https://api.github.com/users/andrewrk/followers","following_url":"https://api.github.com/users/andrewrk/following{/other_user}","gists_url":"https://api.github.com/users/andrewrk/gists{/gist_id}","starred_url":"https://api.github.com/users/andrewrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andrewrk/subscriptions","organizations_url":"https://api.github.com/users/andrewrk/orgs","repos_url":"https://api.github.com/users/andrewrk/repos","events_url":"https://api.github.com/users/andrewrk/events{/privacy}","received_events_url":"https://api.github.com/users/andrewrk/received_events","type":"User","site_admin":false},"labels":[{"id":693801403,"node_id":"MDU6TGFiZWw2OTM4MDE0MDM=","url":"https://api.github.com/repos/ziglang/zig/labels/accepted","name":"accepted","color":"0efa16","default":false},{"id":596460696,"node_id":"MDU6TGFiZWw1OTY0NjA2OTY=","url":"https://api.github.com/repos/ziglang/zig/labels/breaking","name":"breaking","color":"fbca04","default":false},{"id":244683519,"node_id":"MDU6TGFiZWwyNDQ2ODM1MTk=","url":"https://api.github.com/repos/ziglang/zig/labels/bug","name":"bug","color":"fc2929","default":true},{"id":693800736,"node_id":"MDU6TGFiZWw2OTM4MDA3MzY=","url":"https://api.github.com/repos/ziglang/zig/labels/proposal","name":"proposal","color":"aaefdc","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/ziglang/zig/milestones/4","html_url":"https://github.com/ziglang/zig/milestone/4","labels_url":"https://api.github.com/repos/ziglang/zig/milestones/4/labels","id":2505124,"node_id":"MDk6TWlsZXN0b25lMjUwNTEyNA==","number":4,"title":"0.3.0","description":"Accept and resolve these issues, reject, or postpone them before releasing 0.3.0.","creator":{"login":"andrewrk","id":106511,"node_id":"MDQ6VXNlcjEwNjUxMQ==","avatar_url":"https://avatars0.githubusercontent.com/u/106511?v=4","gravatar_id":"","url":"https://api.github.com/users/andrewrk","html_url":"https://github.com/andrewrk","followers_url":"https://api.github.com/users/andrewrk/followers","following_url":"https://api.github.com/users/andrewrk/following{/other_user}","gists_url":"https://api.github.com/users/andrewrk/gists{/gist_id}","starred_url":"https://api.github.com/users/andrewrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andrewrk/subscriptions","organizations_url":"https://api.github.com/users/andrewrk/orgs","repos_url":"https://api.github.com/users/andrewrk/repos","events_url":"https://api.github.com/users/andrewrk/events{/privacy}","received_events_url":"https://api.github.com/users/andrewrk/received_events","type":"User","site_admin":false},"open_issues":82,"closed_issues":152,"state":"open","created_at":"2017-05-07T16:17:21Z","updated_at":"2018-09-01T14:43:51Z","due_on":"2018-09-12T07:00:00Z","closed_at":null},"comments":5,"created_at":"2018-08-10T20:28:57Z","updated_at":"2018-09-02T17:30:35Z","closed_at":null,"author_association":"MEMBER","body":"Currently there is a race condition when using multithreaded coroutines. Consider the following scenario:\r\n\r\n1. Thread 1 does `epoll_wait`/`kevent`/`GetQueuedCompletionStatus`, blocking until some work can be done. The thread wakes up, and it is about to execute `resume coroutine_handle`.\r\n2. Thread 2 is executing the coroutine, which reaches a suspend point, and then reads its atomic state and learns that it is scheduled to be canceled (destroyed). It runs the defers and errdefers, which dutifully remove the coroutine from the epoll set. However because of (1) it isn't even in the epoll set/kqueue/iocp. (1) is about to `resume` the coroutine and there's nothing (2) can do to stop it. (2) proceeds to destroy the coroutine frame. The memory is gone.\r\n3. Thread 1 does `resume coroutine_handle` which now points to invalid memory. Boom.\r\n\r\nIn order to fix this, we need to introduce new syntax and semantics. Current semantics are:\r\n\r\n * `async` creates a `promise` which must be consumed with  `cancel` or `await`.\r\n * `suspend` must be consumed with a `resume`.\r\n\r\nThe problem here described above - `resume` and `cancel` racing with each other. When a suspended coroutine is canceled, the memory must not be destroyed until the `suspend` is canceled or resumed. Proposal for new semantics:\r\n\r\n * `async` creates a `promise` which must be consumed with  `cancelasync` or `await`.\r\n * `suspend` must be consumed with a `cancelsuspend` or `resume`.\r\n\r\nWith these semantics, each coroutine essentially has 0, 1, or 2 references, and when the reference count reaches 0 it is destroyed. There is the \"async reference\", which is the main one, and the \"suspend reference\", which might never exist.\r\n\r\nTherefore it is crucial that when a coroutine uses `suspend` - which is a low level feature intended mainly for low level library implementations of concurrency primitives - that it ensures it will be consumed with either `resume` or `cancelsuspend`.\r\n\r\n`defer` and `errdefer` will both run when a coroutine's cancellation process begins. This means that the first `cancelasync` or `cancelsuspend` will run the defers of the coroutine. When the other reference drops, the memory will be destroyed."},"comment":{"url":"https://api.github.com/repos/ziglang/zig/issues/comments/417946316","html_url":"https://github.com/ziglang/zig/issues/1363#issuecomment-417946316","issue_url":"https://api.github.com/repos/ziglang/zig/issues/1363","id":417946316,"node_id":"MDEyOklzc3VlQ29tbWVudDQxNzk0NjMxNg==","user":{"login":"shawnl","id":510834,"node_id":"MDQ6VXNlcjUxMDgzNA==","avatar_url":"https://avatars1.githubusercontent.com/u/510834?v=4","gravatar_id":"","url":"https://api.github.com/users/shawnl","html_url":"https://github.com/shawnl","followers_url":"https://api.github.com/users/shawnl/followers","following_url":"https://api.github.com/users/shawnl/following{/other_user}","gists_url":"https://api.github.com/users/shawnl/gists{/gist_id}","starred_url":"https://api.github.com/users/shawnl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shawnl/subscriptions","organizations_url":"https://api.github.com/users/shawnl/orgs","repos_url":"https://api.github.com/users/shawnl/repos","events_url":"https://api.github.com/users/shawnl/events{/privacy}","received_events_url":"https://api.github.com/users/shawnl/received_events","type":"User","site_admin":false},"created_at":"2018-09-02T17:30:35Z","updated_at":"2018-09-02T17:30:35Z","author_association":"CONTRIBUTOR","body":"> The epoll loop will always call resume on its promises,\r\n\r\nNo it doesn't. If no events come it they never get resumed. And changing the loop to guarantee this is the same logic as knowing when to call `destroy`. The `cancel` part can be taken out of the promise type and become part of the loop: `.scheduleCancellation()`."}}
{"actor":{"display_login":"shawnl"}}