I just checked in a first attempt at an interface for DNS Service Discovery in Pike 7.7. This lets Pike programs register services so that Rendezvous/Zeroconf-aware applications on the same network can see them. For example, I plan on using it to announce web servers and have them show up as Safari bookmarks automatically.
The current implementation is designed to work with Mac OS X 10.3 or later as well as Linux (using libhowl). However, I don't have a Linux machine with the developer package of libhowl installed so I'm sure some tweaks will be needed for it to compile correctly.
A natural step forward would be to design a Protocols.DNS_SD.Browser class next. This can be a bit trickier depending on what type of API the high-level code should have (e.g. async callbacks or just a snapshot of the currently available services). If anyone feels like helping out it would be great!
(BTW, thanks to Grubba for adding the missing testsuite.in to fix the yellow blupps in Xenofarm.)
A natural step forward would be to design a Protocols.DNS_SD.Browser class next. This can be a bit trickier depending on what type of API the high-level code should have (e.g. async callbacks or just a snapshot of the currently available services).
Are you forced to pick a favourite there? Both ways sound useful, for different problem domains.
Indeed, both are useful and can be implemented independently.
The system/library APIs I'm calling into are all async so I suppose that approach is the easiest. However, that places the burden on the Pike application to wait long enough (maybe a second or two) to find services.
Another thing is unclear to me: the library functions take a void *context value and I'd stuff the browser object in there. But how can I be sure the context pointer I get handed in the callback is the same object I had initially (or even a valid object)? In this case I believe I'm able to cancel an ongoing lookup task from the object's exit callback so I'm probably safe anyway.
Just keep a ref to it. Objects remain as long as they have references, even when they've been destructed.
I thought of that too, but will the object ever be destructed then? I want the lookup task to be running for the lifetime of the browser object, but since I'm holding the last reference myself it seems it will remain valid forever. In other words, instead of performing the cleanup in the object's exit callback I need a notification when the refcount reaches 1, right?
That situation is very similar to how the backend treats its files. The design there is that when a file has registered that it wants events, the backend holds a counted ref to it so no other refs are necessary. I think that's reasonable since the file still is reachable - when the event comes it might very well run code that adds other refs to it again. When the file no longer is registered for any events, it effectively loses the ref from the backend and can disappear.
I suggest you use the same approach. If your events are single-shot, the callback you have would "deregister" its own object and thereby free a ref. If there are no other refs to it then, it'll get destructed at that point. Your exit callback should still "deregister" (and remove the extra ref) if the object is registered, to make it work if it's destructed explicitly.
This will make the async interface behave consistently with the file callbacks, which is a good thing imho.
Too bad the callback isn't single-shot... This is how I envision the Pike API to work:
{ object b = Browse("_http._tcp"); res1 = b->get_results(); // get all results so far res2 = b->get_results(); // get all results so far b->stop(); // terminate async lookup res3 = b->get_results(); // all results at the time of stop() }
Now, if I don't call stop() the results array should keep updating for as long as b is valid. Handling destruct(b) should be easy, but the problem is if b goes out of scope and loses a ref; since we've added a ref internally we won't detect that case and the lookup will continue forever.
I'm thinking I'm better off not adding the ref to begin with. Then my destroy() method can do whatever stop() does to cancel the lookup, and I'll pray that the callback isn't invoked with an old object any more (which shouldn't happen unless there are bugs in the library).
In an async interface that's exactly what one would expect. Having to keep an extra ref would be inconsistent.
You can still get the behaviour you want in your synchronous Browse class by making it a wrapper with a one way ref to a separate object that contains the callback. It'd work just like the destruct keys that wraps the cyclic node trees in the xslt engine, but foolproof since the callback object doesn't need to be externally accessible at all.
pike-devel@lists.lysator.liu.se