Design advice sought: Backend implementation

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Design advice sought: Backend implementation

Bill Welliver-2
I'd like to solicit some design feedback for a new feature that I'd like
to contribute. I apologize for the length of this message, but hopefully
it contains all of the details needed to get the conversation started.

First, some quick background:

Over the past few years I've written a few modules[1] that provide access
to a number of technologies that are either part of Darwin or one of
Apple's Darwin-based OSes (OSX, iOS, etc). A common feature that keeps
occurring is their reliance on an active run loop. Normally, this is
either NSRunloop or CFRunLoop (they are distinct APIs, but they share
a common set of event sources.) In the past, I've resorted to adding a
persistent call-out to the default back end, which is similar to the
approach taken by the GTK2 module. This is normally good enough for
sitatuions where events are relatively infrequent, such as GUI-input work.

However, it's not always suitable for higher interrupt volumes and it also
has the side effect of presenting a continual, though low, load on the
CPU. This isn't a huge problem, but it's unclean and if one were to
increase the frequency of the poll-out to the CF/NS run loop, the load
becomes higher, and thus harder to ignore.

A Solution:

After doing some research on better ways to approach the problem, I
discovered that CF/NS run loops use kqueue at their core. As a result, a
kqueue can be added to the run loop and polled for events. That's
convenient, because pike uses kqueue on Darwin in its default backend,
Pike.PollDeviceBackend. Swapping out the direct call to kevent() with one
that calls the run loop results in "native" Pike I/O working the way it
should, plus Darwin layered functionality working properly as well (and
low CPU utilization, too).

I think that this is a useful new feature to add to Pike, but I've got
some concerns about how best to include it. Right now, I've created a
branch called hww3/cfrunloop_backend[2] that includes the functionality
and swaps out the existing implementation under Darwin. That is, of
course, the simplest approach, but I think is probably not the best idea:

Pros:

- Uses kqueue() at its core, so the existing backend will fire events in
the same way.

- Allows a number of technologies to function normally when used from with
Pike processes, without the use of hacks.

Cons:

- There is exactly one Run Loop per thread, which, if used universally,
could present issues because this means behavior would differ from other
Backends.

- There is slightly more overhead per trip through the backend due to
events being single-shot. This means that the kqueue has to be re-added to
the runloop each loop. I don't think it's a major overhead, it still
exists.

Alternate Implementations:

A second approach would be to provide an additional backend
implementation, say Pike.CFBackend, that's available on Darwin based
systems. I see one or two problems with this, though:

- There would need to be a way to activiate this selectively at runtime,
something like set_default_backend(Pike.CFBackend()), which probably
doesn't exist right now, right?

- Because 90% of the implementation would be shared with
PollDeviceBackend, I think it would involve a fair amount of code
duplication inside of backend.cmod, which I think would make maintenance
more difficult.

A third (and right now, I believe the best) approach would be to add one
or two methods to Pike.Backend that could be implemented by
PollDeviceBackend on Darwin and be used to activate this alternate mode.

For example:

    int(0..1) has_client_mode()

which would return true if there were a client optimization (I suggest
client because CFRunLoop would be more useful for client apps than
traditional server based apps, though that's certainly not an either/or
situaiton) and:

   void enable_client_mode(int(0..1))

that would trigger calls to CFRunLoopRunInMode() instead of kevent()
directly.

Input Sought:

I think either of the two alternative approaches would be just fine,
assuming that the concerns could be addressed. I'm just not sure which
would be best from a design and implementation standpoint (I sort of
think the last option would be simplest for _me_ to get working, but if
someone has desire to help tackle it, I'd be game to play along).

The upshot of allowing the proposed backend changes for other folks using
Pike are that:

a) SDL and GL becomes usable out of the box on OSX
b) I am prepared to contribute a few pieces of code, namely:
   - FSEvents module
   - Updated Filesystem.Monitor that uses FSEvents on Darwin and inotify on
       Linux to eliminate polling
   - DNS_SD querying support on OSX
   - ObjectiveC bridge (possibly)

Hopefully we can have a bit of discussion about the options and approaches
and come up with a solution that works without being terribly
inconvenient. Please do drop me a note if anything about this proposal is
unclear.

Best,

Bill

[1] Public.ObjectiveC, Public.System.FSEvents, Public.IO.IOWarror,
additions to DNS-SD; SDL and GL on OSX also share this requirement.
[2] http://hg.welliver.org/pike/changesets, not yet pushed to the pike
master repo.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Design advice sought: Backend implementation

