File segment/section/record locks in Linux threads

前端 未结 4 1750
萌比男神i
萌比男神i 2021-01-28 16:42

I have a multi-threaded process where a file is shared (read and written) by multiple threads. Is there any way a thread can lock one file segment so that other threads cannot a

4条回答
  •  Happy的楠姐
    2021-01-28 17:28

    No. The region-locking feature you're asking about has surprising semantics and it is not being further developed because it is controlled by POSIX. (In fact, it is Kirk McKusick's preferred example of what's wrong with POSIX.) If there is a non-POSIX byte-range lock facility in Linux, I can't find it.

    There is discussion of the problems of POSIX byte-range locking in a multithreaded world here: http://www.samba.org/samba/news/articles/low_point/tale_two_stds_os2.html.

    However, if you're concerned only with threads within one process, you can build your own region-locking using semaphores. For example:

    #include 
    #include 
    #include 
    
    // A record indicating an active lock.
    struct threadlock {
      int fd;  // or -1 for unused entries.
      off_t start;
      off_t length;
    };
    
    // A table of all active locks (and the unused entries).
    static struct threadlock all_locks[100];
    
    // Mutex housekeeping.
    static pthread_mutex_t mutex;
    static pthread_cond_t some_lock_released;
    static pthread_once_t once_control = PTHREAD_ONCE_INIT;
    static void threadlock_init(void) {
      for (int i = 0; i < sizeof(all_locks)/sizeof(all_locks[0]); ++i)
        all_locks[i].fd = -1;
      pthread_mutex_init(&mutex, (pthread_mutexattr_t *)0);
      pthread_cond_init(&some_lock_released, (pthread_condattr_t *)0);
    }
    
    // True iff the given region overlaps one that is already locked.
    static bool region_overlaps_lock(int fd, off_t start, off_t length) {
      for (int i = 0; i < sizeof(all_locks)/sizeof(all_locks[0]); ++i) {
        const struct threadlock *t = &all_locks[i];
        if (t->fd == fd &&
            t->start < start + length &&
            start < t->start + t->length)
          return true;
      }
      return false;
    }
    
    // Returns a pointer to an unused entry, or NULL if there isn't one.
    static struct threadlock *find_unused_entry(void) {
      for (int i = 0; i < sizeof(all_locks)/sizeof(all_locks[0]); ++i) {
        if (-1 == all_locks[i].fd)
          return &all_locks[i];
      }
      return 0;
    }
    
    // True iff the lock table is full.
    static inline bool too_many_locks(void) {
      return 0 == find_unused_entry();
    }
    
    // Wait until no thread has a lock for the given region
    // [start, start+end) of the given file descriptor, and then lock
    // the region. Keep the return value for threadunlock.
    // Warning: if you open two file descriptors on the same file
    // (including hard links to the same file), this function will fail
    // to notice that they're the same file, and it will happily hand out
    // two locks for the same region.
    struct threadlock *threadlock(int fd, off_t start, off_t length) {
      pthread_once(&once_control, &threadlock_init);
      pthread_mutex_lock(&mutex);
    
      while (region_overlaps_lock(fd, start, length) || too_many_locks())
        pthread_cond_wait(&some_lock_released, &mutex);
    
      struct threadlock *newlock = find_unused_entry();
      newlock->fd = fd;
      newlock->start = start;
      newlock->length = length;
    
      pthread_mutex_unlock(&mutex);
      return newlock;
    }
    
    // Unlocks a region locked by threadlock.
    void threadunlock(struct threadlock *what_threadlock_returned) {
      pthread_mutex_lock(&mutex);
    
      what_threadlock_returned->fd = -1;
      pthread_cond_broadcast(&some_lock_released);
    
      pthread_mutex_unlock(&mutex);
    }
    

    Caution: the code compiles but I haven't tested it even a little.

提交回复
热议问题