TransferServer.m

/*
    File:       TransferServer.m
 
    Contains:   Sample server class, talked to through Distributed Objects.
 
    Written by: Quinn "The Eskimo!".
    Modified by Timothy Carroll
 
    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 "TransferServer.h"
 
#import "Controller.h"
 
// IMPORTANT: See the documentation ("ReadMe.rtf" under Supporting Files) for
// big picture information about this project.
 
static long transferCount;
    // This variable holds the count of the number of transfers
    // by any object of this class.  We use this to generate the
    // transfer ID for a transfer, which is helpful in debugging
    // the output of the transfers, matching up starts with finishes
    // and so on.
 
static NSLock *transferCountLock;
    // This is a lock for the above variable because it's used by
    // multiple different threads.
 
static long threadCount;
 
static NSLock *threadCountLock;
 
static NSConnection *theConnection;
@implementation TransferServer
 
+ (void)initialize
    // This method is called before any other methods in our class
    // are called.  We use it to set up the transferCount variable
    // and its lock.
{
    static BOOL tooLate = NO;
 
    // initialize should only be called once.  If it's called
    // a second time, something weird is happening, and we want
    // to know about it.
    
    NSAssert( ! tooLate, @"Hmm, duplicate initialize messages");
 
    transferCount = 0;
    transferCountLock = [[NSLock alloc] init];
    NSAssert( transferCountLock != nil, @"Could not create transferCountLock");
    
    threadCount = 0;
    threadCountLock = [[NSLock alloc] init];
    NSAssert( threadCountLock != nil, @"Could not create threadCountLock");
    
    tooLate = YES;
}
 
+ (long)newTransferID
    // This method increments the transferCount global variable
    // and returns it as the new transfer ID.  Transfer operations
    // call this method to get a unique ID with which to stamp
    // their transfers, which helps in debugging the concurrent
    // operations in progress.  Note that we have to lock
    // transferCount before modifying it, let another thread
    // execute while we're halfway through modifying it.
{
    long result;
 
    [transferCountLock lock];
    
    transferCount += 1;
    result = transferCount;
    
    [transferCountLock unlock];
 
    return (result);
}
 
+ (void)connectWithPorts:(NSArray *)portArray
    // See comments in implementation part.
{
    NSAutoreleasePool *pool;
    NSConnection *connectionToController;
    TransferServer *serverObject;
 
    // First we must create an autorelease pool.  Methods that we invoke
    // are going to expect to be able to do [object autorelease], but this
    // doesn't work unless we explicitly create an autorelease pool because
    // we're running in a new thread, and threads don't start off with a
    // default autorelease pool.
    
    pool = [[NSAutoreleasePool alloc] init];
 
    // Now we connect back to the main thread using the ports given in our
    // portArray argument.  Note the subtle difference in that we use
    // connectWithReceivePort here, whereas Controller's init method uses
    // initWithReceivePort.
    
    connectionToController = [NSConnection connectionWithReceivePort:[portArray objectAtIndex:0]
                                        sendPort:[portArray objectAtIndex:1]];
 
    // Now create a server object.  In this example, we only have one server
    // object to handle all requests.
    
    serverObject = [[self alloc] init];
 
    // Now get the proxy of the root object at the other end of the connection
    // (which was set to be the controller object in Controller's init method)
    // and send it the setServer message so that it knows about our server object.
    // Note that there is nothing magic about the method name "setServer"; it's
    // merely the name I decided to use when designing these two objects.
 
    [connectionToController setRootObject:serverObject];
 
    // We now release our server object, so the controller has the only reference
    // to it.  Well, actually, the NSConnection also has a reference to it,
    // which is what keeps the object around until the main thread picks it up.
    
    [serverObject release];
 
theConnection = connectionToController;
    // Now we enter our run loop.  The run loop waits looking for events and
    // executes them.  In the case of a non-application thread, the source
    // of events is the NSConnections installed in the loop.  In our case, this
    // in the NSConnection we created earlier in this routine.  When a DO
    // message arrives on our receive port, the run loop unpackages the message
    // and executes the corresponding Objective-C method.
        
    [[NSRunLoop currentRunLoop] run];
 
    // Clean up.
    
    [pool release];
    [NSThread exit];
 
    return;
}
 
- (id)init
    // See comments in interface part.
{
    self = [super init];
    if (self != nil) {
        [threadCountLock lock];
        threadCount += 1;
        threadID = threadCount;
        [threadCountLock unlock];
    }
    return (self);
}
 
- (oneway void)slowTransfer:(Controller *)controller
    // See comments in interface part.
{
    long i;
    long transferID;
 
    transferID = [TransferServer newTransferID];
 
    [controller outputString:[NSString stringWithFormat:@"Thread = %02ld, Connection's retain count is %02ld\n", threadID , [theConnection retainCount]]];
    [controller outputString:[NSString stringWithFormat:@"Thread = %02ld, Connection's retain count is %02ld\n", threadID , [theConnection retainCount]]];
    
    [controller outputString:[NSString stringWithFormat:@"  Starting slowTransfer   Thread=%02ld, ID=%02ld\n",
                                                        threadID, transferID] ];
 
    for (i = 0; i < 3; i++) {
 
        // Sleep for 1 second and then output a progress string to the controller.
        // You could substitute any other synchronous activity here, such as an
        // extended computation, or blocking I/O (file system or network).
        
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
        [controller outputString:
                    [NSString stringWithFormat:@"    Doing slowTransfer    Thread=%02ld, ID=%02ld, Step=%ld\n",
                                            threadID, transferID , i] ];
    }
 
    [controller outputString:[NSString stringWithFormat:@"  Finished slowTransfer   Thread=%02ld, ID=%02ld\n",
                                                        threadID, transferID]];
 
    [controller outputString:[NSString stringWithFormat:@"Thread = %02ld, Connection's retain count is %02ld\n", threadID , [theConnection retainCount]]];
 
    [controller serverFinished:self];
}
 
- (oneway void)slowerTransfer:(Controller *)controller
    // See comments in interface part.
{
    long i;
    long transferID;
 
    transferID = [TransferServer newTransferID];
    
    [controller outputString:[NSString stringWithFormat:@"  Starting slowerTransfer Thread=%02ld, ID=%02ld\n",
                                                        threadID, transferID]];
    
    for (i = 0; i < 10; i++) {
 
        // See comment in slowTransfer:.
 
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
        [controller outputString:
                    [NSString stringWithFormat:@"    Doing slowerTransfer  Thread=%02ld, ID=%02ld, Step=%ld\n",
                                            threadID, transferID , i] ];
    }
 
    [controller outputString:[NSString stringWithFormat:@"  Finished slowerTransfer Thread=%02ld, ID=%02ld\n",
                                                    threadID, transferID]];
 
    [controller outputString:[NSString stringWithFormat:@"Thread = %02ld, Connection's retain count is %02ld\n", threadID , [theConnection retainCount]]];
 
    [controller serverFinished:self];
}
 
@end