SuneF wrote:Also I need to figure out a way to poll for input. It's not obvious how to do that since a Node is not allowed to call back up the hierarchy into Protocol stuff. I don't see any other solution than to go threaded.
In my single-threaded, single-CPU approach I do this with an "event handler interface" class which has nothing but one (pure) virtual method "handleEvents()", is implemented by the class that deals with the protocol (in my case "WBEngine"), and is used both by the WBEngine and by the Searcher object. "handleEvents()" reads and processes one command from stdin. The name may be misleading, the name "handleEvent" would be better. When the Searcher decides that it is now time to poll for input it checks "isInputAvailable()" and if this returns true it calls "pEventHandler->handleEvents()" where pEventHandler is a pointer which has been passed to the constructor of the Searcher who stored it.
The virtual function call overhead is negligible in this case since "handleEvents()" is in fact called very rarely (at most once every N nodes, e.g. N=4096, but only if there is really input available). It is in fact the only case of using a virtual function in the release version of my engine, and it does absolutely no harm but helps to keep the design clean.
(The DEBUG version has another one with a similar idea, "onAssert()", which is used to dump the whole state of the engine top-down before terminating if an ASSERT() fires; the constructor of the topmost class registers 'this' as an "AssertionFailureInterface" at a central place, which is nothing but a static pointer variable over which the "onAssert()" is called by the assertionFailure() function - note: DEBUG version only!)
A similar design is possible for a multi-CPU engine. Since most probably you do not want each single search thread to perform polling for input in parallel, you can assign this job to exactly one of these threads somehow, and do as described above. In many cases the commands coming in during search will cause the search to stop anyway, which is kind of a "global" action and does not require some decentralized approach connected with many sorts of synchronization problems.
With this approach I avoid "illegal" interface access from low level to high level parts, which is crucial for a good software design.
What do you think about it?
Best regards,
Sven