Emulating line-buffering mode (Gradle example)
C printf() function doesn't flush buffers by default, and a lot of code using it may miss this, therefore creating interactivity problems to the end user. It's not specific to C though - it's about the standard I/O streams, and the problem may appear anywhere. The post is based on StackExchange discussion, so you can dig deeper if you want: Turn off buffering in pipe.
Example is based on C code that is specific for macos and has the functionality to read connected USB devices and wait for the specific one.
The Gradle script contains two tasks - runSimple task that runs the program naively, and run task that using "script" utility to emulate the line-buffering mode.
If you use runSimple, then there will be no output until the device is actually connected (and no output at all if you use Ctrl-C to break the execution):
~/ > gradle runSimple
Starting a Gradle Daemon, 1 busy Daemon could not be reused, use --status for details
:compile UP-TO-DATE
:runSimple
Waiting for Cruzer
Got 4 connected devices. Still waiting...
Got 4 connected devices. Still waiting...
BUILD SUCCESSFUL
Total time: 7.851 secs
However, run works correctly and shows the output as needed. The best solution though would be using fflush() function, but unfortunately it's not always possible (especially if we use 3rd party program that we can't recompile).
Example is based on C code that is specific for macos and has the functionality to read connected USB devices and wait for the specific one.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <IOKit/IOKitLib.h> | |
#include <IOKit/usb/IOUSBLib.h> | |
int main(int argc, const char *argv[]) { | |
if (argc < 2) { | |
printf("Please specify the device name.\n"); | |
return 1; | |
} | |
const char* requiredName = argv[1]; | |
size_t length = strlen(requiredName); | |
printf("Waiting for %s\n", requiredName); | |
CFMutableDictionaryRef matchingDict; | |
bool found = false; | |
while (!found && (matchingDict = IOServiceMatching(kIOUSBDeviceClassName))) { | |
io_iterator_t iter; | |
io_service_t device; | |
bool iterIsValid = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter) == KERN_SUCCESS; | |
unsigned counter = 0; | |
while (!found && iterIsValid && (device = IOIteratorNext(iter))) { | |
io_name_t devName; | |
counter++; | |
bool nameIsValid = IORegistryEntryGetName(device, devName) == KERN_SUCCESS; | |
found = nameIsValid && !strncmp(devName, requiredName, length); | |
IOObjectRelease(device); | |
} | |
printf("Got %d connected devices. %s\n", counter, found ? "Found!" : "Still waiting..."); | |
iterIsValid && IOObjectRelease(iter); | |
!found && sleep(1); | |
} | |
return !found; | |
} |
The Gradle script contains two tasks - runSimple task that runs the program naively, and run task that using "script" utility to emulate the line-buffering mode.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def APPLICATION_GROUP = 'Application' | |
ext { | |
device = 'Cruzer' | |
program = 'wait' | |
} | |
task compile(type: Exec) { | |
group APPLICATION_GROUP | |
description 'Compiles the native program.' | |
def src = 'wait.c' | |
inputs.files src | |
outputs.files program | |
commandLine 'clang', '-framework', 'IOKit', src, '-o', program | |
} | |
task runSimple(type: Exec, dependsOn: compile) { | |
group APPLICATION_GROUP | |
description 'Waits for the device.' | |
commandLine "./$program", device | |
} | |
task run(type: Exec, dependsOn: compile) { | |
group APPLICATION_GROUP | |
description 'Waits for the device using the line-buffered mode.' | |
commandLine 'script', '-q', '/dev/null', "./$program", device | |
} |
If you use runSimple, then there will be no output until the device is actually connected (and no output at all if you use Ctrl-C to break the execution):
~/ > gradle runSimple
Starting a Gradle Daemon, 1 busy Daemon could not be reused, use --status for details
:compile UP-TO-DATE
:runSimple
Waiting for Cruzer
Got 4 connected devices. Still waiting...
Got 4 connected devices. Still waiting...
BUILD SUCCESSFUL
Total time: 7.851 secs
However, run works correctly and shows the output as needed. The best solution though would be using fflush() function, but unfortunately it's not always possible (especially if we use 3rd party program that we can't recompile).
Comments
Post a Comment