views:

211

answers:

4

I'm having a problem here:

I have a C function:

int my_connect(int sockfd, const struct sockaddr *serv_addr,
      socklen_t addrlen) {
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 alertViewDelegate = [FirewallRest alloc];
 [alertViewDelegate retain];
 //ALog(@"1");

 int error;
 //ALog(@"2");
 char hostname[NI_MAXHOST] = "";
 //ALog(@"3");

 error = getnameinfo(serv_addr, addrlen, hostname, NI_MAXHOST, NULL, 0, 0);
 //ALog(@"4");
 if (error !=0) {
  ALog(@"coudldn't resolve hostname or internal connect");
  [pool release];
  return orig__connect(sockfd, serv_addr, addrlen);
 }
 if (error == 0) {
  ALog(@"hostname: %s", hostname);
  NSString *hostFirst = [NSString stringWithCString:hostname];
  NSString *host = [hostFirst stringByReplacingOccurrencesOfString:@"www." withString:@""];

  NSString *msg = [@"tries to contact: " stringByAppendingString:host];
  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"asdf"
              message:msg
                delegate:alertViewDelegate cancelButtonTitle:@"Never allow!"
             otherButtonTitles:@"1", @"2",@"3", nil];
  [alert show];
  [alert release];



  waitingForResponse = YES;

  while (waitingForResponse == YES) {
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   ALog(@"running Loop1?");
   [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
   ALog(@"running Loop2?");
   [pool drain];
  }


  ALog(@"continues");

  return orig__connect(sockfd, serv_addr, addrlen);
...

It should wait till the UIAlertViewDelegate method (in it's own class) sets waitingForResponse == NO.

extern BOOL waitingForResponse;

@implementation FirewallRest

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
 //NSLog(@"buttonIndex: %@", buttonIndex);

 if (buttonIndex == 0){
  NSLog(@"0");
  waitingForResponse = NO;
 }

 if (buttonIndex == 1){
  NSLog(@"1");
  waitingForResponse = NO;
 }
 if (buttonIndex == 2){
  NSLog(@"2");
  waitingForResponse = NO;
 }
 if (buttonIndex == 3){
  NSLog(@"3");
  waitingForResponse = NO;
 }


}

but somehow that doesn't work :/

does anyone have an idea? or a better way to do this? (I'm not very used to C and objC in one app ;))

Thanks in advance for any help

A: 

This looks a bit weird.. alertViewDelegate = [FirewallRest alloc]; [alertViewDelegate retain]; What is happening here?

Where is 'waitingForResponse' declared? You use the 'extern' keyword. You shouldn't if you don't know what it is doing.

You may need to post a bit more code, including header file.

But, assuming waitingForResponse is a property of alertViewDelegate use self. waitingForResponse when setting waitingForResponse's value and use alertViewDelegate. waitingForResponse when testing it.

+2  A: 

This code

 alertViewDelegate = [FirewallRest alloc];
 [alertViewDelegate retain];

looks very weird. In particular, init is never called on alertViewDelegate. You might expect things to not behave appropriately if the instance isn't properly initialized. You also leak alertViewDelegate, as you have two owership references to it (from alloc and retain, but no balancing releases. At the very least, these lines should be

alertViewDelegate = [[FireallRest alloc] init];

...

[alertViewDelegate release]; //balancing release at end of scope
Barry Wark
yeah, the [alertViewDelegate retain] is not needed. and I'm balancing somewhere else, but thanks.
Yllier
+2  A: 

You should not do IO on the main thread, your need to try to handle a run-loop is evident. Run-loops are tricky, especially since many events do not make the runloop call exit as expected (Anything run on a timer for one, performing selectors just happens to be run on a timer). You could probably solve your problem by not waiting for distantFuture, but just for a few hundred milliseconds at a time.

What you really want to to do is top run my_connect() in a background thread, and just wait for a condition. This code will be much easier to handle, look like less of a hack, and actually perform better.

You can use an instance of NSConditionLock to implement the wait. It is not super obvious how to do this. But you can create a shared instance on NSConditionLock in the variable condLock like this:

condLock = [[NSConditionLock alloc] initWithCondition:0];

And this is then how you would implement the waiting in my_connect():

[condLock lockWhenCondition:1];
// Add code that needs to be thread safe here if you like.
[condLock unlockWithCondition:0];

And this is how you would signal the NSConditionLock that it is time to continue from you delegate method.

[condLock lock];
// More thread safe code here if you like
[condLock unlockWithCondition:1];

You could assign some constants to use instead of 0 and 1 explicitly, and have a quite nice solution.

PeyloW
this is a totally overblown way to solve the OP's question which was not about threads but how to access an instance variable from a c function.
@jib: Nope it is not, my answer was actually not fleshed out enough. The problem is not the variable, but the run-loop hack. Waiting on the main-thread just never is a good idea.
PeyloW
@PeyloW: thank you very much for this suggestion. the problem is that I'm doing codeinjection via MobileSubstrate and connect() is done via the main thread (also the UI), so if I block connect entirely the AlertView won't show up :( (just to be sure I tried showing the AlertView in it's own thread and it didn't work)
Yllier
@Yllier: This is an educated guess on my part, perhaps UIAlertView is using a run-loop mode of it's own. Try replacing `NSDefaultRunLoopMode` with `NSRunLoopCommonModes`, when running the run-loop. Otherwise only event in the default mode are allowed to trigger.
PeyloW
A: 

I ended up using a different system with an ActionSheet:

in my_connect I do ():

[alertViewDelegate performSelectorOnMainThread:@selector(createActionSheet) withObject:nil waitUntilDone:YES];

then in the alertViewDelegate I create the action sheet:

- (void) createActionSheet {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
whatToDo = 4;

UIActionSheet *actionSheet = [[UIActionSheet alloc]
         initWithTitle:[@"this app tries to contact: \n " stringByAppendingString:host]
         delegate:self
         cancelButtonTitle:nil
         destructiveButtonTitle:nil
         otherButtonTitles:@"Always allow!", @"Always Disallow!", @"Allow all for this App",  nil];
actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque;

[actionSheet showInView:[[UIApplication sharedApplication] keyWindow]];
[actionSheet release];
waitUntilDone = YES;

while (waitUntilDone == YES) {

 NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
 //ALog(@"running Loop1?");
 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];
 //ALog(@"running Loop2?");
 [pool2 release];
}
[pool release];

}

so this method is waiting till the delegate's methods are finished and then my_connect() continues.

UI is still very responsive and I didn't notice any slowdown of the apps.

Yllier