Marcus Comstedt (ACROSS) (Hail Ilpalazzo!) @ Pike (-) developers forum
I like anything that will allow us to use SDL and GL on OSX.. :)

After reading your proposal, my initial gut feeling is that the second
or third option (ie new backend for OSX or extending the existing one)
would be the best ones to explore.

I'm sure Grubba will have some thoughts on the subject as well...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Design advice sought: Backend implementation

Marcus Comstedt (ACROSS) (Hail Ilpalazzo!) @ Pike (-) developers forum
In reply to this post by Bill Welliver-2
I'm not sufficiently versed in the Pike internals to comment on your
alternatives, but I too would welcome any CF/NS runloop integration
improvements.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Design advice sought: Backend implementation

Marcus Comstedt (ACROSS) (Hail Ilpalazzo!) @ Pike (-) developers forum
In reply to this post by Bill Welliver-2
> - There is exactly one Run Loop per thread, which, if used universally,
> could present issues because this means behavior would differ from other
> Backends.

What do you mean? It can't be necessary to listen to those events in
every thread, can it?

> A third (and right now, I believe the best) approach would be to add one
> or two methods to Pike.Backend that could be implemented by
> PollDeviceBackend on Darwin and be used to activate this alternate
> mode.

I agree. That way it's possible to choose which backend instance to
use for those events, as well. I suggest you use function names that
make it really obvious that this for platform specific events. E.g:

   void enable_darwin_events (int(0..1) enable);
   int(0..1) query_darwin_events();

Then one could add a constant like Pike.BACKEND_GOT_DARWIN_EVENTS, to
be able to test compile time whether to compile in calls to those
functions (which of course wouldn't exist at all on other platforms).

I've got no idea if "darwin_events" is the technically most accurate
name, but you get the idea. By using such a clearly platform specific
name, it's easier to invent a more generic scheme later if more things
like this get added.

Then it's another matter if we somehow can make it easier to do the
reverse, i.e. to hook in the pike backend in a foreign one. That'd
mean a way to query for all the pike fd's, including the wakeup pipe,
I guess. Or if it's possible to solve a bit more elegantly when
something like kqueue is used.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

CoreFoundation in PollDeviceBackend, was Design advice sought: Backend implementation

Bill Welliver-2
I've checked in support for allowing CoreFoundation to manage IO in PollDeviceBackend, for systems so equipped (OSX, etc). I didn't add a query function, which was an oversight on my part.

>   void enable_core_foundation (int(0..1) enable);
>    constant GOT_CORE_FOUNDATION = 1;

This functionality probably won't bring any major new benefits yet, though it similarly shouldn't break things when enabled. Once I get it polished up a little more, I'll turn my attention to contributing some code that takes advantage of the new functionality.

As always, comments and suggestions are welcome.
Bill

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: Design advice sought: Backend implementation

Bill Welliver-2
In reply to this post by Marcus Comstedt (ACROSS) (Hail Ilpalazzo!) @ Pike (-) developers forum
I've been thinking about this as well. My scope hasn't been universal, as
for the moment my focus is on the built-on-darwin OSen.

In my case, I think it's pretty easy to allow the native run loop handle
fd events; it's just a matter of re-registering the kqueue with the
runloop after it triggers an event.

I'm less sure about the other aspects of the backend, such as timed
events. The run loop has a timer facility, which I think can be adapted to
handle call_outs in a pretty straightforward manner. What else do I need
to think about if a call to Pike.Backend->`()() is no longer being made?

Bill

>
> Then it's another matter if we somehow can make it easier to do the
> reverse, i.e. to hook in the pike backend in a foreign one. That'd
> mean a way to query for all the pike fd's, including the wakeup pipe,
> I guess. Or if it's possible to solve a bit more elegantly when
> something like kqueue is used.
>
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: Design advice sought: Backend implementation

Marcus Comstedt (ACROSS) (Hail Ilpalazzo!) @ Pike (-) developers forum
> I'm less sure about the other aspects of the backend, such as timed
> events. The run loop has a timer facility, which I think can be adapted to
> handle call_outs in a pretty straightforward manner. What else do I need
> to think about if a call to Pike.Backend->`()() is no longer being made?

Grubba is the one who knows this bit best, but afaik you're ok if you
make sure the wakeup pipe is included, and set a suitable timeout
event from the call out list (see low_backend_once_setup).
Loading...