How can I wait for a Rust `Child` process whose stdout has been piped to another? - process

I want to implement yes | head -n 1 in Rust, properly
connecting pipes and checking exit statuses: i.e., I want to be able to
determine that yes exits due to SIGPIPE and that head completes
normally. The pipe functionality is easy (Rust Playground):
use std::process;
fn a() -> std::io::Result<()> {
let child_1 = process::Command::new("yes")
.arg("abracadabra")
.stdout(process::Stdio::piped())
.spawn()?;
let pipe: process::ChildStdout = child_1.stdout.unwrap();
let child_2 = process::Command::new("head")
.args(&["-n", "1"])
.stdin(pipe)
.stdout(process::Stdio::piped())
.spawn()?;
let output = child_2.wait_with_output()?;
let result = String::from_utf8_lossy(&output.stdout);
assert_eq!(result, "abracadabra\n");
println!("Good from 'a'.");
Ok(())
}
But while we can wait on child_2 at any point, the declaration of
pipe moves child_1, and so it’s not clear how to wait on child_1.
If we were to simply add child_1.wait()? before the assert_eq!, we’d
hit a compile-time error:
error[E0382]: borrow of moved value: `child_1`
--> src/main.rs:15:5
|
8 | let pipe: process::ChildStdout = child_1.stdout.unwrap();
| -------------- value moved here
...
15 | child_1.wait()?;
| ^^^^^^^ value borrowed here after partial move
|
= note: move occurs because `child_1.stdout` has type `std::option::Option<std::process::ChildStdout>`, which does not implement the `Copy` trait
We can manage to wait with unsafe and platform-specific
functionality (Rust Playground):
use std::process;
fn b() -> std::io::Result<()> {
let mut child_1 = process::Command::new("yes")
.arg("abracadabra")
.stdout(process::Stdio::piped())
.spawn()?;
use std::os::unix::io::{AsRawFd, FromRawFd};
let pipe: process::Stdio =
unsafe { FromRawFd::from_raw_fd(child_1.stdout.as_ref().unwrap().as_raw_fd()) };
let mut child_2 = process::Command::new("head")
.args(&["-n", "1"])
.stdin(pipe)
.stdout(process::Stdio::piped())
.spawn()?;
println!("child_1 exited with: {:?}", child_1.wait().unwrap());
println!("child_2 exited with: {:?}", child_2.wait().unwrap());
let mut result_bytes: Vec<u8> = Vec::new();
std::io::Read::read_to_end(child_2.stdout.as_mut().unwrap(), &mut result_bytes)?;
let result = String::from_utf8_lossy(&result_bytes);
assert_eq!(result, "abracadabra\n");
println!("Good from 'b'.");
Ok(())
}
This prints:
child_1 exited with: ExitStatus(ExitStatus(13))
child_2 exited with: ExitStatus(ExitStatus(0))
Good from 'b'.
This is good enough for the purposes of this question, but surely there
must be a safe and portable way to do this.
For comparison, here is how I would approach the task in C (without
bothering to capture child_2’s output):
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define FAILIF(e, msg) do { if (e) { perror(msg); return 1; } } while (0)
void describe_child(const char *name, int status) {
if (WIFEXITED(status)) {
fprintf(stderr, "%s exited %d\n", name, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "%s signalled %d\n", name, WTERMSIG(status));
} else {
fprintf(stderr, "%s fate unknown\n", name);
}
}
int main(int argc, char **argv) {
int pipefd[2];
FAILIF(pipe(pipefd), "pipe");
pid_t pid_1 = fork();
FAILIF(pid_1 < 0, "child_1: fork");
if (!pid_1) {
FAILIF(dup2(pipefd[1], 1) == -1, "child_1: dup2");
FAILIF(close(pipefd[0]), "child_1: close pipefd");
execlp("yes", "yes", "abracadabra", NULL);
FAILIF(1, "child_1: execlp");
}
pid_t pid_2 = fork();
FAILIF(pid_2 < 0, "child_2: fork");
if (!pid_2) {
FAILIF(dup2(pipefd[0], 0) == -1, "child_2: dup2");
FAILIF(close(pipefd[1]), "child_2: close pipefd");
execlp("head", "head", "-1", NULL);
FAILIF(1, "child_2: execlp");
}
FAILIF(close(pipefd[0]), "close pipefd[0]");
FAILIF(close(pipefd[1]), "close pipefd[1]");
int status_1;
int status_2;
FAILIF(waitpid(pid_1, &status_1, 0) == -1, "waitpid(child_1)");
FAILIF(waitpid(pid_2, &status_2, 0) == -1, "waitpid(child_2)");
describe_child("child_1", status_1);
describe_child("child_2", status_2);
return 0;
}
Save to test.c and run with make test && ./test:
abracadabra
child_1 signalled 13
child_2 exited 0

