mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 00:41:02 +00:00
Add new explanatory comment at beginning.
([RunLoop -init]): Initialize new ivars. ([RunLoop -currentMode]): New method. ([RunLoop -addFileDescriptor:object:forMode:]): Method replaces -addFileDescriptor:invocation:forMode:, and re-implemented. Not yet functional. ([RunLoop -removeFileDescriptor:forMode:]): Likewise. ([RunLoop -addPort:forMode:]): New method. ([RunLoop -removePort:forMode:]): New method. ([RunLoop -addTimer:forMode:]): Overhauled to do the right thing with the MODE argument. ([RunLoop -acceptInputForMode:beforeDate:]): Method overhauled to ask port objects for their FD's, create FD_2_OBJECT map table on the fly, and initialize FDS on the fly. Now properly removed invalidated ports from the MODE. ([RunLoop -runOnceBeforeDate:forMode:]): New method. ([RunLoop -runOnceBeforeDate:]): New method. ([RunLoop -runUntilDate:forMode:]): New method. ([RunLoop -runUntilDate:]): New method. ([RunLoop +runUntilDate:forMode:]): New method. ([RunLoop +runOnceBeforeDate:]): New method. ([RunLoop +runOnceBeforeDate:forMode:]): New method. ([RunLoop +currentMode]): New method. (RunLoopDefaultMode): New global variable. ([NSObject -performSelector:afterDelay:]): Not implemented. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1187 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
d885418358
commit
d1ae74f76b
1 changed files with 258 additions and 57 deletions
315
Source/RunLoop.m
315
Source/RunLoop.m
|
@ -21,6 +21,37 @@
|
|||
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* This is the beginning of a RunLoop implementation.
|
||||
It is still in the early stages of development, and will most likely
|
||||
evolve quite a bit more before the interface settles.
|
||||
|
||||
Distinguishing between different MODES is not currently implemented.
|
||||
Handling NSTimers is implemented, but currently disabled.
|
||||
|
||||
Does it strike anyone else that NSNotificationCenter,
|
||||
NSNotificationQueue, NSNotification, NSRunLoop, the "notifications"
|
||||
a run loop sends the objects on which it is listening, NSEvent, and
|
||||
the event queue maintained by NSApplication are all much more
|
||||
intertwined/similar than OpenStep gives them credit for?
|
||||
|
||||
I wonder if these classes could be re-organized a little to make a
|
||||
more uniform, "grand-unified" mechanism for: events,
|
||||
event-listening, event-queuing, and event-distributing. It could
|
||||
be quite pretty.
|
||||
|
||||
(GNUstep would definitely provide classes that were compatible with
|
||||
all these OpenStep classes, but those classes could be wrappers
|
||||
around fundamentally cleaner GNU classes. RMS has advised using an
|
||||
underlying organization/implementation different from NeXT's
|
||||
whenever that makes sense---it helps legally distinguish our work.)
|
||||
|
||||
Thoughts and insights, anyone?
|
||||
|
||||
*/
|
||||
|
||||
/* Alternate names: InputDemuxer, InputListener, EventListener.
|
||||
Alternate names for Notification classes: Dispatcher, EventDistributor, */
|
||||
|
||||
#include <objects/stdobjects.h>
|
||||
#include <objects/RunLoop.h>
|
||||
#include <objects/Heap.h>
|
||||
|
@ -37,9 +68,6 @@
|
|||
Just define it to use memset(). */
|
||||
#define bzero(PTR, LEN) memset (PTR, 0, LEN)
|
||||
|
||||
/* Alternate names: InputDemuxer, InputListener
|
||||
Alternate names for Notification classes: Dispatcher, EventDistributor, */
|
||||
|
||||
static int debug_run_loop = 1;
|
||||
|
||||
@implementation RunLoop
|
||||
|
@ -55,99 +83,180 @@ static RunLoop *current_run_loop;
|
|||
/* This is the designated initializer. */
|
||||
- init
|
||||
{
|
||||
FD_ZERO (&_fds);
|
||||
_fd_2_object = NSCreateMapTable (NSIntMapKeyCallBacks,
|
||||
NSObjectMapValueCallBacks, 0);
|
||||
_fd_objects = [Bag new];
|
||||
_timers = [Heap new];
|
||||
_dispatcher = [NotificationDispatcher defaultInstance];
|
||||
[super init];
|
||||
_current_mode = RunLoopDefaultMode;
|
||||
_mode_2_timers = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
||||
NSObjectMapValueCallBacks, 0);
|
||||
_mode_2_in_ports = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
||||
NSObjectMapValueCallBacks, 0);
|
||||
_mode_2_fd_listeners = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
||||
NSObjectMapValueCallBacks, 0);
|
||||
return self;
|
||||
}
|
||||
|
||||
- notificationDispatcher
|
||||
- (id <String>) currentMode
|
||||
{
|
||||
return _dispatcher;
|
||||
}
|
||||
|
||||
- (void) setNotificationDispatcher: d
|
||||
{
|
||||
_dispatcher = d;
|
||||
return _current_mode;
|
||||
}
|
||||
|
||||
|
||||
/* xxx This is similar to [NotificationDispatcher addObserver...]
|
||||
Perhaps NotificationDispatcher and InputDemuxer will be merged. */
|
||||
/* Adding and removing file descriptors. */
|
||||
|
||||
- (void) addFileDescriptor: (int)fd
|
||||
invocation: invocation
|
||||
object: listener
|
||||
forMode: (id <String>)mode
|
||||
{
|
||||
/* xxx But actually we should let it be added multiple times,
|
||||
and keep count. (?) */
|
||||
if (debug_run_loop)
|
||||
printf ("\tRunLoop adding fd %d\n", fd);
|
||||
assert (!NSMapGet (_fd_2_object, (void*)fd));
|
||||
NSMapInsert (_fd_2_object, (void*)fd, invocation);
|
||||
FD_SET (fd, &_fds);
|
||||
/* xxx Perhaps this should be a Bag instead. */
|
||||
Array *fd_listeners;
|
||||
|
||||
/* xxx We need to keep track of the FD too!
|
||||
Perhaps I'll make a FileDescriptor class to hold all this;
|
||||
it will be more analogous the Port object. */
|
||||
[self notImplemented: _cmd];
|
||||
|
||||
fd_listeners = NSMapGet (_mode_2_fd_listeners, mode);
|
||||
if (!fd_listeners)
|
||||
{
|
||||
fd_listeners = [Array new];
|
||||
NSMapInsert (_mode_2_fd_listeners, mode, fd_listeners);
|
||||
[fd_listeners release];
|
||||
}
|
||||
[fd_listeners addObject: listener];
|
||||
}
|
||||
|
||||
- (void) removeFileDescriptor: (int)fd
|
||||
forMode: (id <String>)mode
|
||||
{
|
||||
if (debug_run_loop)
|
||||
printf ("\tRunLoop removing fd %d\n", fd);
|
||||
assert (NSMapGet (_fd_2_object, (void*)fd));
|
||||
NSMapRemove (_fd_2_object, (void*)fd);
|
||||
FD_CLR (fd, &_fds);
|
||||
#if 1
|
||||
[self notImplemented: _cmd];
|
||||
#else
|
||||
Array *fd_listeners;
|
||||
|
||||
fd_listeners = NSMapGet (_mode_2_fd_listeners, mode);
|
||||
if (!fd_listeners)
|
||||
/* xxx Careful, this is only suppose to "undo" one -addPort:.
|
||||
If we change the -removeObject implementation later to remove
|
||||
all instances of port, we'll have to change this code here. */
|
||||
[fd_listeners removeObject: port];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Adding and removing port objects. */
|
||||
|
||||
- (void) addPort: port
|
||||
forMode: (id <String>)mode
|
||||
{
|
||||
/* xxx Perhaps this should be a Bag instead. */
|
||||
Array *in_ports;
|
||||
|
||||
in_ports = NSMapGet (_mode_2_in_ports, mode);
|
||||
if (!in_ports)
|
||||
{
|
||||
in_ports = [Array new];
|
||||
NSMapInsert (_mode_2_in_ports, mode, in_ports);
|
||||
[in_ports release];
|
||||
}
|
||||
[in_ports addObject: port];
|
||||
}
|
||||
|
||||
- (void) removePort: port
|
||||
forMode: (id <String>)mode
|
||||
{
|
||||
/* xxx Perhaps this should be a Bag instead. */
|
||||
Array *in_ports;
|
||||
|
||||
in_ports = NSMapGet (_mode_2_in_ports, mode);
|
||||
if (in_ports)
|
||||
/* xxx Careful, this is only suppose to "undo" one -addPort:.
|
||||
If we change the -removeObject implementation later to remove
|
||||
all instances of port, we'll have to change this code here. */
|
||||
[in_ports removeObject: port];
|
||||
}
|
||||
|
||||
|
||||
/* Adding timers. They are removed when they are invalid. */
|
||||
|
||||
- (void) addTimer: timer
|
||||
forMode: (id <String>)mode
|
||||
{
|
||||
assert (_timers);
|
||||
[_timers addObjectIfAbsent: timer];
|
||||
Heap *timers;
|
||||
|
||||
timers = NSMapGet (_mode_2_timers, mode);
|
||||
if (!timers)
|
||||
{
|
||||
timers = [Heap new];
|
||||
NSMapInsert (_mode_2_timers, mode, timers);
|
||||
[timers release];
|
||||
}
|
||||
/* xxx Should we make sure it isn't already there? */
|
||||
[timers addObject: timer];
|
||||
}
|
||||
|
||||
|
||||
/* Running the loop. */
|
||||
/* Fire appropriate timers. */
|
||||
|
||||
- limitDateForMode: (id <String>)mode
|
||||
{
|
||||
/* Linux doesn't always return double from methods, even though
|
||||
I'm using -lieee. */
|
||||
#if 1
|
||||
return nil;
|
||||
#else
|
||||
Heap *timers;
|
||||
NSTimer *min_timer = nil;
|
||||
id saved_mode;
|
||||
|
||||
saved_mode = _current_mode;
|
||||
_current_mode = mode;
|
||||
|
||||
timers = NSMapGet (_mode_2_timers, mode);
|
||||
if (!timers)
|
||||
return nil;
|
||||
|
||||
/* Does this properly handle timers that have been sent -invalidate? */
|
||||
while ((min_timer = [_timers minObject])
|
||||
while ((min_timer = [timers minObject])
|
||||
&& ([[min_timer fireDate] timeIntervalSinceNow] > 0))
|
||||
{
|
||||
[_timers removeFirstObject];
|
||||
[timers removeFirstObject];
|
||||
/* Firing will also increment its fireDate, if it is repeating. */
|
||||
if ([min_timer isValid])
|
||||
{
|
||||
[min_timer fire];
|
||||
if ([[min_timer fireDate] timeIntervalSinceNow] < 0)
|
||||
[_timers addObject: min_timer];
|
||||
[timers addObject: min_timer];
|
||||
}
|
||||
}
|
||||
if (debug_run_loop)
|
||||
printf ("\tlimit date %f\n",
|
||||
[[min_timer fireDate] timeIntervalSinceReferenceDate]);
|
||||
|
||||
_current_mode = saved_mode;
|
||||
return [min_timer fireDate];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Listen to input sources */
|
||||
|
||||
- (void) acceptInputForMode: (id <String>)mode
|
||||
beforeDate: limit_date
|
||||
{
|
||||
NSTimeInterval ti;
|
||||
struct timeval timeout;
|
||||
void *select_timeout;
|
||||
fd_set fds_copy;
|
||||
fd_set fds; /* The file descriptors we will listen to. */
|
||||
fd_set read_fds; /* Copy for listening to read-ready fds. */
|
||||
fd_set exception_fds; /* Copy for listening to exception fds. */
|
||||
int select_return;
|
||||
int fd_index;
|
||||
NSMapTable *fd_2_object;
|
||||
id saved_mode;
|
||||
|
||||
saved_mode = _current_mode;
|
||||
_current_mode = mode;
|
||||
|
||||
/* xxx No, perhaps this isn't the right thing to do. */
|
||||
#if 0
|
||||
/* If there are no input sources to listen to, just return. */
|
||||
if (NSCountMapTable (_fd_2_object) == 0)
|
||||
|
@ -180,37 +289,93 @@ static RunLoop *current_run_loop;
|
|||
select_timeout = NULL;
|
||||
}
|
||||
|
||||
fds_copy = _fds;
|
||||
/* Get ready to listen to file descriptors.
|
||||
Initialize the set of FDS we'll pass to select(), and create
|
||||
an empty map for keeping track of which object is associated
|
||||
with which file descriptor. */
|
||||
FD_ZERO (&fds);
|
||||
fd_2_object = NSCreateMapTable (NSIntMapKeyCallBacks,
|
||||
NSObjectMapValueCallBacks, 0);
|
||||
|
||||
/* Wait for incoming data, listening to the file descriptors in FDS. */
|
||||
select_return = select (FD_SETSIZE, &fds_copy, NULL, NULL, select_timeout);
|
||||
|
||||
/* Do the pre-listening set-up for the file descriptors of this mode. */
|
||||
{
|
||||
}
|
||||
|
||||
/* Do the pre-listening set-up for the ports of this mode. */
|
||||
{
|
||||
id ports = NSMapGet (_mode_2_in_ports, mode);
|
||||
if (ports)
|
||||
{
|
||||
id port;
|
||||
int i;
|
||||
|
||||
/* If a port is invalid, remove it from this mode. */
|
||||
for (i = [ports count]-1; i >= 0; i--)
|
||||
{
|
||||
port = [ports objectAtIndex: i];
|
||||
if (![port isValid])
|
||||
[ports removeObjectAtIndex: i];
|
||||
}
|
||||
|
||||
/* Ask our ports for the list of file descriptors they
|
||||
want us to listen to; add these to FD_LISTEN_SET. */
|
||||
for (i = [ports count]-1; i >= 0; i--)
|
||||
{
|
||||
int port_fd_count = 128;
|
||||
int port_fd_array[port_fd_count];
|
||||
port = [ports objectAtIndex: i];
|
||||
if ([port respondsTo: @selector(getFds:count:)])
|
||||
[port getFds: port_fd_array count: &port_fd_count];
|
||||
while (port_fd_count--)
|
||||
{
|
||||
FD_SET (port_fd_array[port_fd_count], &fds);
|
||||
NSMapInsert (fd_2_object,
|
||||
(void*)port_fd_array[port_fd_count], port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for incoming data, listening to the file descriptors in _FDS. */
|
||||
read_fds = fds;
|
||||
exception_fds = fds;
|
||||
select_return = select (FD_SETSIZE, &read_fds, NULL, &exception_fds,
|
||||
select_timeout);
|
||||
|
||||
if (debug_run_loop)
|
||||
printf ("\tselect returned %d\n", select_return);
|
||||
|
||||
if (select_return < 0)
|
||||
{
|
||||
/* Some exceptional condition happened. */
|
||||
/* xxx We can do something with exception_fds, instead of
|
||||
aborting here. */
|
||||
perror ("[TcpInPort receivePacketWithTimeout:] select()");
|
||||
abort ();
|
||||
}
|
||||
else if (select_return == 0)
|
||||
return;
|
||||
|
||||
|
||||
/* Look at all the file descriptors select() says are ready for reading;
|
||||
invoke the corresponding invocation for each of the ready ones. */
|
||||
notify the corresponding object for each of the ready fd's. */
|
||||
for (fd_index = 0; fd_index < FD_SETSIZE; fd_index++)
|
||||
if (FD_ISSET (fd_index, &fds_copy))
|
||||
if (FD_ISSET (fd_index, &read_fds))
|
||||
{
|
||||
id fd_invocation = (id) NSMapGet (_fd_2_object, (void*)fd_index);
|
||||
assert (fd_invocation);
|
||||
[fd_invocation
|
||||
invokeWithObject: [NSNumber numberWithInt: fd_index]];
|
||||
/* xxx We can get rid of this NSNumber autorelease later. */
|
||||
id fd_object = (id) NSMapGet (fd_2_object, (void*)fd_index);
|
||||
assert (fd_object);
|
||||
[fd_object readyForReadingOnFileDescriptor: fd_index];
|
||||
}
|
||||
|
||||
/* Clean up before returning. */
|
||||
NSFreeMapTable (fd_2_object);
|
||||
_current_mode = saved_mode;
|
||||
}
|
||||
|
||||
- (BOOL) runMode: (id <String>)mode
|
||||
beforeDate: date
|
||||
|
||||
/* Running the run loop once through for timers and input listening. */
|
||||
|
||||
- (BOOL) runOnceBeforeDate: date forMode: (id <String>)mode
|
||||
{
|
||||
id d;
|
||||
|
||||
|
@ -223,23 +388,26 @@ static RunLoop *current_run_loop;
|
|||
}
|
||||
|
||||
/* Find out how long we can wait; and fire timers that are ready. */
|
||||
d = [self limitDateForMode: nil];
|
||||
d = [self limitDateForMode: mode];
|
||||
if (!d)
|
||||
d = date;
|
||||
|
||||
/* Wait, listening to our input sources. */
|
||||
[self acceptInputForMode: mode
|
||||
beforeDate: d];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) runOnceBeforeDate: date
|
||||
forMode: (id <String>)mode
|
||||
{
|
||||
return [self runMode: mode beforeDate: date];
|
||||
return [self runOnceBeforeDate: date forMode: _current_mode];
|
||||
}
|
||||
|
||||
- (void) runUntilDate: date
|
||||
|
||||
/* Running the run loop multiple times through. */
|
||||
|
||||
- (void) runUntilDate: date forMode: (id <String>)mode
|
||||
{
|
||||
volatile double ti;
|
||||
|
||||
|
@ -250,17 +418,23 @@ static RunLoop *current_run_loop;
|
|||
id arp = [NSAutoreleasePool new];
|
||||
if (debug_run_loop)
|
||||
printf ("\trun until date %f seconds from now\n", ti);
|
||||
[self runMode: nil beforeDate: date];
|
||||
[self runOnceBeforeDate: date forMode: mode];
|
||||
[arp release];
|
||||
ti = [date timeIntervalSinceNow];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) runUntilDate: date
|
||||
{
|
||||
[self runUntilDate: date forMode: _current_mode];
|
||||
}
|
||||
|
||||
- (void) run
|
||||
{
|
||||
[self runUntilDate: [NSDate distantFuture]];
|
||||
}
|
||||
|
||||
|
||||
/* Class methods that send messages to the current instance. */
|
||||
|
||||
+ (void) run
|
||||
|
@ -275,21 +449,48 @@ static RunLoop *current_run_loop;
|
|||
[current_run_loop runUntilDate: date];
|
||||
}
|
||||
|
||||
+ (void) runUntilDate: date forMode: (id <String>)mode
|
||||
{
|
||||
assert (current_run_loop);
|
||||
[current_run_loop runUntilDate: date forMode: mode];
|
||||
}
|
||||
|
||||
+ (BOOL) runOnceBeforeDate: date
|
||||
{
|
||||
return [current_run_loop runOnceBeforeDate: date];
|
||||
}
|
||||
|
||||
+ (BOOL) runOnceBeforeDate: date forMode: (id <String>)mode
|
||||
{
|
||||
return [current_run_loop runOnceBeforeDate: date forMode: mode];
|
||||
}
|
||||
|
||||
+ currentInstance
|
||||
{
|
||||
assert (current_run_loop);
|
||||
return current_run_loop;
|
||||
}
|
||||
|
||||
+ (id <String>) currentMode
|
||||
{
|
||||
return [current_run_loop currentMode];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/* RunLoop mode strings. */
|
||||
|
||||
id RunLoopDefaultMode = @"RunLoopDefaultMode";
|
||||
|
||||
|
||||
/* NSObject method additions. */
|
||||
|
||||
@implementation NSObject (PerformingAfterDelay)
|
||||
|
||||
- (void) performSelector: (SEL)sel afterDelay: (NSTimeInterval)delay
|
||||
{
|
||||
|
||||
[self notImplemented: _cmd];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in a new issue