1

I am trying to create a login page that displays a waiting page while attempting. it has an UIActivityIndicatorView runs on main thread and another thread doing the connection. The code works fine when I uncomment NSLog, but it keeps running when I comment it out. Can anyone explain to me why it doesn't exist the while loop when there is no NSLog? it is a bit complicated. I have a control class that prepare the websocket command and fires it if and only if the websocket is successfully connected.

The action flow is like this:

LoginButton is clicked -> rootView calls [_spinner startAnimating]; -> attempting connecting to websocket -> send log in command when successfully connected.

rootView has to animate the spinner and wait for the response.

  [_spinner startAnimating];
  [NSThread detachNewThreadSelector:@selector(attampingWS) toTarget:self withObject:nil];


  - (void) attampingWS {
     while ([connection isAttamptingWS] && ![connection isConnectedToWebSocket]) {
        /** waiting until it's done **/
        //      NSLog(@"?");
     }
     if ([connection isConnectedToWebSocket]) {
           [self proceedLogin];
           [_spinner performSelectorOnMainThread:@selector(stopAnimating) withObject:nil waitUntilDone:NO];
     }
  }

in connection class:

  @property(nonatomic) NSTimer *attampConnectionTimeOut;
  @property(nonatomic) NSTimer *attampConnection;
  @property BOOL isConnectedToWebSocket;

  - (BOOL) isAttamptingWS {
     return [_attampConnection isValid];
  }

  - (BOOL) isWaitingForResponse {
     return [_waitingForResponse isValid];
  }
  /** this method is redirected from websocket by using delegation **/ 
  - (void)dbConnectionDidConnected:(websocket *)connection {
     [self _terminateAttamptingConnection];
     [self setIsConnectedToWebSocket:TRUE];
  }
JonatWang
  • 1,663
  • 1
  • 11
  • 11
  • 1
    thats interesting ....waiting for the answer.. :) – Prashant Nikam Sep 10 '13 at 04:27
  • What happens when the connection succeeds/is cancelled? Does the same `connection` object return different values, or does it get replaced by a new object which returns the different values? – ughoavgfhw Sep 10 '13 at 04:36
  • 1
    Why not just give the `connection` object a block that it executes whenever it has connected (or not) to the socket? – Dave DeLong Sep 10 '13 at 04:42
  • I would avoid this kind of loop/block code. I think would be better to use a timer or something like, that you can check for status and then report it to the main thread. The behavior is strange, but it can be a compiler optimization, or some concurrence problem that just occurs when you don't have anything taking time inside your loop (like the NSLog). For the timer, you can use the `[NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:]` – Oberdan Nunes Sep 10 '13 at 04:42
  • I am not sure what you meant. those connection objects are exactly identical, i only declare it once and there is no other places that changes it. even if they are not the same object or returns different value, it still not explaining why it doesn't exit the while loop when it doesn't contain NSLog. – JonatWang Sep 10 '13 at 04:43
  • Maybe the NSLog should go on the main thread. Can you wrap it in dispatch_async( dispatch_get_main_queue(), ^{}) and see what happens? – danh Sep 10 '13 at 04:47
  • em I have no idea how to use dispatch_async or dispatch_get_main_queue(), ^{} – JonatWang Sep 10 '13 at 04:53
  • Please post details of the connection object; the answer to your thread sync problems lies with that object. – trojanfoe Sep 10 '13 at 05:58
  • Relevant about why: http://stackoverflow.com/questions/17304258/empty-while-loop-hangs-in-iphone-release-build/17305889#17305889 – Gabriele Petronella Sep 10 '13 at 06:22

2 Answers2

0

You should never use an empty loop to sync between threads, it will waste a lot CPU time and more importantly, as you have found your loop won’t exit. Because the compiler will optimize the code and you will never get correct result of [connection isAttamptingWS].

But to solve this exact problem (to see why this would happen) I think you can add volatile keyword to the return value of isAttamptingWS.

Qiulang
  • 10,295
  • 11
  • 80
  • 129
  • No, the better solution is to get the connection object to callback (perhaps using a block) to initiate the success/failed sequence once it has something to say. – trojanfoe Sep 10 '13 at 05:46
  • Of course that is a better solution. I suggested to use volatile just to let him understand why this would happen. Let me reword my answer – Qiulang Sep 10 '13 at 05:51
  • Your rewording doesn't appear to have worked; you still want to use the same basic approach as the OP. However this question cannot be properly answered as the OP has not revealed what the connection object is he's using. – trojanfoe Sep 10 '13 at 05:58
  • I know that an empty while loop is not a good design pattern, but it is just an weird behavior I can't understand before I tried to optimize it. I have used a timer to avoid this problem like Oberdan Nunes suggested. However, I don't really like the idea of using Timer. Since it keeps calling the method. Is there any way to let the main thread remand idle or doing nothing before websocket is successfully connected and login command is sent? – JonatWang Sep 10 '13 at 06:18
  • Of course, e.g. after calling [NSThread detachNewThreadSelector:@selector(attampingWS) toTarget:self withObject:nil] your main thread can just return and let worker thread callback to your main thread when it is done. – Qiulang Sep 10 '13 at 06:29
  • `volatile` cannot help here (it's ignored when used on a return type). The compiler cannot optimize anything away as it does not see what the message in the condition will do. – Nikolai Ruhe Sep 10 '13 at 07:24
  • This is no optimizer problem, because sending messages (or calling functions) has potential side effects, the compiler cannot optimize away. – Amin Negm-Awad Sep 10 '13 at 07:34
0

Can anyone explain to me why it doesn't exit the while loop when there is no NSLog?

Your code (invisibly) uses the main thread's run loop to process connection and timer callbacks. Those callbacks control the connection object's state and, as a result, the loop condition.

When removing the NSLog call the status cannot change as the main thread is blocked and the runloop cannot process its sources.

When inserting the call again, NSLog's implementation polls the runloop (maybe to communicate with the asl server). Within the call to NSLog the run loop can dispatch delayed actions, timers or connection callbacks, as dbConnectionDidConnected:. In this method the loop condition is becomes false and the loop exits as soon as the call to NSLog returns.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200