4

I am writing an application which has to communicate with a device connected via USB. The app sends and receives data in turns from the device on a fixed timing. All Rx/Tx happens in a separate thread because otherwise the UI would be blocked. The basic structure looks basically like this. (autorelease pools and stuff omitted)

-(void)comThread:(id)arg {
  while(state == kIsConnected) {
    // let timers run
    [runLoop runUntilDate:[NSDate distantFuture]];
    // handle data
    if(rxTxState == kRx) {
      // do some stuff to pass data to upper layers
      rxTxState = kTx;
    }
    if(rxTxState == kTx) {
      // do some stuff to send data
      rxTimeoutTimer = [NSTimer scheduledTimer....];
    }
  } 
}

After sending data, the app waits for either data to be received or the rxTimeoutTimer to fire which leads to retransmission of the packet. The rx-operation works since the underlying layers use async system calls and calls a rx-handler which look basically like this.

-(void)receiveData:(NSData*)data{
  [rxQueue addObject:data];
  [rxTimeoutTimer invalidate];  // cancel timeout
}

Is there an (easy) way to make the [runLoop runUntilDate:] exit from receiveData:? The Apple docs say that removing all timer sources does not guarantee the RunLoop to exit. I read something about calling performSelector:onThread:... but it either did not work or I did not get the point.

Thanks.

insanelygreat
  • 53
  • 1
  • 4

4 Answers4

9
CFRunLoopStop([runLoop getCFRunLoop]);
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
5

The standard pattern is to run the runloop for some timeout period (e.g. 0.5 seconds), and then iterate until the task is accomplished:

while(state == kIsConnected) {
  while(!iterationDone) {
    [runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
    //do other stufff
  }
}

-(void)receiveData:(NSData*)data{
  [rxQueue addObject:data];
  [rxTimeoutTimer invalidate];  // cancel timeout
  iterationDone = YES;
}
Barry Wark
  • 107,306
  • 24
  • 181
  • 206
  • The whole timeout is 0.5 seconds in my case. I would have to run the RunLoop for fractions of this time like 0.05 seconds. I wonder if it really has this resolution. – insanelygreat Jan 12 '10 at 18:11
  • It really has this resolution. – rpj Jan 20 '10 at 19:26
  • The `-runMode:beforeDate:` method with a `distantFuture` argument might work too, since it processes one input source then returns, at which point the `while` condition would be checked and (if it's true) the run loop restarted. – nevan king Mar 13 '18 at 12:48
0

CFRunLoopStop([runLoop getCFRunLoop]); and CancelPerformSelector

didnot work for me , See my Code for running [NSRunLoop currentRunLoop]

timerUpdateLocation = [NSTimer scheduledTimerWithTimeInterval:[time intValue] target:self selector:@selector(startTrackingBg) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timerUpdateLocation forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];

To Stop it I Just invalidate timer.

[timerUpdateLocation invalidate];

Zeeshan
  • 4,194
  • 28
  • 32
0

Do like this can exit. but it will exit after a few seconds.

 -(void)comThread:(id)arg {
      BOOL ret = YES;
      rxTimeoutTimer = [NSTimer scheduledTimer....];
      while(ret) {
        // let timers run
        ret = [runLoop runUntilDate:[NSDate distantFuture]];
      } 
    }
    -(void)receiveData:(NSData*)data{
      [rxQueue addObject:data];
      [rxTimeoutTimer invalidate];  // cancel timeout
      iterationDone = YES;
    }

the kennytm's answer seems right.

wudijimao
  • 53
  • 6