Use Option::take:
let pipe = child_1.stdout.take().unwrap();
let child_2 = process::Command::new("head")
.args(&["-n", "1"])
.stdin(pipe)
.stdout(process::Stdio::piped())
.spawn()?;
let output = child_2.wait_with_output()?;
child_1.wait()?;
See also:
How do I move out of a struct field that is an Option?
Unable to pipe to or from spawned child process more than once
How do I read the output of a child process without blocking in Rust?
Reading and writing to a long-running std::process::Child
Cannot move out of borrowed content when unwrapping

Related

communication between processes using DUP2 and Unnamed pipes

In child Process when I am writing to pipe 2 after reading data from pipe 1 then after writing no other instruction is being executed. Like if i create another process but the fork system call is not executed. I have also tried by restoring the stdout file descriptor and then cout something but nothing happpens on console..
please have a look. I think there might be some ambiguity in closing the pipes or dup2 which i am unable to get. Thanks.
`#include<iostream>
#include<unistd.h>
#include<fcntl.h>
#include<sys/wait.h>
using namespace std;
int main()
{
char buff[100];
int fd1[2];
int fd2[2];
pipe(fd1);
pipe(fd2);
pid_t pid1 = fork();
if(pid1 > 0)
{
//int a = dup(1);
close(fd1[0]);
close(fd2[0]);
dup2(fd1[1], 1);
cout<<"Hello"<<endl;
//dup2(a,1);
//cout<<"hellow"<<endl;
//dup2(fd2[0], 0);
//cin>>buff;
//cout<<buff<<endl;
close(fd1[1]);
//close(a);
}
else if(pid1 == 0)
{
int a = dup(1);
close(fd1[1]);
close(fd2[0]);
dup2(fd1[0], 0);
cin>>buff;
cout<<buff<<endl;
dup2(fd2[1], 1);
cout<<buff<<endl;
close(fd1[0]);
close(fd2[1]);
pid_t pid2 = fork();
if(pid2 == 0)
{
cout<<"In C2 "<<endl;
}
}
return 0;
}

Minimal Vulkan example of VkDebugUtilsMessengerEXT usage doesn't output anything?

The following Vulkan program attempts to create an instance and setup a VkDebugUtilsMessengerEXT - but when run it doesn't output anything:
#include <stdlib.h>
#include <stdio.h>
#include <vulkan/vulkan.h>
VkBool32 VKAPI_PTR debug_utils_messenger_callback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData) {
printf("%s", pCallbackData->pMessage);
return VK_FALSE;
}
int main() {
// create instance
VkInstanceCreateInfo instance_create_info = {};
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
const char* layers[] = {"VK_LAYER_LUNARG_standard_validation"};
instance_create_info.ppEnabledLayerNames = layers;
instance_create_info.enabledLayerCount = 1;
const char* extensions[] = {VK_EXT_DEBUG_UTILS_EXTENSION_NAME};
instance_create_info.enabledExtensionCount = 1;
instance_create_info.ppEnabledExtensionNames = extensions;
VkInstance instance;
if (VK_SUCCESS != vkCreateInstance(&instance_create_info, NULL, &instance))
exit(EXIT_FAILURE);
// load kCreateDebugUtilsMessengerEXT
PFN_vkCreateDebugUtilsMessengerEXT pvkCreateDebugUtilsMessengerEXT =
(PFN_vkCreateDebugUtilsMessengerEXT)
vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if (pvkCreateDebugUtilsMessengerEXT == NULL)
exit(EXIT_FAILURE);
// create debug utils messenger
VkDebugUtilsMessengerCreateInfoEXT debug_utils_messenger_create_info = {};
debug_utils_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
debug_utils_messenger_create_info.messageSeverity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
debug_utils_messenger_create_info.messageType =
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
debug_utils_messenger_create_info.pfnUserCallback = debug_utils_messenger_callback;
VkDebugUtilsMessengerEXT debug_utils_messenger;
if (VK_SUCCESS != pvkCreateDebugUtilsMessengerEXT(instance, &debug_utils_messenger_create_info, NULL, &debug_utils_messenger))
exit(EXIT_FAILURE);
// destroy instance
vkDestroyInstance(instance, NULL);
}
Why not? I would expect that it outputs some debug messages from the debug_utils_messenger_callback?
Object Tracker does track debug utils objects, but it looks like the layer is only reporting objects belonging to undestroyed device objects at DestroyInstance-time, and debugutils shows up in that list. It should go in a separate Instance object list and get spit out at DestroyInstance time.
A github issue has been submitted: https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/658

how to get (segment offset) each different plane (IODeviceTree, IOService ....) to make a dump?

I want to read analyze a plane (IODeviceTree IOUSB IOService IOACPIPlane)
without using ioreg, by creating a pointer (segment offset) in memory, my question is how to get the address of a plane, that in C or Objective C. Thank you for your answers.
First of all, I'm not sure what you mean by "segment offset" in this context, but the rest of the question makes sense, so I'll just ignore that part for my answer.
Second, the source code for ioreg is available here so you can see exactly how that does it.
A quick summary of how I would do it:
The main function you need to call is IORegistryCreateIterator().
Do not set the options argument to kIORegistryIterateRecursively - otherwise it will be difficult to find the graph structure.
For the plane argument, specify e.g. kIOServicePlane.
Keep calling IOIteratorNext(), and every time you get a registry entry back, try to recurse using IORegistryIteratorEnterEntry() and every time you get IO_OBJECT_NULL back, step one level back out using IORegistryIteratorExitEntry().
Working example code:
#include <stdio.h>
#include <IOKit/IOKitLib.h>
int main(int argc, const char * argv[])
{
io_iterator_t iter = IO_OBJECT_NULL;
unsigned recursion_level = 0;
kern_return_t result = IORegistryCreateIterator(kIOMasterPortDefault, kIOServicePlane, 0, &iter);
if (result == 0 && iter != IO_OBJECT_NULL)
{
while (true)
{
io_object_t entry = IOIteratorNext(iter);
if (entry != IO_OBJECT_NULL)
{
io_name_t name = "";
IORegistryEntryGetName(entry, name);
printf("%*s+ %s\n", recursion_level * 2, "", name);
++recursion_level;
result = IORegistryIteratorEnterEntry(iter);
assert(result == KERN_SUCCESS);
}
else
{
if (recursion_level == 0)
break;
result = IORegistryIteratorExitEntry(iter);
assert(result == KERN_SUCCESS);
--recursion_level;
}
}
}
return 0;
}
(Make sure to link against the IOKit.framework)
Of course, you can do much more interesting things than call IORegistryEntryGetName() on each registry entry.

sending characters from parent to child process and returning char count to parent in C

So for an assignment I have for my Computer Systems class, I need to type characters in the command line when the program runs.
These characters (such as abcd ef) would be stored in argv[].
The parent sends these characters one at a time through a pipe to the child process which then counts the characters and ignores spaces. After all the characters are sent, the child then returns the number of characters that it counted for the parent to report.
When I try to run the program as it is right now, it tells me the value of readIn is 4, the child processed 0 characters and charCounter is 2.
I feel like I'm so close but I'm missing something important :/ The char array for a and in the parent process was an attempt to hardcode the stuff in to see if it worked but I am still unsuccessful. Any help would be greatly appreciated, thank you!
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
//
// Child process counts number of characters sent through pipe.
//
// Child process returns number of characters counted to parent process.
//
// Parent process prints number of characters counted by child process.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h> // for fork()
#include <sys/types.h> // for pid_t
#include <sys/wait.h> // for waitpid()
int main(int argc, char **argv)
{
int fd[2];
pid_t pid;
int status;
int charCounter = 0;
int nChar = 0;
char readbuffer[80];
char readIn = 'a';
//char a[] = {'a', 'b', 'c', 'd'};
pipe(fd);
pid = fork();
if (pid < 0) {
printf("fork error %d\n", pid);
return -1;
}
else if (pid == 0) {
// code that runs in the child process
close(fd[1]);
while(readIn != 0)
{
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
printf("The value of readIn is %d\n", readIn);
if(readIn != ' ')
{
charCounter++;
}
}
close(fd[0]);
//open(fd[1]);
//write(fd[1], charCounter, sizeof(charCounter));
printf("The value of charCounter is %d\n", charCounter);
return charCounter;
}
else
{
// code that runs in the parent process
close(fd[0]);
write(fd[1], &argv, sizeof(argv));
//write(fd[1], &a, sizeof(a));
close(fd[1]);
//open(fd[0]);
//nChar = read(fd[0], readbuffer, sizeof(readbuffer));
nChar = charCounter;
printf("CS201 - Assignment 3 - Andy Grill\n");
printf("The child processed %d characters\n\n", nChar);
if (waitpid(pid, &status, 0) > 0)
{
if (WIFEXITED(status))
{
}
else if (WIFSIGNALED(status))
{
}
}
return 0;
}
}
You're misusing pipes.
A pipe is a unidirectional communication channel. Either you use it to send data from a parent process to a child process, or to send data from a child process to the parent. You can't do both - even if you kept the pipe's read and write channels open on both processes, each process would never know when it was its turn to read from the pipe (e.g. you could end up reading something in the child that was supposed to be read by the parent).
The code to send the characters from parent to child seems mostly correct (more details below), but you need to redesign child to parent communication. Now, you have two options to send the results from child to parent:
Use another pipe. You set up an additional pipe before forking for child-to-parent communication. This complicates the design and the code, because now you have 4 file descriptors to manage from 2 different pipes, and you need to be careful where you close each file descriptor to make sure processes don't hang. It is also probably a bit overkill because the child is only sending a number to the parent.
Return the result from the child as the exit value. This is what you're doing right now, and it's a good choice. However, you fail to retrieve that information in the parent: the child's termination status tells you the number of characters processed, you can fetch this value with waitpid(2), which you already do, but then you never look at status (which contains the results you're looking for).
Remember that a child process has its own address space. It makes no sense to try to read charCounter in the parent because the parent never modified it. The child process gets its own copy of charCounter, so any modifications are seen by the child only. Your code seems to assume otherwise.
To make this more obvious, I would suggest moving the declarations of variables to the corresponding process code. Only fd and pid need to be copied in both processes, the other variables are specific to the task of each process. So you can move the declarations of status and nChar to the parent process specific code, and you can move charCounter, readbuffer and readIn to the child. This will make it very obvious that the variables are completely independent on each process.
Now, some more specific remarks:
pipe(2) can return an error. You ignore the return value, and you shouldn't. At the very least, you should print an error message and terminate if pipe(2) failed for some reason. I also noticed you report errors in fork(2) with printf("fork error %d\n", pid);. This is not the correct way to do it: fork(2) and other syscalls (and library calls) always return -1 on error and set the errno global variable to indicate the cause. So that printf() will always print fork error -1 no matter what the error cause was. It's not helpful. Also, it prints the error message to stdout, and for a number of reasons, error messages should be printed to stderr instead. So I suggest using perror(3) instead, or manually print the error to stderr with fprintf(3). perror(3) has the added benefit of appending the error message description to the text you feed it, so it's usually a good choice.
Example:
if (pipe(fd) < 0) {
perror("pipe(2) error");
exit(EXIT_FAILURE);
}
Other functions that you use throughout the code may also fail, and again, you are ignoring the (possible) error returns. close(2) can fail, as well as read(2). Handle the errors, they are there for a reason.
The way you use readIn is wrong. readIn is the result of read(2), which returns the number of characters read (and it should be an int). The code uses readIn as if it were the next character read. The characters read are stored in readbuffer, and readIn will tell you how many characters are on that buffer. So you use readIn to loop through the buffer contents and count the characters. Something like this:
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
while (readIn > 0) {
int i;
for (i = 0; i < readIn; i++) {
if (readbuffer[i] != ' ') {
charCounter++;
}
}
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
}
Now, about the parent process:
You are not writing the characters into the pipe. This is meaningless:
write(fd[1], &argv, sizeof(argv));
&argv is of type char ***, and sizeof(argv) is the same as sizeof(char **), because argv is a char **. Array dimensions are not kept when passed into a function.
You need to manually loop through argv and write each entry into the pipe, like so:
int i;
for (i = 1; i < argv; i++) {
size_t to_write = strlen(argv[i]);
ssize_t written = write(fd[1], argv[i], to_write);
if (written != to_write) {
if (written < 0)
perror("write(2) error");
else
fprintf(stderr, "Short write detected on argv[%d]: %zd/zd\n", i, written, to_write);
}
}
Note that argv[0] is the name of the program, that's why i starts at 1. If you want to count argv[0] too, just change it to start at 0.
Finally, as I said before, you need to use the termination status fetched by waitpid(2) to get the actual count returned by the child. So you can only print the result after waitpid(2) returned and after making sure the child terminated gracefully. Also, to fetch the actual exit code you need to use the WEXITSTATUS macro (which is only safe to use if WIFEXITED returns true).
So here's the full program with all of these issues addressed:
// Characters from command line arguments are sent to child process
// from parent process one at a time through pipe.
//
// Child process counts number of characters sent through pipe.
//
// Child process returns number of characters counted to parent process.
//
// Parent process prints number of characters counted by child process.
#include <stdlib.h>
#include <stdio.h>
#include <string.h> // for strlen()
#include <unistd.h> // for fork()
#include <sys/types.h> // for pid_t
#include <sys/wait.h> // for waitpid()
int main(int argc, char **argv)
{
int fd[2];
pid_t pid;
if (pipe(fd) < 0) {
perror("pipe(2) error");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid < 0) {
perror("fork(2) error");
exit(EXIT_FAILURE);
}
if (pid == 0) {
int readIn;
int charCounter = 0;
char readbuffer[80];
if (close(fd[1]) < 0) {
perror("close(2) failed on pipe's write channel");
/* We use abort() here so that the child terminates with SIGABRT
* and the parent knows that the exit code is not meaningful
*/
abort();
}
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
while (readIn > 0) {
int i;
for (i = 0; i < readIn; i++) {
if (readbuffer[i] != ' ') {
charCounter++;
}
}
readIn = read(fd[0], readbuffer, sizeof(readbuffer));
}
if (readIn < 0) {
perror("read(2) error");
}
printf("The value of charCounter is %d\n", charCounter);
return charCounter;
} else {
int status;
if (close(fd[0]) < 0) {
perror("close(2) failed on pipe's read channel");
exit(EXIT_FAILURE);
}
int i;
for (i = 1; i < argc; i++) {
size_t to_write = strlen(argv[i]);
ssize_t written = write(fd[1], argv[i], to_write);
if (written != to_write) {
if (written < 0) {
perror("write(2) error");
} else {
fprintf(stderr, "Short write detected on argv[%d]: %zd/%zd\n", i, written, to_write);
}
}
}
if (close(fd[1]) < 0) {
perror("close(2) failed on pipe's write channel on parent");
exit(EXIT_FAILURE);
}
if (waitpid(pid, &status, 0) < 0) {
perror("waitpid(2) error");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("CS201 - Assignment 3 - Andy Grill\n");
printf("The child processed %d characters\n\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "Child terminated abnormally with signal %d\n", WTERMSIG(status));
} else {
fprintf(stderr, "Unknown child termination status\n");
}
return 0;
}
}
Some final notes:
The shell splits arguments by spaces, so if you start the program as ./a.out this is a test, the code will not see a single space. This is irrelevant, because spaces are supposed to be ignored anyway, but if you want to test that the code really ignores spaces, you need to quote the parameters so that the shell does not process them, as in ./a.out "this is a test" "hello world" "lalala".
Only the rightmost (least significant) 8 bits of a program's exit code are used, so WEXITSTATUS will never return more than 255. If the child reads more than 255 characters, the value will wrap around, so you effectively have a character counter modulo 256. If this is a problem, then you need to go with the other approach and set up a 2nd pipe for child-to-parent communication and write the result there (and have the parent read it). You can confirm this on man 2 waitpid:
WEXITSTATUS(status)
returns the exit status of the child. This consists of the least
significant 8 bits of the status argument that the child
specified in a call to exit(3) or _exit(2) or as the argument for a return
statement in main(). This macro should be employed only if
WIFEXITED returned true.

GSL: Error reporting

I want to use the GSL for integration
http://www.gnu.org/software/gsl/manual/html_node/Numerical-Integration.html
However, I find no convenient way how the integrated function
(the function f in the example http://www.gnu.org/software/gsl/manual/html_node/Numerical-integration-examples.html)
can report an error to the integrator. I want to integrate a function which itself results from an integration that could fail. This is my sample program
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_errno.h>
double f (double x, void * params) {
GSL_ERROR("test error",GSL_FAILURE);
return 0.0;
}
int main (void)
{
gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);
double result, error;
gsl_function F;
F.function = &f;
gsl_set_error_handler_off();
int status = gsl_integration_qags (&F, 0, 1, 0, 1e-7, 1000,
w, &result, &error);
printf ("status = %d\n", status);
status = GSL_FAILURE;
printf ("status = %d\n", status);
gsl_integration_workspace_free (w);
return 0;
}
resulting in the output
status = 0
status = -1
I think the integrator should rather stop and return my error code. How can I achieve this?
Thank you very much for your help!!!
2011-04-27: I also tried this variant, after Brian Gough told me,
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_errno.h>
double f (double x, void * params) {
GSL_ERROR("test error",GSL_FAILURE);
return GSL_NAN;
}
int main (void)
{
gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);
double result, error;
gsl_function F;
F.function = &f;
gsl_set_error_handler_off();
int status = gsl_integration_qags (&F, 0, 1, 0, 1e-7, 1000,
w, &result, &error);
printf ("status = %d\n", status);
status = GSL_FAILURE;
printf ("status = %d\n", status);
gsl_integration_workspace_free (w);
return 0;
}
it did not help either. I will now fill out a bug report.
Thanks to Xuebin Wu from the GSL Mailing list the problem is solved:
Hi,
GSL_ERROR itself is a macro, it looks like
gsl_error (reason, __FILE__, __LINE__, gsl_errno);
return gsl_errno;
The function already returns before you return NAN, because GSL_ERROR
has been called. Turning the handler off just let the first line do
nothing. The default error handler abort the program after printing
error message.
I do not think it is a bug. Maybe you can write your own error handler
to solve your problem. For example, you can use "goto" to jump out of
gsl_integration_qags, or set some global variable to indicate the
integration result is incorrect.
PS: I believe this macro is what you need,
Macro: GSL_ERROR_VAL (reason, gsl_errno, value)
This macro is the same as GSL_ERROR but returns a user-defined value
of value instead of an error code. It can be used for mathematical
functions that return a floating point value.
The following example shows how to return a NaN at a mathematical
singularity using the GSL_ERROR_VAL macro,
if (x == 0)
{
GSL_ERROR_VAL("argument lies on singularity",
GSL_ERANGE, GSL_NAN);
}
So I adjusted the code according to
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_errno.h>
double f (double x, void * params) {
// return GSL_NAN;
GSL_ERROR_VAL ("argument lies on singularity", GSL_ERANGE, GSL_NAN);
}
int main (void)
{
gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);
double result, error;
gsl_function F;
F.function = &f;
gsl_set_error_handler_off();
int status = gsl_integration_qags (&F, 0, 1, 0, 1e-7, 1000,
w, &result, &error);
printf ("status = %d\n", status);
status = GSL_FAILURE;
printf ("status = %d\n", status);
gsl_integration_workspace_free (w);
return 0;
}
and everything works as expected...
A bit hackish, but I'd probably have your function store some flag. When it encounters an error it sets the flag and returns zero for all subsequent evaluations. Then, after you've integrated it you can check this flag to see if the result is valid.
What about to write a wrapper for the function which returns pointer to a structure, containing function results and error status ? Or if you use c++, this encapsulation can be made with use of objects ....