Cannot use GCD send sources to read from Serial Port file descriptors

I'm having problems using Grand Central Dispatch Source events when reading from serial ports.

I use dispatch_source_create with DISPATCH_SOURCE_TYPE_READ so that the OS runs my code block when there is data to read from the fileDescriptor file associated with the serial port. Here is my code

- (void) receiveThread { globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [self fileDescriptor], 0, globalQueue); dispatch_source_set_event_handler(readSource, ^{ char buffer[512]; NSString *bar; ssize_t numBytes; int expected; expected = dispatch_source_get_data(readSource); printf("expected:%d\n",expected); do { numBytes = read([self fileDescriptor], buffer, 512); buffer[numBytes] = '\x000'; //make sure that the string is terminated. printf("bytes:%ld\n",numBytes); if (numBytes != -1) { bar = [NSString stringWithCString:&buffer]; //printf("bytes:%ld\n",numBytes); NSLog(@"String:%@\n",bar); [[NSNotificationCenter defaultCenter] postNotificationName:EISerialTextDidArrive object:bar]; } } while (numBytes > 0); }); dispatch_resume(readSource); } 

When the program starts, the block is called first when serial data is sent to the port. Then I get a message in the console

 [Switching to process 11969 thread 0x6603] 

When more characters are sent to the serial port, the code block is not called. I can still send characters from the serial port, and I can confirm that the characters are sent, but the block does not start a second time.

From the documentation and examples on the Internet, I expect the block to be called multiple times while there are characters in the serial buffer.

+4
source share
2 answers

@KazukiSakamoto correctly indicate that the file descriptor must have O_NONBLOCK installed on it. I found several other problems that may have stuck you: you used &buffer , when you just have to use buffer . In addition, you had a 512-byte buffer, then you read up to 512 bytes and set the next to 0 (for zero termination). If you really read 512 bytes, this would cause a buffer overflow. Also, it seems that readSource is an iVar, and you are referencing self in a block. This is likely to lead to the creation of a save cycle and should be avoided.

Anyway, I wrote the most trivial little application, and then encoded contacts 2 and 3 of my serial port, so everything that is written out will be repeated, and then connect the button in my application to send some data. Worked like a charm! Here is the code:

 @implementation SOAppDelegate { int fd; dispatch_source_t readSrc; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { fd = open("/dev/mySerialPort", O_RDWR | O_NONBLOCK); __block dispatch_source_t blockReadSrc = readSrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue()); dispatch_source_set_event_handler(readSrc, ^{ NSLog(@"expected: %lu\n", dispatch_source_get_data(blockReadSrc)); ssize_t numBytes; do { char buffer[513]; numBytes = read((int)dispatch_source_get_handle(blockReadSrc), buffer, 512); buffer[numBytes] = '\x000'; //make sure that the string is terminated. NSLog(@"numBytes: %ld\n",numBytes); if (numBytes != -1) { NSLog(@"String:%@\n", [NSString stringWithUTF8String: buffer]); } } while (numBytes > 0); }); dispatch_resume(readSrc); } - (IBAction)sendData: (id)sender { write(fd, "foobar", 6); } - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { if (fd > 0) close(fd); fd = 0; if (readSrc) dispatch_release(readSrc); readSrc = nil; return NSTerminateNow; } @end 
+3
source

AFAIK, [self fileDescriptor] must be unblocked. Are you sure about that?

 fcntl([self fileDescriptor], F_SETFL, O_NONBLOCK); 
+1
source

Source: https://habr.com/ru/post/1343622/


All Articles