Обновление глобальной переменной в обратном вызове асинхронного ввода-вывода

Надеюсь, включенный код C не слишком длинный. Я попытался сократить его до минимума, чтобы показать проблему, с которой я столкнулся. Я использую асинхронный ввод-вывод для записи небольшого количества данных. Основной код вызывает aio_write а затем ожидает, пока обратный вызов ввода-вывода установит глобальную переменную, прежде чем вернуться. Я знаю, что это не обычный способ делать что-то, но я пытаюсь протестировать что-то, связанное с гораздо большим кодом, который вызывает у меня настоящую проблему.

Этот код компилируется и отлично работает под 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

Однако с Intel (скомпилированным с «-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
....

Я новичок в асинхронном вводе-выводе. Я знаю, что есть проблемы с глобальными переменными, например, с условиями гонки. Но в этом простом коде не должно возникать этой проблемы. Есть идеи, что не так, и почему это неправильно только для оптимизированной сборки 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  numelemsn");
           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 existsn");
      } else {
         printf("errno is %dn", 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 waitingn");
                   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 secn", 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 bytesn", write_error);
   printf("writer received %en", alldata[0]);

   close(writing->aio_fildes);

   no_more_writes = 1;
   printf("writer thinks no_more_writes is %dn",
                   no_more_writes);


   return;

}

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *