gnustep-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: NSRunLoop Tidying


From: Richard Frith-Macdonald
Subject: Re: NSRunLoop Tidying
Date: Fri, 8 Oct 2010 17:14:03 +0100

On 8 Oct 2010, at 13:24, David Chisnall wrote:

> Hi Everyone,
> 
> One of the things that has been on my to-do list for a long time is tidying 
> up NSRunLoop.  The current implementation predates modern event delivery 
> mechanisms and tries to shoe-horn the UNIX approach everywhere.
> 
> Modern systems include a unified mechanism for event delivery, and a lot of 
> the hacks to work around the fact that older *NIX systems didn't have these 
> are complicating the code a lot on these platforms.
> 
> My main motivation for doing this is to support the KQueue APIs on *BSD.  
> This provides a single function for waiting on every kind of event.  Windows 
> has had something similar forever, Linux can do the same thing with timerfd 
> in recent versions, and Solaris has completion ports, which provide similar 
> functionality.
> 
> As I understand it, there are four kinds of things that a runloop has to 
> handle:
> 
> 1) Events on a file descriptor (read / write).
> 2) Timers expiring.
> 3) Timeout expiring (e.g. -runMode:beforeDate:)
> 4) Messages from threads.
> 
> Of these, (3) is trivial.  All wait mechanisms support this, so implementing 
> it is trivial with any underlying mechanism.  Similarly, (1) is the default 
> case for pretty much all of the event-waiting calls, so it's simple.
> 
> Currently, (2) is implemented in terms of (3).  This is quite untidy.  It 
> means that we need to maintain an ordered list of timers in the runloop code, 
> find the one closest to the present, and then use this as the timeout.

Actually the current implementation uses an unsorted list as depending on the 
ordering introduced a subtle bug ... a timer can be present in more than one 
run loop, and/or in the same loop in more than one mode.  If it fires in one 
loop and changes its next fire date (ie is a repeating timer), that 
automatically breaks the ordering for the other loops/modes that it exists in.  
Of course, what we really want is probably something more complex to keep track 
of all the loops/modes the timer is in, and tell them that they need to 
re-order their list of timers ... if we did that we could depend on the order 
in the list.
The API demands that we keep track of the timers some way (so we can return the 
correct value from the -limitDateForMode: method), but it doesn't have to be an 
ordered list if a better alternative is available (though I can't really think 
of one).

> 
>  This is required with traditional select() and poll(), but Win32 has 
> SetTimer, Linux has timerfd(), and *BSD has kevent(), all of which allow you 
> to schedule timer events and wait on them just like fd events.

I don't really see how that can help since the API explicitly separates timer 
firing (done in -limitDateForMode:) and I/O event handling ... so having timers 
and I/O events use the same mechanism in the operating system does not mean 
that they can be done at the same place in the code or with a single system 
call.  So using things like SetTimer() and timerfd() may just add complexity.

> (4) is trivial on Windows, via PostThreadMessage(), which allows you to 
> deliver a message to a specific thread.  Kqueue has a EVFILT_USER, which 
> allows you to deliver events to other threads, and Solaris 10 event ports 
> have something similar.
> 
> To properly support efficient native APIs, we should move the handling of all 
> of these into the per-platform code and remove anything platform-specific 
> from the general code in -base.  There are lots of random #ifdefs scattered 
> about the place currently.  
> 
> My overall plan is:
> 
> - Move -addTimer:forMode: into the platform-specific code.
> - Tidy up the GSRunLoopCtx stuff so it isn't quite so full of #ifdefs.
> - Make the NSObject methods in NSThread.m call (private) runloop methods that 
> delegate to the platform-specific code.
> - Remove classes like GSPerformHolder from the generic code.
> - Implement a kqueue back end for *BSD.
> - Make the win32 back end use SetTimer() and PostThreadMessage().
> 
> Comments / suggestions?

I agree with "Dr. H. Nikolaus Schaller" <address@hidden> ... that it would make 
most sense to start by implementing the CFRunLoop API, and then re-implement 
NSRunLoop on top of it (it's quite hard to do the other way round).

Also, you need to put together *LOTS* to testcases in the testsuite to ensure 
that the implementation behaves exactly like OSX does ... this is because 
NSRunLoop is a really key part of so much of the system and very subtle/minor 
changes in behavior can have really nasty effects on applications.  It's really 
not good enough to have an implementation which does what the documentation 
says, we have to mimic actual OSX behavior pretty closely.

My guess is that your idea of re-implementing timer handling in a platform 
specific way is actually a  recipe for a less maintainable and more complex 
codebase, since the timeout behavior is largely mandated by the API and I can't 
currently see how platform specific APIs will help... but maybe you have some 
ideas you didn't mention here.  In general it's a good idea (for 
maintainability) to keep platform specific code to a minimum, so for instance I 
wouldn't use PostThreadMessage()  as it's probably no more efficient than the 
current code using SetEvent(), and would demand changes to NSThread to hold 
additional thread data and a message queue.

On the other hand, the kqueue API is supposed to be significantly more 
efficient than poll/select ... so using that on BSD and the similar epoll API 
on Linux would be good.

So, what would be great is to:
1. Implement the CFRunLoop
2. Keep platform specific code to a minimum (though make use of it where it 
really helps performance) for maintainability
3. Have testcases to check that it behaves like OSX on all platforms
4. Finally, re-implement NSRunLoop on top of CFRunLoop




reply via email to

[Prev in Thread] Current Thread [Next in Thread]