Chapter 19. Non-blocking I/O

GNUstep supports non-blocking I/O on file and network access, which prevent freezing on user interface and avoid using hard-to-debug thread. Here I'll make an application which use the Unix command find to search the files. You can also check the Finger in gnustep-examples, which is the reference of this example.

Use Gorm to make this interface. Adjust the resizing attributes if needed.

Figure 19.1. Interface of Search

Interface of Search

Make a subclass of NSObject called "Controller" and instaniate it. Add outlets: "label" and "textView", and connect them to the interface. Add one action: "searchAction:" and connect NSTextField to the action. Set Controller object as the delegate of NSOwner (NSApp).

Create the source code of Controller class.

When user hit the ENTER key in the NSTextField, -searchAction: will be called and use find to search the file. GNUstep use NSTask to access other non-GNUstep command, and use notification to return the result of non-blocking I/O. Here is the source code of -searchAction:

Controller.m

- (void) searchAction: (id) sender
{
  NSString *file;
  NSArray *args;
  NSTask *task;

  file = [sender stringValue];
  args = [NSArray arrayWithObjects: NSHomeDirectory(), @"-name", file, @"-print", nil];

  ASSIGN(pipe, [NSPipe pipe]);
  task = [NSTask new];
  [task setLaunchPath: @"/usr/bin/find"];
  [task setArguments: args];
  [task setStandardOutput: pipe];
  fileHandle = [pipe fileHandleForReading];
  [[NSNotificationCenter defaultCenter] addObserver: self
                                        selector: @selector(taskEnded:)
                                        name: NSTaskDidTerminateNotification
                                        object: nil];
  [[NSNotificationCenter defaultCenter] addObserver: self
                                        selector: @selector(readData:)
                                        name: NSFileHandleReadCompletionNotification
                                        object: fileHandle];
  [fileHandle readInBackgroundAndNotify];
  [task launch];
}

NSTask requires launching path as the path of the Unix command, and arguments as the parameters for the command. I redirect the output of NSTask to a NSPipe, and get a NSFileHandle from this NSPipe so that I can access the result of NSTask. Once the environment is set up, I add the Controller as the observer for the notifications, which listens to the end of task and the end of output.

Finally, I ask the NSFileHandle to read in the background and start the NSTask. That's the way to use non-blocking I/O on NSFileHandle.

Now, I need the methods for the notification.

Controller.m

- (void) readData: (NSNotification *) not
{
  NSData *data = [[not userInfo] objectForKey: NSFileHandleNotificationDataItem]
;
  NSString *string = [[NSString alloc] initWithData: data
                                       encoding: [NSString defaultCStringEncoding]];
  [textView setString: string];
}

- (void) taskEnded: (NSNotification *) not
{
  [[NSNotificationCenter defaultCenter] removeObserver: self];
  [fileHandle closeFile];
}

Once the reading of output is ended, notification center will call -readData: as specified in the -searchAction:. Then I get the output out of NSFileHandle and put it into NSTextView. Once the task is ended, I remove the observer for notification and close the NSFileHandle.

About the names and keys to use in notification, you can check the header of NSFileHandle and NSTask. Here is the source code: Search-src.tar.gz

NSFileHandle serve as the port to read and write file, and it can also be used to access network by connecting to the BSD socket. GNUstep/OpenSTEP don't have interface for the socket. You have to make one using C library. But GNUstep offer some extension to access network. You can check the header for NSFileHandle for GNUstepExtensions category.

Besides NSFileHandle, NSURL and NSURLHandle also support non-blocking I/O for access data through URL. In NSURL, set up a client object and use -loadResourceDataNotifyingClient:usingCache: to read in the background. The client will receive the data. In NSURLHandle, set up a client object and use -loadInBackground. That's pretty much the classes you want them to be non-blocking. If you want to make a class which is non-blocking, you need to study the run loop of GNUstep, which is out of the range of this Tutorial.

There is also a netclasses which can be use to access network as client or server port if you don't want to write the socket in C. It has been used in several GNUstep projects especially in instant messengers.