Вопрос по c++, mutex, posix, cygwin – cygwin pthread_mutex_timedlock surrogate

4

К сожалению, реализация библиотеки pthread в cygwin GCC 4.5.3 не поддерживает стандартную функцию POSIX.

int pthread_mutex_timedlock(pthread_mutex_t* mutex, struct timespec* abstime);

У кого-нибудь есть хорошая идея, как реализовать хороший обходной путь для этого метода в классе-обертке мьютекса? Может быть с помощьюpthread_mutex_trylock() с (в миллисекундах)nanosleep() вызов? У меня нет хорошего представления о последней идее, но в любом случае реализация C ++ может выглядеть так:

bool MyPosixMutexWrapper::try_lock(const TimeDuration<>& timeout)
{
    if(valid)
    {
        if(timeout == TimeDuration<>::Zero)
        {
             if(pthread_mutex_trylock(&mutexHandle) == 0)
             {
                 return true;
             }
        }
        else
        {
            struct timespec now;
            clock_gettime(CLOCK_REALTIME,&now);
            TimeDuration<> tnow(now);
            tnow += timeout;
            struct timespec until = tnow.getNativeValue();
#if defined(_POSIX_TIMEOUTS)
            if(pthread_mutex_timedlock(&mutexHandle,&until) == 0)
            {
                return true;
            }
#else
            long milliseconds = timeout.milliseconds();
            while(milliseconds > 0)
            {
                 if(pthread_mutex_trylock(&mutexHandle) == 0)
                 {
                     return true;
                 }

                 struct timespec interval;
                 struct timespec remaining;
                 interval.tv_sec = 0;
                 interval.tv_nsec = 1000000;
                 do
                 {
                     remaining.tv_sec = 0;
                     remaining.tv_nsec = 0;
                     if(nanosleep(&interval,&remaining) < 0)
                     {
                         if(errno == EINTR)
                         {
                             interval.tv_sec = remaining.tv_sec;
                             interval.tv_nsec = remaining.tv_nsec;
                         }
                         else
                         {
                             return false;
                         }
                     }
                     clock_gettime(CLOCK_REALTIME,&now);
                     tnow = TimeDuration<>(now);
                     if(tnow >= TimeDuration(until))
                     {
                          return pthread_mutex_trylock(&mutexHandle) == 0;
                     }
                } while(remaining.tv_sec > 0 || remaining.tv_nsec > 0);
                --milliseconds;
            }
#endif
        }
    }
    return pthread_mutex_trylock(&mutexHandle) == 0;
}

У кого-нибудь есть лучшая идея или улучшения для этого кода?

@AdamRosenfield: я бы предпочел несколько «POSIX-совместимый»; реализация,_POSIX_TIMEOUTS указывает, чтоpthread_mutex_timedlock() функция доступна или нет. Я не хочу Windows нативное решение в этом коде. πάντα ῥεῖ
Вы могли бы использовать мьютексы Windows вместо мьютексов pthread и использоватьWaitForSingleObject с соответствующим таймаутом. Но мьютексы Windows очень жесткие и требуют системного вызова во всех случаях, даже неоспоримой блокировки, поэтому помимо необходимости писать весь новый код для их устранения, вы можете столкнуться с падением производительности. Adam Rosenfield
Как я уже боялся, предложенная реализация не работает хорошо в среде Cygwin. Несмотря на то, что я улучшил код, чтобы проверить время ожидания ... πάντα ῥεῖ

Ваш Ответ

1   ответ
3

pthread_cond_timedwait чтобы имитировать ваш таймер блокировки. Хитрость в том, чтоtimed_mutex_ никогда не проводится очень долго, так как ожиданиеtimed_cond_ снимает блокировкуtimed_mutex_ также выпущен сразу послеlocked_ установлен или не установлен.

struct MutexGuard {
    pthread_mutex_t &mutex_;
    MutexGuard (pthread_mutex_t &m) : mutex_(m) {
        pthread_mutex_lock(&mutex_);
    }
    ~MutexGuard () {
        pthread_mutex_unlock(&mutex_);
    }
};

struct TimedMutex {
    pthread_mutex_t timed_mutex_;
    pthread_cond_t timed_cond_;
    bool locked_;

    TimedMutex ()
        : timed_mutex_(), timed_cond_(), locked_(false) {
        pthread_mutex_init(&timed_mutex_, 0);
        pthread_cond_init(&timed_cond_, 0);
    }

    ~TimedMutex () {
        pthread_cond_destroy(&timed_cond_);
        pthread_mutex_destroy(&timed_mutex_);
    }

    int lock (const struct timespec *t) {
        MutexGuard g(timed_mutex_);
        while (locked_) {
            int r = pthread_cond_timedwait(&timed_cond_, &timed_mutex_, t);
            if (r < 0) return r;
        }
        locked_ = true;
        return 0;
    }

    void lock () {
        MutexGuard g(timed_mutex_);
        while (locked_) {
            pthread_cond_wait(&timed_cond_, &timed_mutex_);
        }
        locked_ = true;
    }

    void unlock () {
        MutexGuard g(timed_mutex_);
        locked_ = false;
        pthread_cond_signal(&timed_cond_);
    }
};
@PaulGroke: Я понял это только сегодня утром и работал над этим только сейчас. Спасибо за понимание. С уважением
@ user315052: Arrr, черт возьми, я только что опубликовал ответ с "bool" версия - кажется, я был слишком медленным :)
Я не понимаю, почему вы использовали бы здесь два разных мьютекса, простое решение состояло бы в том, чтобы создать свой собственный тип временного мьютекса, используя ОДИН мьютекс и одну условную переменную. Кроме того, это решение, вероятно, будет очень медленным, когда утверждается.
@ g-makulik: боюсь, я не знаю обоснований решений комитета C ++ 11. Но я предполагаю, что это позволяет как программисту включать меньший набор кода из библиотеки, чтобы выполнить работу, так и составителю библиотеки, чтобы обеспечить самую тесную реализацию, которая обеспечивает функциональность.
Спасибо, ваш ответ выглядит очень многообещающе. Просто для любопытства: по этой ли причине стандарт cx11 предусматривает отдельные типы для mutex и timedmutex? Поскольку последнему могут потребоваться дополнительные ресурсы для его реализации? πάντα ῥεῖ

Похожие вопросы