Мьютекс и переменной условие реализации через фьютекс


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

  #ifndef __SYNCHRONIZATION__
  #define __SYNCHRONIZATION__

  #include <unistd.h>
  #include <limits.h>
  #include <sys/syscall.h>
  #include <linux/futex.h>
  #include "types.h"
  #include "assembly.h"

  typedef UINT32 mutex;
  typedef struct condvar condvar;

  struct condvar    {
   mutex *m;
   int seq;
  };

  void mutex_init(mutex *m) {
   *m = 0;
  }

  void mutex_destroy(mutex *m)  {
   *m = 0;
  }

  void mutex_lock(mutex *m) {
   UINT32 c;
   if((c = __sync_val_compare_and_swap(m, 0, 1)) != 0)  {
    do  {
        if((c == 2) || __sync_val_compare_and_swap(m, 1, 2) != 0)
            syscall(SYS_futex, m, FUTEX_WAIT_PRIVATE, 2, NULL, NULL, 0);
    } while((c = __sync_val_compare_and_swap(m, 0, 2)) != 0);
   }
  }

  void mutex_unlock(mutex *m)   {
   if(__sync_fetch_and_sub(m, 1) != 1)  {
    *m = 0;
    syscall(SYS_futex, m, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
   }
  }

  void cond_init(condvar *c, mutex *m)  {
   c->m = m;
   c->seq = 0;
  }

  void cond_destroy(condvar *c) {
   c->m = NULL;
   c->seq = 0;
  }

  void cond_signal(condvar *c)  {
   __sync_fetch_and_add(&(c->seq), 1);
   syscall(SYS_futex, &(c->seq), FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);
  }

  void cond_broadcast(condvar *c)   {
   __sync_fetch_and_add(&(c->seq), 1);
   syscall(SYS_futex, &(c->seq), FUTEX_REQUEUE_PRIVATE, 1, (void *) INT_MAX, c->m, 0);
  }

  void cond_wait(condvar *c)    {
   UINT32 oldSeq = c->seq;
   mutex_unlock(c->m);
   syscall(SYS_futex, &(c->seq), FUTEX_WAIT_PRIVATE, oldSeq, NULL, NULL, 0);
   while (xchg32(c->m, 2))  {
    syscall(SYS_futex, c->m, FUTEX_WAIT_PRIVATE, 2, NULL, NULL, 0);
   }
  }

  #endif


3877
4
задан 10 августа 2011 в 08:08 Источник Поделиться
Комментарии
1 ответ

У меня нет особого опыта код Фьютекс, поэтому отнеситесь к этому с осторожностью:

Глядя на mutex_lock, я думаю, что некоторые комментарии были бы полезны. Как Я
понимаю, это то, что он делает:


  • Во-первых, давайте определимся, что ваши значения 0, 1, 2 означает. Я понимаю
    значит 'свободный', 'заблокирована', 'заблокированы и оспорены' соответственно. Некоторые #определенными
    констант, это будет ясно (если это правильно).

  • Первым сравнить-и-поменять пытается заблокировать мьютекс.

    if((c = __sync_val_compare_and_swap(m, 0, 1)) != 0)  {

    Если это возможно, то
    функция завершается с мьютекс заблокирован (т. е. его значение-1). Однако если
    мьютекс не был разблокирован (0) это не удается, и мы входим в не...цикл while.


  • На данный момент мы знаем, что мьютекс (или, если быть точным - это возможно
    изменен) либо заблокированы или оспариваемого. Если она была оспорена мы хотим сделать
    система фьютекс вызова ждать его, чтобы быть разблокированы.

        if((c == 2) || __sync_val_compare_and_swap(m, 1, 2) != 0)
    syscall(SYS_futex, m, FUTEX_WAIT_PRIVATE, 2, NULL, NULL, 0);

    Поэтому мы делаем систему
    звоните, если мы знаем, что мьютекс был оспорен (с == 2) или если он был
    только заперта, и мы преуспели в этом оспаривается. Но поскольку мьютекс
    возможно, изменились с первым сравнить-и-поменять, надо еще
    сравнение и замена, чтобы поменять его заперли 1 к оспариваемому 2. Если мьютекс
    изменил состояние и больше не заперта, это не удастся, и мы не делаем
    системный вызов. (Обратите внимание, что называть себя имеет защиту от мьютекс изменение состояния, прежде чем он заключается, следовательно, '2' параметр, который гарантирует, что если не 2 ядра не вводиться.)


  • Если мы либо не делает системный вызов (потому что мьютекс был изменен) или
    мы позвонили и он вернулся (снова, потому что мьютекс состояние не изменилось)
    мы достигаем в то время как часть цикла.

    } while((c = __sync_val_compare_and_swap(m, 0, 2)) != 0);

    Мы можем ожидать, что мьютекс, чтобы быть свободным
    (0) на данный момент и что мы можем попытаться заблокировать его. Но вместо этого кода
    пытается установить его на оспоримые (2). Если это удастся контура и функция выхода
    с мьютексом значение оспорен, не просто заперты. Это кажется неправильным - то
    последнее сравнение-и-своп должен быть параметр '1', а не '2'. Если мы заменим 2
    с 1, цикл, условие выхода совпадает с состоянием на старте
    функции и мы можем упростить до:

    enum mutex_state {
    FREE = 0,
    LOCKED,
    CONTESTED
    };

    void mutex_lock(mutex *m)
    {
    enum mutex_state c;
    while ((c = __sync_val_compare_and_swap(m, FREE, LOCKED)) != FREE) // set locked
    {
    if ((c == CONTESTED) ||
    __sync_val_compare_and_swap(m, LOCKED, CONTESTED) != FREE) // set contested
    {
    syscall(SYS_futex, m, FUTEX_WAIT_PRIVATE, CONTESTED, NULL, NULL, 0);
    }
    }
    }


5
ответ дан 4 декабря 2013 в 01:12 Источник Поделиться