{"action":"created","issue":{"url":"https://api.github.com/repos/supercollider/supercollider/issues/3299","repository_url":"https://api.github.com/repos/supercollider/supercollider","labels_url":"https://api.github.com/repos/supercollider/supercollider/issues/3299/labels{/name}","comments_url":"https://api.github.com/repos/supercollider/supercollider/issues/3299/comments","events_url":"https://api.github.com/repos/supercollider/supercollider/issues/3299/events","html_url":"https://github.com/supercollider/supercollider/pull/3299","id":274849863,"number":3299,"title":"Server single boot task","user":{"login":"adcxyz","id":3518579,"avatar_url":"https://avatars2.githubusercontent.com/u/3518579?v=4","gravatar_id":"","url":"https://api.github.com/users/adcxyz","html_url":"https://github.com/adcxyz","followers_url":"https://api.github.com/users/adcxyz/followers","following_url":"https://api.github.com/users/adcxyz/following{/other_user}","gists_url":"https://api.github.com/users/adcxyz/gists{/gist_id}","starred_url":"https://api.github.com/users/adcxyz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adcxyz/subscriptions","organizations_url":"https://api.github.com/users/adcxyz/orgs","repos_url":"https://api.github.com/users/adcxyz/repos","events_url":"https://api.github.com/users/adcxyz/events{/privacy}","received_events_url":"https://api.github.com/users/adcxyz/received_events","type":"User","site_admin":false},"labels":[{"id":4477093,"url":"https://api.github.com/repos/supercollider/supercollider/labels/comp:%20scsynth","name":"comp: scsynth","color":"F4FF75","default":false},{"id":348656500,"url":"https://api.github.com/repos/supercollider/supercollider/labels/Work%20In%20Progress%20-%20don't%20merge%20me%20yet","name":"Work In Progress - don't merge me yet","color":"5319e7","default":false}],"state":"open","locked":false,"assignee":{"login":"adcxyz","id":3518579,"avatar_url":"https://avatars2.githubusercontent.com/u/3518579?v=4","gravatar_id":"","url":"https://api.github.com/users/adcxyz","html_url":"https://github.com/adcxyz","followers_url":"https://api.github.com/users/adcxyz/followers","following_url":"https://api.github.com/users/adcxyz/following{/other_user}","gists_url":"https://api.github.com/users/adcxyz/gists{/gist_id}","starred_url":"https://api.github.com/users/adcxyz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adcxyz/subscriptions","organizations_url":"https://api.github.com/users/adcxyz/orgs","repos_url":"https://api.github.com/users/adcxyz/repos","events_url":"https://api.github.com/users/adcxyz/events{/privacy}","received_events_url":"https://api.github.com/users/adcxyz/received_events","type":"User","site_admin":false},"assignees":[{"login":"adcxyz","id":3518579,"avatar_url":"https://avatars2.githubusercontent.com/u/3518579?v=4","gravatar_id":"","url":"https://api.github.com/users/adcxyz","html_url":"https://github.com/adcxyz","followers_url":"https://api.github.com/users/adcxyz/followers","following_url":"https://api.github.com/users/adcxyz/following{/other_user}","gists_url":"https://api.github.com/users/adcxyz/gists{/gist_id}","starred_url":"https://api.github.com/users/adcxyz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adcxyz/subscriptions","organizations_url":"https://api.github.com/users/adcxyz/orgs","repos_url":"https://api.github.com/users/adcxyz/repos","events_url":"https://api.github.com/users/adcxyz/events{/privacy}","received_events_url":"https://api.github.com/users/adcxyz/received_events","type":"User","site_admin":false}],"milestone":null,"comments":1,"created_at":"2017-11-17T12:28:21Z","updated_at":"2017-11-17T18:54:22Z","closed_at":null,"author_association":"CONTRIBUTOR","pull_request":{"url":"https://api.github.com/repos/supercollider/supercollider/pulls/3299","html_url":"https://github.com/supercollider/supercollider/pull/3299","diff_url":"https://github.com/supercollider/supercollider/pull/3299.diff","patch_url":"https://github.com/supercollider/supercollider/pull/3299.patch"},"body":"This PR is based on the lockClientID PR #3275, and implements a simple deterministic order for all Server-boot-related items, as proposed in  #3286.\r\n Compare what is new relative to #3275:\r\nhttps://github.com/adcxyz/supercollider/compare/lockClientIDWhileRunning...adcxyz:server_singleBootTask?expand=1\r\n\r\n(Partly copied from my post in #3286, rework server boot logic):\r\nThe order proposed here follows james' sketch, and all actions run in a single Task.\r\nThis makes the boot process deterministic and easy to reason about.\r\n\r\nThe PR is relative to 3.9 because this is what I based it on - \r\nit need not go into 3.9 if devs prefer to release it as is + PR #3275. \r\nIMO, it does solidify the boot process, and makes a step towards #3286, \r\nwhich could be desirable and useful to have quickly. \r\n\r\nThe order proposed and implemented here is:\r\n* boot the server process -> server.hasBooted is true\r\n* notify, get clientID, make allocators -> serverRunning is true\r\n* do ServerBoot.run: everything that can or must happen before server tree:\r\n   SynthDescLib, NodeWatcher, etc.\r\n sync \r\n* do server.initTree:\r\n  create defaultGroups\r\n  all user setup actions that needs server tree go into:\r\n  s.tree \r\n  ServerTree.run \r\n  sync when done\r\n* do all tempBootItems \r\n  user actions added by doWhenBooted, in the order they were added in.\r\n\r\nAll of them allow waiting and conditions within the functions.\r\n\r\n```\r\n// Here is a variant of test_bootSequence that works as a test on 3.8.0: \r\nfork {\r\n\tvar a, cond, b1, b2, t1, t2, oldTFunc;\r\n\tvar numBootFuncs = ServerBoot.objects !? { ServerBoot.objects[s].size } ? 0;\r\n\tvar numTreeFuncs = ServerTree.objects !? { ServerTree.objects[s].size } ? 0;\r\n\tvar expectedList = List[ '1_Bt', '2_Bt', '3_tr', '4_Tr', '5_Tr', '6_wt', '7_do' ];\r\n\t\t\r\n\t// list of function names to check\r\n\ta = List[];\r\n\t// add functions to run at all boot stages\r\n\tServerBoot.add (b1 = { |sv1| (sv1.name + \": \").post; a.add('1_Bt'.postln); }, s);\r\n\tServerBoot.add (b2 = { |sv2| (sv2.name + \": \").post; a.add('2_Bt'.postln); }, s);\r\n\ts.tree =        { (s.name + \": \").post; 0.1.wait; a.add('3_tr'.postln); };\r\n\tServerTree.add (t1 = { |sv| (sv.name + \": \").post; 0.1.wait; a.add('4_Tr'.postln); }, s);\r\n\tServerTree.add (t2 = { |sv| (sv.name + \": \").post; 0.1.wait; a.add('5_Tr'.postln); }, s);\r\n\t\t\r\n\ts.quit;\r\n\ta = List[];\r\n\ts.waitForBoot  { ().play; s.post; a.add('6_wt'.postln); };\r\n\ts.doWhenBooted { 0.1.wait; s.post; a.add('7_do'.postln); };\r\n\ts.doWhenBooted { 2.wait; cond.unhang; };\r\n\ts.boot;\r\n\t\r\n\tcond = Condition.new;\r\n\tcond.hang;\r\n\t\t\r\n\t// wait for slow late tasks, just in case\r\n\t5.wait;\r\n\t\r\n\ta.postln; \r\n\tif (a == expectedList) { \r\n\t\t\"*** YES, BOOT ORDER IS CORRECT. *** \".postln;\r\n\t} { \r\n\t\t\"*** NO, BOOT ORDER IS not correct. *** \".postln;\r\n\t};\r\n\t\r\n\t// cleanup\r\n\tServerBoot.remove(b1, s);\r\n\tServerBoot.remove(b2, s);\r\n\tServerTree.remove(t1, s);\r\n\tServerTree.remove(t2, s);\r\n\ts.quit;\r\n}\r\n```"},"comment":{"url":"https://api.github.com/repos/supercollider/supercollider/issues/comments/345332164","html_url":"https://github.com/supercollider/supercollider/pull/3299#issuecomment-345332164","issue_url":"https://api.github.com/repos/supercollider/supercollider/issues/3299","id":345332164,"user":{"login":"adcxyz","id":3518579,"avatar_url":"https://avatars2.githubusercontent.com/u/3518579?v=4","gravatar_id":"","url":"https://api.github.com/users/adcxyz","html_url":"https://github.com/adcxyz","followers_url":"https://api.github.com/users/adcxyz/followers","following_url":"https://api.github.com/users/adcxyz/following{/other_user}","gists_url":"https://api.github.com/users/adcxyz/gists{/gist_id}","starred_url":"https://api.github.com/users/adcxyz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adcxyz/subscriptions","organizations_url":"https://api.github.com/users/adcxyz/orgs","repos_url":"https://api.github.com/users/adcxyz/repos","events_url":"https://api.github.com/users/adcxyz/events{/privacy}","received_events_url":"https://api.github.com/users/adcxyz/received_events","type":"User","site_admin":false},"created_at":"2017-11-17T18:54:22Z","updated_at":"2017-11-17T18:54:22Z","author_association":"CONTRIBUTOR","body":"> At first glance -- I'm proposing one and only one server init thread. In this PR, doWhenBooted still forks a new Routine, every time it's invoked -- here: https://github.com/supercollider/supercollider/pull/3299/files#diff-de780da009f27cd5c06ba08d7d17e409R82. That is, you'll have the server init thread plus one thread for every time the user called doWhenBooted or waitForBoot -- which is different from \"one and only one.\"\r\n```\r\nahm, no. This is ServerStatusWatcher:doWhenBooted, which is unused - I left it in for the unlikely case that anyone uses that in user code somewhere - I had it redirected to Server:doWhenBooted at some point, thought it still did that. will do, it is more consistent.\r\n\r\nServer:doWhenBooted does what I think is the right thing:\r\n```\r\n\tdoWhenBooted { |onComplete, limit=100, onFailure|\r\n\t\t// what to do with onFailure functions?\r\n\t\t// put in onFailureFuncs list and evaluate after a timeout?\r\n\t\tif (Server.postingBootInfo) {\r\n\t\t\t\"% .% - onComplete: %\\n\"\r\n\t\t\t.postf(this, thisMethod.name, onComplete.cs);\r\n\t\t};\r\n\t\tif (this.serverRunning) {\r\n\t\t\tif (Server.postingBootInfo) { \"running onComplete directly.\".postln };\r\n\t\t\tonComplete.value\r\n\t\t} {\r\n\t\t\tthis.addBootItem(onComplete);\r\n\t\t}\r\n\t}\r\n// and then the main action is here:\r\n\tprRunBootTask {\r\n\t\tif (Server.postingBootInfo) { \"%.%\\n\".postf(this, thisMethod.name) };\r\n\t\tTask {\r\n\t\t\tif (Server.postingBootInfo) { \"prRun: % - %\\n\".postf(this, \"ServerBoot.run\") };\r\n\t\t\tServerBoot.run(this);\r\n\t\t\t0.2.wait;\r\n\t\t\tthis.sync;\r\n\t\t\tif (Server.postingBootInfo) { \"prRun: % .%\\n\".postf(this, \"initTree\") };\r\n\t\t\tthis.initTree;\r\n\t\t\t0.2.wait;\r\n\t\t\tthis.sync;\r\n\t\t\tif (Server.postingBootInfo) { \"prRun: % .%\\n\".postf(this, \"tempBootItems.do\") };\r\n\t\t\ttempBootItems.do(_.value);\r\n\t\t\ttempBootItems.clear;\r\n\t\t\t0.2.wait;\r\n\t\t\tthis.sync;\r\n\t\t}.play(AppClock);\r\n\t}\r\n```\r\n\r\nHere is a test that fails in 3.8.0 50% of the time, and works fine in this PR:\r\n```\r\nfork { \r\n\ts.quit;\r\n\ta = b = nil;\r\n\ts.doWhenBooted { \\A.postln; a = (5..13); };\r\n\t// do something that takes about 0.1 sec, \r\n\t// or just do 0.1.wait; \r\n\tbench { 1000000.do { exprand(1, 100).sqrt } };\r\n\t// -> in 3.8, 50% chance that B happens before A:\r\n\ts.doWhenBooted { \\B.postln; b = a.scramble.keep(5) };\r\n\ts.waitForBoot { \r\n\t\t// wait till both have finished;\r\n\t\t0.5.wait;\r\n\t\t[a, b].postln;\r\n\t};\r\n}\r\n```\r\nSo I think it does what I promised already. Looking forward to your full review! \r\nbest adc"}}
{"actor":{"display_login":"adcxyz"}}