如何有选择地忽略一些inotify事件?

时间:2016-06-01 06:17:47

标签: c++ inotify

假设我在生产者 - 消费者设置中有两个进程(在此示例中模拟了两个线程)。也就是说,一个进程将数据写入文件,另一个进程使用文件中的数据,然后清除所述文件。

我目前拥有的设置,基于我从各种资源在线投入的点点滴滴,是我应该使用锁定文件来确保一次只有一个进程可以访问数据文件。生产者获取锁,写入文件,然后释放锁。同时,消费者使用inotify等待修改事件,此时它获取锁,消耗数据并清空文件。

这似乎相对简单,但是让我失望的部分是当我在我的消费者线程中清空文件时,它再次触发inotify修改事件,再次引发整个流程,并以数据文件结束再次清除,因此永远重复。

我已经尝试了几种方法来解决这个问题,但它们似乎都不对。我担心做错了会引入潜在的竞争条件,或者我最终会跳过修改事件等等。

这是我目前的代码:

#include <fstream>
#include <iostream>
#include <string>
#include "pthread.h"
#include "sys/file.h"
#include "sys/inotify.h"
#include "sys/stat.h"
#include "unistd.h"

const char* lock_filename = "./test_lock_file";
const char* data_filename = "./test_data_file";

int AquireLock(char const* lockName) {
  mode_t m = umask(0);
  int fd = open(lockName, O_RDWR | O_CREAT, 0666);
  umask(m);
  bool success = false;
  if (fd < 0 || flock(fd, LOCK_EX) < 0) {
    close(fd);
    return -1;
  }
  return fd;
}

void ReleaseLock(int fd, char const* lockName) {
  if (fd < 0) return;
  remove(lockName);
  close(fd);
}

void* ConsumerThread(void*) {
  // Set up inotify.
  int file_descriptor = inotify_init();
  if (file_descriptor < 0) return nullptr;
  int watch_descriptor =
      inotify_add_watch(file_descriptor, data_filename, IN_MODIFY);
  if (watch_descriptor < 0) return nullptr;
  char buf[4096] __attribute__((aligned(__alignof__(inotify_event))));

  while (true) {
    // Read new events.
    const inotify_event* event;
    ssize_t numRead = read(file_descriptor, buf, sizeof(buf));
    if (numRead <= 0) return nullptr;

    // For each event, do stuff.
    for (int i = 0; i < numRead; i += sizeof(inotify_event) + event->len) {
      event = reinterpret_cast<inotify_event*>(&buf[i]);

      // Critical section!
      int fd = AquireLock(lock_filename);

      // Read from the file.
      std::string line;
      std::ifstream data_file(data_filename);
      if (data_file.is_open()) {
        while (getline(data_file, line)) {
          std::cout << line << std::endl;
        }
        data_file.close();

        // Clear the file by opening then closing without writing to it.
        std::ofstream erase_data_file(data_filename);
        erase_data_file.close();
        std::cout << "file cleared." << std::endl;
      }
      ReleaseLock(fd, lock_filename);
      // Critical section over!
    }
  }
  return nullptr;
}

int main(int argv, char** argc) {
  // Set up other thread.
  pthread_t thread;
  int rc = pthread_create(&thread, NULL, ConsumerThread, nullptr);
  if (rc) return rc;

  // Producer thread: Periodically write to a file.
  while (true) {
    sleep(3);
    // Critical section!
    int fd = AquireLock(lock_filename);

    // Write some text to a file
    std::ofstream data_file(data_filename);
    int counter = 0;
    if (data_file.is_open()) {
      std::cout << "Writing to file.\n";
      data_file << "This is some example data. " << counter++ << "\n";
      data_file.close();
    }
    ReleaseLock(fd, lock_filename);
    // Critical section over!
  }

  pthread_exit(NULL);
  return 0;
}

我的一个想法是在使用inotify_rm_watch的消费者线程的关键部分的开头禁用跟踪修改事件,然后在离开临界区之前重新添加它。这似乎不起作用。即使事件被禁用,修改事件仍然会被触发,我不知道为什么。

我还考虑过使用布尔值来查看在使用文件时是否有任何文件内容,并且仅在文件不为空时清除文件。这感觉有点hacky,因为它仍然在循环中进行第二次不必要的迭代,但如果我找不到更好的解决方案,我可能会选择它。理想情况下,只有生产者线程的修改才能触发事件,而消费者可能会以某种方式忽略或禁用它自己的文件修改,但我不确定如何实现这种效果。

0 个答案:

没有答案