在异步 I/O 回调中更新全局变量
Updating a global variable in an asynchronous I/O callback
希望附带的C代码不要太长。我试图将它削减到最低限度以显示我遇到的问题。我正在使用异步 I/O 写入少量数据。主代码调用aio_write
然后等待I/O回调设置一个全局变量再返回。我知道这不是通常的做事方式,但我正在尝试测试与给我真正问题的更大代码相关的东西。
此代码在 Cray 或 GNU 下编译和运行都很好。
在 Intel 下,它编译和运行都很好,优化“-O1”。但是具有更高优化级别的 Intel 构建会在等待循环中挂起。回调确实设置了全局变量,但主代码从未看到它发生。
例如,在 GNU 下,输出类似于
> head -n 32 /dev/urandom > random_stuff.dat # put random data into test file
> ./write_only 10 # run the code; read and write ten doubles from the input file
reader read 5.991429e-16
writer wrote 80 bytes
writer received 5.991429e-16
writer thinks no_more_writes is 1
ran in 7.650000e-04 sec
然而,使用英特尔(在“-O2”或更高版本编译),我得到
reader read 5.991429e-16
writer wrote 80 bytes
writer received 5.991429e-16
writer thinks no_more_writes is 1
main code still waiting
main code still waiting
main code still waiting
main code still waiting
....
我对异步比较陌生 I/O。我知道全局变量存在问题,例如竞争条件。但是这个简单的代码不应该出现这个问题。知道哪里出了问题,为什么仅针对优化的 Intel 构建是错误的? (顺便说一句,如果我将全局变量 wait 替换为基于 aio_error() 的常规等待,那么对于所有编译器、所有优化级别,一切都可以正常工作。不过,我认为,对于我正在处理的更大的真实代码,我需要更像我在这里做的东西。)
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <aio.h>
#include <fcntl.h>
#include <unistd.h>
#include<strings.h>
/* for EINPROGRESS value: */
#include <errno.h>
/* for timing */
#include <sys/time.h>
void writer(sigval_t sigval);
int chunksize, numelems;
double *alldata;
int no_more_writes=0;
int main(int argc, char **argv) {
struct aiocb aiowriter;
int fdin, fdout;
struct timeval start_time, end_time;
long int time_usec;
double time_sec;
char *infile = "random_stuff.dat";
if (argc < 2) {
printf("./write_only numelems\n");
return 1;
}
numelems = atoi(argv[1]);
chunksize = numelems * sizeof(double);
// allocate alldata
alldata = (double*)calloc(numelems, sizeof(double));
// open up input file and read
fdin = open(infile, O_RDONLY);
read(fdin, alldata, chunksize);
close(fdin);
printf("reader read %e \n", alldata[0]);
// writing
// open up output file
fdout = open("write_out.dat", O_WRONLY | O_CREAT, S_IRUSR | S_IRGRP);
// Check is opening of output file was successful:
if (fdout == -1) {
printf("cannot open 'write_out.dat'\n");
if (errno == EACCES || errno == EEXIST) {
printf("file aleady exists\n");
} else {
printf("errno is %d\n", errno);
}
return errno;
}
// bzero out aiowriter
bzero((char*)&aiowriter, sizeof(struct aiocb));
// set writer file desc. and other related info
aiowriter.aio_fildes = fdout;
aiowriter.aio_offset = 0;
aiowriter.aio_nbytes = chunksize;
// set writer buffer
aiowriter.aio_buf = (char*)alldata;
// set writer callback info
aiowriter.aio_sigevent.sigev_notify = SIGEV_THREAD;
aiowriter.aio_sigevent.sigev_notify_function = writer;
aiowriter.aio_sigevent.sigev_notify_attributes = NULL;
aiowriter.aio_sigevent.sigev_value.sival_ptr = &aiowriter;
// timing
gettimeofday(&start_time, NULL);
// the write
aio_write(&aiowriter);
int debugindex=0;
// wait for final writing to finish
while (no_more_writes != 1) {
usleep(100);
if (debugindex > 10000) {
printf("main code still waiting\n");
debugindex=1;
}
debugindex++;
}
// alternative wait loop:
//while (aio_error(&aiowriter) == EINPROGRESS) {}
// timing
gettimeofday(&end_time, NULL);
time_usec = 1000000*(end_time.tv_sec - start_time.tv_sec) +
(end_time.tv_usec - start_time.tv_usec);
time_sec = time_usec / 1000000.;
printf("ran in %e sec\n", time_sec);
free(alldata);
}
// write function
void writer(sigval_t sigval) {
int write_error;
// inputs: the AIO writing object
struct aiocb *writing;
writing = (struct aiocb *)sigval.sival_ptr;
// check amount of data written
write_error = aio_return(writing);
printf("writer wrote %d bytes\n", write_error);
printf("writer received %e\n", alldata[0]);
close(writing->aio_fildes);
no_more_writes = 1;
printf("writer thinks no_more_writes is %d\n",
no_more_writes);
return;
}
你需要将全局变量设置为volatile
否则编译器可以优化冗余加载,因为它不会知道变量从外部改变了。
volatile int no_more_writes = 0;
希望附带的C代码不要太长。我试图将它削减到最低限度以显示我遇到的问题。我正在使用异步 I/O 写入少量数据。主代码调用aio_write
然后等待I/O回调设置一个全局变量再返回。我知道这不是通常的做事方式,但我正在尝试测试与给我真正问题的更大代码相关的东西。
此代码在 Cray 或 GNU 下编译和运行都很好。
在 Intel 下,它编译和运行都很好,优化“-O1”。但是具有更高优化级别的 Intel 构建会在等待循环中挂起。回调确实设置了全局变量,但主代码从未看到它发生。
例如,在 GNU 下,输出类似于
> head -n 32 /dev/urandom > random_stuff.dat # put random data into test file
> ./write_only 10 # run the code; read and write ten doubles from the input file
reader read 5.991429e-16
writer wrote 80 bytes
writer received 5.991429e-16
writer thinks no_more_writes is 1
ran in 7.650000e-04 sec
然而,使用英特尔(在“-O2”或更高版本编译),我得到
reader read 5.991429e-16
writer wrote 80 bytes
writer received 5.991429e-16
writer thinks no_more_writes is 1
main code still waiting
main code still waiting
main code still waiting
main code still waiting
....
我对异步比较陌生 I/O。我知道全局变量存在问题,例如竞争条件。但是这个简单的代码不应该出现这个问题。知道哪里出了问题,为什么仅针对优化的 Intel 构建是错误的? (顺便说一句,如果我将全局变量 wait 替换为基于 aio_error() 的常规等待,那么对于所有编译器、所有优化级别,一切都可以正常工作。不过,我认为,对于我正在处理的更大的真实代码,我需要更像我在这里做的东西。)
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <aio.h>
#include <fcntl.h>
#include <unistd.h>
#include<strings.h>
/* for EINPROGRESS value: */
#include <errno.h>
/* for timing */
#include <sys/time.h>
void writer(sigval_t sigval);
int chunksize, numelems;
double *alldata;
int no_more_writes=0;
int main(int argc, char **argv) {
struct aiocb aiowriter;
int fdin, fdout;
struct timeval start_time, end_time;
long int time_usec;
double time_sec;
char *infile = "random_stuff.dat";
if (argc < 2) {
printf("./write_only numelems\n");
return 1;
}
numelems = atoi(argv[1]);
chunksize = numelems * sizeof(double);
// allocate alldata
alldata = (double*)calloc(numelems, sizeof(double));
// open up input file and read
fdin = open(infile, O_RDONLY);
read(fdin, alldata, chunksize);
close(fdin);
printf("reader read %e \n", alldata[0]);
// writing
// open up output file
fdout = open("write_out.dat", O_WRONLY | O_CREAT, S_IRUSR | S_IRGRP);
// Check is opening of output file was successful:
if (fdout == -1) {
printf("cannot open 'write_out.dat'\n");
if (errno == EACCES || errno == EEXIST) {
printf("file aleady exists\n");
} else {
printf("errno is %d\n", errno);
}
return errno;
}
// bzero out aiowriter
bzero((char*)&aiowriter, sizeof(struct aiocb));
// set writer file desc. and other related info
aiowriter.aio_fildes = fdout;
aiowriter.aio_offset = 0;
aiowriter.aio_nbytes = chunksize;
// set writer buffer
aiowriter.aio_buf = (char*)alldata;
// set writer callback info
aiowriter.aio_sigevent.sigev_notify = SIGEV_THREAD;
aiowriter.aio_sigevent.sigev_notify_function = writer;
aiowriter.aio_sigevent.sigev_notify_attributes = NULL;
aiowriter.aio_sigevent.sigev_value.sival_ptr = &aiowriter;
// timing
gettimeofday(&start_time, NULL);
// the write
aio_write(&aiowriter);
int debugindex=0;
// wait for final writing to finish
while (no_more_writes != 1) {
usleep(100);
if (debugindex > 10000) {
printf("main code still waiting\n");
debugindex=1;
}
debugindex++;
}
// alternative wait loop:
//while (aio_error(&aiowriter) == EINPROGRESS) {}
// timing
gettimeofday(&end_time, NULL);
time_usec = 1000000*(end_time.tv_sec - start_time.tv_sec) +
(end_time.tv_usec - start_time.tv_usec);
time_sec = time_usec / 1000000.;
printf("ran in %e sec\n", time_sec);
free(alldata);
}
// write function
void writer(sigval_t sigval) {
int write_error;
// inputs: the AIO writing object
struct aiocb *writing;
writing = (struct aiocb *)sigval.sival_ptr;
// check amount of data written
write_error = aio_return(writing);
printf("writer wrote %d bytes\n", write_error);
printf("writer received %e\n", alldata[0]);
close(writing->aio_fildes);
no_more_writes = 1;
printf("writer thinks no_more_writes is %d\n",
no_more_writes);
return;
}
你需要将全局变量设置为volatile
否则编译器可以优化冗余加载,因为它不会知道变量从外部改变了。
volatile int no_more_writes = 0;