Erik Friesen
Erik Friesen

Reputation: 175

fwrite fopen blocking threads

I have a multithreaded linux app written in c running on a i.mx6 arm. I have a 25lc256 spi eeprom mapped to the file system. Writes are relatively slow at the driver level, not much can be done there. The problem is that file functions are blocking other threads for too long. usleep additions don't seem to help, it appears I'll have to do something different, but its not clear to me what to change.

The output on this calling this function from one thread is

EEprom write
EEprom saved in 522.000000 703.000000 705.000000 723.000000 662596.000000 1328858.000000
Capture -EPIPE snd_pcm_prepare

With Capture -EPIPE snd_pcm_prepare coming from the thread where the audio buffer underran because of the blocked thread, I suppose.

int SaveCurrentConfig(void) {//EEPROM
    int r = 1;
    struct timeval tvs, tv1, tv2, tv3, tv4, tv5, tv6;
    gettimeofday(&tvs, NULL);
    printf("EEprom write\n");
    pthread_mutex_lock(&eepromconfigmutex);
    {
        char * ConfigXml = BuildXmlConfig();
        FILE * WriteConfig = fopen(ConfigPath, "w");
        if (WriteConfig == NULL) {
            MyLog("Unable to open eeprom %s\n", strerror(errno));
            r = 0;
            goto badfinish;
        }
        gettimeofday(&tv1, NULL);
        size_t len = strlen(ConfigXml);
        unsigned short CRC = ComputeChecksum(ConfigXml, len);
        fwrite((char*) &len, 1, sizeof (size_t), WriteConfig);
        gettimeofday(&tv2, NULL);
        fwrite((char*) &CRC, 1, 2, WriteConfig);
        gettimeofday(&tv3, NULL);
        fwrite(ConfigXml, 1, strlen(ConfigXml), WriteConfig);
        gettimeofday(&tv4, NULL);
        fseek(WriteConfig, ConfigOffset2, SEEK_SET);
        fwrite((char*) &len, 1, sizeof (size_t), WriteConfig);
        fwrite((char*) &CRC, 1, 2, WriteConfig);
        fwrite(ConfigXml, 1, strlen(ConfigXml), WriteConfig);
        gettimeofday(&tv5, NULL);
        fclose(WriteConfig);
badfinish:
        free(ConfigXml);
    }
    pthread_mutex_unlock(&eepromconfigmutex);
    gettimeofday(&tv6, NULL);
    double diff1 = time_diff(tvs, tv1);
    double diff2 = time_diff(tvs, tv2);
    double diff3 = time_diff(tvs, tv3);
    double diff4 = time_diff(tvs, tv4);
    double diff5 = time_diff(tvs, tv5);
    double diff6 = time_diff(tvs, tv6);

    printf("EEprom saved in %f  %f  %f  %f  %f  %f\n", diff1, diff2, diff3, diff4, diff5, diff6);
    return r;
}

Upvotes: 1

Views: 1133

Answers (3)

caf
caf

Reputation: 239311

If that thread calling fclose() is blocking other threads from running for long periods, then the problem is likely that it is spending a lot of time in kernel mode inside the eeprom driver code, flushing out the pending writes.

There's a few things you could try:

  • Ensure you're running a kernel with the PREEMPT configuration option selected. This will allow the thread to be preempted while it's running the eeprom driver code.
  • In conjunction with the first suggestion, set a higher scheduling priority for the sound thread (and/or a lower scheduling priority for the eeprom writing thread).
  • Change your code to flush the writes out to the eeprom more frequently by calling fflush() after writing a smaller block of data (you may need to also call fsync() on the underlying file descriptor).

Upvotes: 1

ron
ron

Reputation: 995

I would try an

fflush( WriteConfig );

just before your

fclose(WriteConfig);

Upvotes: 0

Brian Malehorn
Brian Malehorn

Reputation: 2685

Looks like the problem is in fclose(). fclose(), but default, will not return until the contents of the file have been written to disk. I believe you can force it to return early with O_NONBLOCK, from man 2 open:

O_NONBLOCK or O_NDELAY

   When possible, the file is opened in  nonblocking  mode.   Neither
   the  open()  nor  any subsequent operations on the file descriptor
   which is returned will cause the calling process to wait.  For the
   handling  of FIFOs (named pipes), see also fifo(7).  For a discus‐
   sion of the effect of O_NONBLOCK  in  conjunction  with  mandatory
   file locks and with file leases, see fcntl(2).

You can try this out with:

int fd = open(ConfigPath, O_WRONLY | O_NONBLOCK);
FILE * WriteConfig = fdopen(fd, "w");

Upvotes: 0

Related Questions