2

I have the following AcceptCallBack method and was hoping to add an UIActivityIndicator while the method is running, hence the [mvc performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];. invoke is the method which makes the UI changes. And then I have this line [mvc performSelectorOnMainThread:@selector(hide) withObject:nil waitUntilDone:YES]; to remove the UIActivityIndicator. However what seem to happen is that invoke only gets called when AcceptCallBack has finished executing. Are AcceptCallBack and invoke not running on two different threads, therefore allowing them to run simultaneously??

void AcceptCallBack(
                CFSocketRef socket,
                CFSocketCallBackType type,
                CFDataRef address,
                const void *data,
                void *info)
{
NSLog(@"Start Receiving...");

MasterViewController* mvc = (__bridge MasterViewController*)info;
[mvc performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];

CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFIndex bytes;
UInt8 buffer[8192];
UInt8 * fileData;
UInt8 recv_len = 0;

/* The native socket, used for various operations */
CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data;

/* Create the read and write streams for the socket */
CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock,
                             &readStream, &writeStream);

if (!readStream || !writeStream) {
    close(sock);
    fprintf(stderr, "CFStreamCreatePairWithSocket() failed\n");
    return;
}

CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);


bool headerRead = false;
int dataWritten = 0;
NSMutableString* filename = NULL;


NSMutableString * header = [[NSMutableString alloc] init];

while (true) {
    memset(buffer, 0, sizeof(buffer));
    bytes = CFReadStreamRead(readStream, buffer, sizeof(buffer));

    recv_len += bytes;

    if (bytes < 0) {
        fprintf(stderr, "CFReadStreamRead() failed: %d\n", (int)bytes);
        close(sock);
        return;
    }
    if (bytes == 0) {
        break;
    }

    if (!headerRead) {
        for (int b=0; b<bytes; b++) {
            if (buffer[b] == '\n') {
                headerRead = true;
                NSLog(@"Header is: %@", header);

                NSArray *listItems = [header componentsSeparatedByString:@":"];
                filename = [[NSMutableString alloc] init];
                [filename appendString:[listItems objectAtIndex:2]];
                [filename replaceOccurrencesOfString:@"/" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [filename length])];
                fileData = (UInt8*)malloc(sizeof(UInt8) * [[listItems objectAtIndex:3] intValue]);

                b++;
                memcpy(fileData, buffer + b, bytes - b);
                dataWritten = bytes - b;

                break;
            } else {
                [header appendFormat:@"%c", buffer[b]];
            }

        }
    } else {
        memcpy(fileData + dataWritten, buffer, bytes);
        dataWritten += bytes;
    }

}



NSString* docFile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];

NSData * outputData = [[NSData alloc] initWithBytes:fileData length:dataWritten];
if ([outputData writeToFile:docFile atomically:false] == YES) {
    NSLog(@"File received and successfully written out to file------------------------------");

    MasterViewController * thing = (__bridge MasterViewController*)info;

    [thing restClient:NULL loadedFile:docFile];

    NSString *destDir = @"/Slide2Me/";
    [[thing restClient] uploadFile:filename toPath:destDir
                     withParentRev:nil fromPath:docFile];
    [mvc performSelectorOnMainThread:@selector(hide) withObject:nil waitUntilDone:YES];




} else {
    NSLog(@"Failed to write received data to file");
}

}

EDIT So what I ended up doing to get my desired result is put all the above code in dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0) and sandwich it with the changes I want to make on the main thread like so....

void AcceptCallBack(
                    CFSocketRef socket,
                    CFSocketCallBackType type,
                    CFDataRef address,
                    const void *data,
                    void *info)
{


NSLog(@"Start Receiving...");

MasterViewController* mvc = (__bridge MasterViewController*)info;

[mvc performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:YES];


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
      [the code above];

});

[mvc performSelectorOnMainThread:@selector(hide) withObject:nil waitUntilDone:YES];
}
user1135469
  • 1,020
  • 11
  • 22
  • http://stackoverflow.com/questions/7290931/gcd-threads-program-flow-and-ui-updating/7291056#7291056 – bryanmac Feb 27 '13 at 01:16

1 Answers1

6

You can use GCD to make it multithreaded, and give signal when indicator start and stop. performSelector... will be executed when function is finished.. and It doesn't say how you call the AcceptCallBack method, I guess it's already on main thread. If it is then you need to call the AcceptCallback in another thread and use below code to perform "invoke" method on main thread.

dispatch_async(dispatch_get_main_queue(), ^{
    <do work here>
});

http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW1

EDIT:

I would do it like this

static dispatch_queue_t processing_queue;
static dispatch_queue_t request_processing_queue() {
    if (processing_queue == NULL) {
        processing_queue = dispatch_queue_create("com.xxx.processing", 0);
    }
    return processing_queue;
}

void AcceptCallBack(
                    CFSocketRef socket,
                    CFSocketCallBackType type,
                    CFDataRef address,
                    const void *data,
                    void *info)
{

    __block MasterViewController* mvc = (__bridge MasterViewController*)info;

    dispatch_async(processing_queue(), ^{

       dispatch_async(dispatch_get_main_queue(), ^{
            [mvc invoke];
        });


       ..... < Do AcceptCallback Code Here >


       dispatch_async(dispatch_get_main_queue(), ^{
            [mvc hide];
        });

    });

}

WARNING: This is just pseudo code..

andykkt
  • 1,696
  • 16
  • 23
  • Using GCD has the same outcome, which made me look into it further and noticed that AcceptCallBack is actually running on the main thread. I haven't quite got the grasp on this socket stuff yet...but AcceptCallBack is called like this `TCPServer = CFSocketCreate(NULL,PF_INET,SOCK_STREAM,IPPROTO_TCP,kCFSocketAcceptCallBack,(CFSocketCallBack)AcceptCallBack, &CTX);` is there anyway to make it run in another thread (not main)?? – user1135469 Feb 27 '13 at 01:35
  • I've put the answer I was looking for in my EDIT, but an up vote for making me realise `AcceptCallBack` was being called on the main thread. – user1135469 Feb 27 '13 at 12:15