{"action":"created","issue":{"url":"https://api.github.com/repos/ocaml/ocaml/issues/2112","repository_url":"https://api.github.com/repos/ocaml/ocaml","labels_url":"https://api.github.com/repos/ocaml/ocaml/issues/2112/labels{/name}","comments_url":"https://api.github.com/repos/ocaml/ocaml/issues/2112/comments","events_url":"https://api.github.com/repos/ocaml/ocaml/issues/2112/events","html_url":"https://github.com/ocaml/ocaml/pull/2112","id":371995057,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI0MzE2NTYz","number":2112,"title":"Thread.yield fairness with multiple busy yielding threads","user":{"login":"ahh","id":247726,"node_id":"MDQ6VXNlcjI0NzcyNg==","avatar_url":"https://avatars2.githubusercontent.com/u/247726?v=4","gravatar_id":"","url":"https://api.github.com/users/ahh","html_url":"https://github.com/ahh","followers_url":"https://api.github.com/users/ahh/followers","following_url":"https://api.github.com/users/ahh/following{/other_user}","gists_url":"https://api.github.com/users/ahh/gists{/gist_id}","starred_url":"https://api.github.com/users/ahh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ahh/subscriptions","organizations_url":"https://api.github.com/users/ahh/orgs","repos_url":"https://api.github.com/users/ahh/repos","events_url":"https://api.github.com/users/ahh/events{/privacy}","received_events_url":"https://api.github.com/users/ahh/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-10-19T14:58:13Z","updated_at":"2018-10-19T15:06:35Z","closed_at":null,"author_association":"NONE","pull_request":{"url":"https://api.github.com/repos/ocaml/ocaml/pulls/2112","html_url":"https://github.com/ocaml/ocaml/pull/2112","diff_url":"https://github.com/ocaml/ocaml/pull/2112.diff","patch_url":"https://github.com/ocaml/ocaml/pull/2112.patch"},"body":"Here's a very hacky repro for the problem: the below code counts how many times a thread goes through a loop yielding before successfully *doing* so.  At trunk, this runs 10,000+ times per thread, or more if the threads are pinned to the same CPU.  With this patch, we reliably yield every time, because a yielding thread is properly marked as a waiter.\r\n```\r\nlet threads = 4\r\nlet print = Sys.argv.(1) |> bool_of_string\r\nlet iters = Sys.argv.(2) |> int_of_string\r\n\r\nlet yields = ref 0\r\nlet are_ready = ref 0\r\nlet are_done = ref 0\r\n\r\nlet last = ref '!'\r\n\r\nlet start = ref 0.0\r\n\r\nlet stop = ref 0.0\r\n\r\nlet run_lengths = ref []\r\n\r\nlet report label cnt =\r\n  let old = !run_lengths in\r\n  run_lengths := (label, cnt) :: old;;\r\n\r\nlet threads =\r\n  List.init threads (Thread.create (fun i ->\r\n    (* Label our threads A..Z (clearer than TIDs, and integer indices are confusable\r\n       with run lengths) *)\r\n    let label = Char.unsafe_chr (Char.code 'A' + i) in\r\n    incr are_ready;\r\n    (* Don't make any progress until all threads are spawned and properly contending for\r\n       the Ocaml lock. *)\r\n    if !are_ready = threads then start := (Unix.gettimeofday ());\r\n    while !are_ready < threads do\r\n      Thread.yield ()\r\n    done;\r\n    let consecutive = ref 0 in\r\n    while !yields < iters do\r\n      incr yields;\r\n      last := label;\r\n      Thread.yield ();\r\n      incr consecutive;\r\n      if not (!last = label)\r\n      then (\r\n        report label !consecutive;\r\n        consecutive := 0)\r\n    done;\r\n    if !consecutive > 0 then report label !consecutive;\r\n    incr are_done;\r\n    if !are_done = threads then stop := (Unix.gettimeofday ())\r\n  ));;\r\n\r\n\r\nList.iter Thread.join threads;;\r\n\r\nlet report (label, count) = Printf.printf \"%c ran for %d\\n\" label count;;\r\n\r\nif print then List.iter report (List.rev !run_lengths)\r\n\r\nlet time = !stop -. !start;;\r\nlet switches = List.length !run_lengths;;\r\nPrintf.printf \"Executed in %fs with %d switches\\n\" time switches\r\n\r\n```\r\n\r\nI can measure no overhead in throughput from the normal volume of yielding due to the tick thread (say, with two threads merrily allocating) but I'm happy to run more tests.\r\n\r\nThanks!"},"comment":{"url":"https://api.github.com/repos/ocaml/ocaml/issues/comments/431394963","html_url":"https://github.com/ocaml/ocaml/pull/2112#issuecomment-431394963","issue_url":"https://api.github.com/repos/ocaml/ocaml/issues/2112","id":431394963,"node_id":"MDEyOklzc3VlQ29tbWVudDQzMTM5NDk2Mw==","user":{"login":"xavierleroy","id":3845810,"node_id":"MDQ6VXNlcjM4NDU4MTA=","avatar_url":"https://avatars0.githubusercontent.com/u/3845810?v=4","gravatar_id":"","url":"https://api.github.com/users/xavierleroy","html_url":"https://github.com/xavierleroy","followers_url":"https://api.github.com/users/xavierleroy/followers","following_url":"https://api.github.com/users/xavierleroy/following{/other_user}","gists_url":"https://api.github.com/users/xavierleroy/gists{/gist_id}","starred_url":"https://api.github.com/users/xavierleroy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xavierleroy/subscriptions","organizations_url":"https://api.github.com/users/xavierleroy/orgs","repos_url":"https://api.github.com/users/xavierleroy/repos","events_url":"https://api.github.com/users/xavierleroy/events{/privacy}","received_events_url":"https://api.github.com/users/xavierleroy/received_events","type":"User","site_admin":false},"created_at":"2018-10-19T15:06:35Z","updated_at":"2018-10-19T15:06:35Z","author_association":"CONTRIBUTOR","body":"Interesting proposal, thanks!  @jhjourdan, would you like to look at it?\r\n"}}
{"actor":{"display_login":"xavierleroy"}}