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