Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Controller.m
/* |
File: Controller.m |
Contains: Central control object for sample. |
Written by: Quinn "The Eskimo!" |
Created: Tue 10-Jun-1997 |
Copyright: (c)1997 by Apple Computer, Inc., all rights reserved. |
Change History (most recent first): |
You may incorporate this sample code into your applications without |
restriction, though the sample code has been provided "AS IS" and the |
responsibility for its operation is 100% yours. However, what you are |
not permitted to do is to redistribute the source as "DSC Sample Code" |
after having made changes. If you're going to re-distribute the source, |
we require that you make it clear in the source that the code was |
descended from Apple Sample Code, but that you've made changes. |
*/ |
#import "Controller.h" |
#import "TransferServer.h" |
// IMPORTANT: See the documentation ("ReadMe.rtf" under Supporting Files) for |
// big picture information about this project. |
@implementation Controller |
- (id)init |
// See comments in interface part. |
{ |
self = [super init]; |
if (self != nil) { |
freeServers = [[NSMutableArray alloc] initWithCapacity:5]; |
NSAssert( freeServers != nil, @"Could not allocate freeServers array"); |
} |
return (self); |
} |
- (void)showAboutPanel: (id) sender |
{ |
// Method to load the .nib file for the info panel. |
if (!infoPanel) { |
if (![NSBundle loadNibNamed:@"InfoPanel" owner:self]) { |
NSLog(@"Failed to load InfoPanel.nib"); |
NSBeep(); |
return; |
} |
[infoPanel center]; |
} |
[infoPanel makeKeyAndOrderFront:nil]; |
} |
- (void)awakeFromNib |
{ |
[mainWindow makeKeyAndOrderFront:self]; |
} |
- (oneway void)outputString:(NSString *)theString |
// See comments in interface part. Note that textField is |
// an instance variable declared in our header file that was wired |
// to the real text field in the window via Interface Builder. |
{ |
[textField replaceCharactersInRange:NSMakeRange( |
[ [textField string] length ], 0) |
withString:theString]; |
} |
- (void)serverFinished:(TransferServer *)server |
// See comments in interface part. |
{ |
NSAssert( freeServers != nil, @"Free servers array is nil"); |
NSAssert( server != nil, @"Parameter error"); |
[freeServers addObject:server]; |
} |
- (TransferServer *)createNewServer |
// Creates a new TransferServer object that's running in a different |
// thread and returns it to the caller. |
{ |
TransferServer *result; |
NSPort *port1; |
NSPort *port2; |
NSConnection *connectionToTransferServer; |
NSArray *portArray; |
long waitCounter; |
// First create two new ports and a new NSConnection for sending |
// and receiving Distributed Object messages through those ports. We do |
// this (rather than use attempting to reuse the default NSConnection |
// that all applications have) because we want the ports to remain |
// anonymous. These ports are for talking between our application's |
// threads only; we don't want them published by name on the network. |
port1 = [NSPort port]; |
port2 = [NSPort port]; |
connectionToTransferServer = [[NSConnection alloc] initWithReceivePort:port1 sendPort:port2]; |
// Now put the two ports in an array and start a new thread, executing |
// TransferServer's connectWithPorts: method, with that array as its |
// argument. Notice how the ports are reversed here, so TransferServer's |
// connectWithPorts connects its send port to our receive port and vice versa. |
portArray = [NSArray arrayWithObjects:port2, port1, nil]; |
[NSThread detachNewThreadSelector:@selector(connectWithPorts:) |
toTarget:[TransferServer class] |
withObject:portArray]; |
// Now we wait for the new thread to execute and set the root object at |
// the other end of the connection. The loop just spins until this happen. |
// In theory this is a waste of time, but in practice we never get to increment |
// waitCounter. |
waitCounter = 0; |
while ( [connectionToTransferServer rootProxy] == nil ) { |
waitCounter += 1; |
NSAssert( waitCounter < 10000000, @"TransferServer did not set up rootProxy quickly enough."); |
} |
if (waitCounter != 0) { |
NSLog(@"waitCounter=%ld\n", waitCounter); |
} |
// The following line is an interesting optimisation. We tell our proxy |
// to the transferServer object to about the methods that we're going to |
// send to the proxy. This optimises Distributed Object's delivery of |
// messages. [Normally when DO encounters a new method, it must first |
// conduct a transaction with the remote end to find the types for the |
// arguments of that message. It then bundles up the method and its |
// parameters and sends it. It also caches the response so that following |
// invokations of that method only take one transaction. By setting |
// a protocol for the proxy, you let DO know about the messages in |
// advance, and avoid it ever having to do two transactions.] |
[ [connectionToTransferServer rootProxy] setProtocolForProxy:@protocol(TransferServerInterface)]; |
// Now return the remote server object (actually its proxy) to our caller. |
result = (TransferServer *) [connectionToTransferServer rootProxy]; |
// Note that at this stage we're bleeding the connectionToTransferServer |
// NSConnection object; we're about to destroy our only reference to it. |
// Well that's not actually true because the our thread's run loop still |
// has a reference to it. As we never destroy a thread once we've created it, |
// we don't really need our own reference to it after this point. |
return (result); |
} |
- (TransferServer *)findFreeServer |
// Finds a server that is not busy doing things and returns |
// it to the caller. It first checks the freeServers array |
// to see if we can reuse a pre-existing, but not busy, server. |
// Otherwise it creates a new server to do the job. |
{ |
TransferServer *result; |
if ( [freeServers count] == 0 ) { |
result = [self createNewServer]; |
[result retain]; |
} else { |
result = [freeServers objectAtIndex:[freeServers count] - 1]; |
[freeServers removeLastObject]; |
} |
NSAssert(result != nil, @"findFreeServer didn't"); |
return (result); |
} |
- (void)doSomethingSlow:(id)sender |
// See comments in interface part. |
{ |
TransferServer *transferServer; |
[self outputString:@"doSomethingSlow:\n"]; |
transferServer = [self findFreeServer]; |
[transferServer slowTransfer:self]; |
} |
- (void)doSomethingSlower:(id)sender |
// See comments in interface part. |
{ |
TransferServer *transferServer; |
[self outputString:@"doSomethingSlower:\n"]; |
transferServer = [self findFreeServer]; |
[transferServer slowerTransfer:self]; |
} |
/* |
TIMC |
Currently, this routine does not work properly. Both connection objects seem to be retained many more times than necessary -- and the number goes up as you make server calls. So, this is close to what is needed, but clearly something else is going on in DO. |
*/ |
- (void)killThreads: (id)sender |
{ |
TransferServer *server; |
NSConnection *connect; |
int loop; |
loop = [freeServers count]; |
while (loop > 0) |
{ |
server = [freeServers objectAtIndex:loop-1]; |
[freeServers removeLastObject]; |
connect = [(id) server connectionForProxy]; |
// we invalidate our send port, which will invalidate the connection in the |
// threaded object, causing its runloop to abort and the thread to exit. |
[[connect receivePort] invalidate]; |
// release the connection, we're done with it |
NSLog([NSString stringWithFormat:@"Connection being disposed, retain count is %02ld\n", [connect retainCount]]); |
[server release]; |
[connect release]; |
loop--; |
} |
}; |
@end |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14