Обратный вызов ALSA (обработчик SIGIO) иногда зависает где-то в boost::posix_time::microsec_clock::local_time()

Я использую ALSA в асинхронном режиме с обратными вызовами (snd_async_add_pcm_handler()). Каждый обратный вызов ALSA вызывается из обработчика сигналов SIGIO. Каждый обратный вызов вызывает мою функцию getCurrentTimeMs():

// Return current milliseconds (don't care - local time or UTC).
long long getCurrentTimeMs(void)
{
    std::cout << "+"; std::cout.flush();
    long long ret = 0;

#define Z
#ifdef Z
    struct timespec ts;
    clock_gettime( CLOCK_MONOTONIC, &ts);
    ret = ts.tv_sec * 1000;
    ret += ts.tv_nsec / 1000000;
#else
    boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
    std::cout << "."; std::cout.flush();
    boost::posix_time::ptime    epoch_start(boost::gregorian::date(1970,1,1));
    std::cout << "."; std::cout.flush();
    boost::posix_time::time_duration dur = now - epoch_start;
    std::cout << "."; std::cout.flush();
    ret = dur.total_milliseconds();
#endif
    std::cout << "-"; std::cout.flush();
    return ret;
}

  • Signal handler can be called before previous handler is finished;
  • i need current time in ms to measure precise samplerate.

    Если я комментирую #define Z, используется ускорение. В «ускоренном режиме» приложение зависает через непредсказуемое время от начала воспроизведения звука. Приложение strace show зависает на этом:

        write(1, "+"..., 1)                     = 1
        gettimeofday({1332627252, 660534}, NULL) = 0
        futex(0xb68dba4c, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
    

    Но 0xb68dba4c встречается всего 2...3 раза во всем журнале трассировки. futex(0xb68dba4c ... — это не то, что происходит при каждом вызове getCurrentTimeMs(). Но когда это происходит, все зависает, и это происходит только после этого gettimeofday; i см. "+" на консоли, а затем происходит этот фьютекс. Но до этого приложение может воспроизводить тонны звука, вызывая getCurrentTimeMs() при каждом обратном вызове 50 раз в секунду. , Такая загадка...

    В #define Z используется мой код. В этом случае приложение работает отлично - проигрывает гигабайты WAV-файлов без зависаний.

    Приложение имеет 2 потока, запущенных через boost::threadpool, и оба используют метод getCurrentTimeMs(); давайте предположим, что у меня есть некоторые ошибки взаимоблокировки; но я понятия не имею, как #define Z может повлиять на это.

    EDIT: На мой вопрос дан такой ответ, и я принимаю этот ответ:
    1) http://permalink.gmane.org/gmane.linux.alsa.devel/96282
    2) http://answerpot.com/showthread.php?3448138-ALSA+асинхронный+обратныйвызов+повторныйввод+и+DEADLOCK.


  • person pavelkolodin    schedule 25.03.2012    source источник


    Ответы (1)


    Если это то, что мне кажется, есть два вида асинхронного планирования: асинхронные потоки и асинхронные прерывания («сигналы»). Потоки выполняются независимо друг от друга, если только они явно не синхронизируются; сигналы планируются асинхронно, но вытесняют и блокируют любой поток, в который они доставлены. Это очень похоже на то, что функции boost или iostreams, которые вы вызываете, достигают потокобезопасности за счет блокировки, что делает их _un_safe для вызова обработчика прерывания, потому что поток, вытесненный обработчиком, вполне может уже удерживать блокировку.

    Одна вещь, которую вы могли бы сделать, — организовать доставку всех сигналов в поток, который больше ничего не делает — например, запустить поток сразу при запуске и запустить там основной код, оставив исходный основной поток выделенным для обработки сигналов.

    person jthill    schedule 25.03.2012
    comment
    Благодарю вас! Но гарантировано ли, что сигналы будут обрабатываться только основным потоком? Является ли основной поток единственным потоком, который прерывается сигналами? - person pavelkolodin; 25.03.2012
    comment
    Эй, извините, я опоздал, так как вы сказали, что ALSA вы можете получить для Linux, я думаю, что проще всего, вероятно, при запуске заблокировать все и клонировать () в ваш внутренний main () с тем, что вы хотите разделить, затем установите маску сигнала как вам нравится. Затем этот внутренний основной и все, что он создает, по умолчанию запускает сигналы отключения. - person jthill; 31.03.2012