2013-06-13 2 views
0

В качестве текущего заголовка сообщения, говорящего об этом, boost boost :: interprocess :: interprocess_condition :: wait предполагает атомарную разблокировку мьютекса, пока он ждет , но это не так.boost :: interprocess :: interprocess_condition :: wait не атомирует разблокировку мьютекса во время ожидания

В следующем коде:

boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> state_access_lock(impl->state->state_access_mut); 
impl->state->state_access_cond.wait(state_access_lock); 

В VS2010 в режиме отладки, я нажал паузу и был удивлен, когда я увидел, что state_access_lock все еще заблокирован во время ожидания.

Но это не то, что сообщает boost. here.

Есть ли у кого-нибудь предложение?

Спасибо.

+0

Есть ли у вас фактическое наблюдение за поведением кода или просто значением некоторой переменной в отладчике? –

+0

Я понял это в первую очередь. Потому что мой второй поток, который должен был писать, затем вызывать уведомление, ждал, когда мьютекс будет выпущен вечно. Только после этого я решил проверить, что происходит с моим мьютексом, в первый поток, используя режим отладки VS. –

+0

Кроме того, очень маловероятно, что такая фундаментальная неисправность осталась бы незамеченной, каждая отдельная кодировка различных переменных условия (кстати, вы должны указать, какой из них вы используете) проходит через разблокировку блокировки мьютекса. Таким образом, ошибка должна быть где-то в другом месте. Попробуйте опубликовать больше контекста. –

ответ

0

Основываясь на комментариях, я думаю, что могу сделать вывод.

Не доверяйте членам scoped_lock, которые вы передаете в interprocess_condition :: wait(). Контракт interprocess_condition (в отличие от interprocess_condition_any) указывает, что вы можете использовать его только с блокировкой для interprocess_mutex. Зная это, переменная условия вытаскивает внутренний мьютекс из вашей блокировки, чтобы выполнять свою работу более эффективно, чем если бы она ничего не знала о блокировке.

Итак, когда дело доходит до разблокировки мьютекса, он не вызывает unlock() на вашем scoped_lock, а непосредственно на мьютексе. Это нормально для внутренней реализации; не делайте этого дома. Плохие вещи случаются, если вы не запираете мьютексы до того, как блокировка выходит из области видимости.

Другими словами, поведение, которое вы видите в отладчике, не указывает на проблему. Если у вас тупик, это должно быть где-то в другом месте.

Редактировать

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

+0

Итак, вместо этого 'boost :: interprocess :: scoped_lock state_access_lock (impl-> state-> state_access_mut); impl-> state-> state_access_cond.wait (state_access_lock); 'я должен написать:' state-> state_access_mut.lock(); state-> state_access_cond.do_wait (state-> state_access_mut); 'затем манипулировать' state_access_lock' сам, а не помещать его в 'scoped_lock', я прав? –

+0

Нет, код как написано выглядит нормально. Просто переменная условия разблокирует базовый мьютекс напрямую, а не через объект блокировки, поэтому вы видите запутанные значения в отладчике. Но это должен быть эффект * только *. Если вы зашли в тупик, у вас другая проблема. Почему бы вам не опубликовать код для обоих потоков? –

+0

Я разместил код, используя «Добавить другой ответ», но код идет выше вашего сегодняшнего ответа. –

0

Ok это первая нить:

void CSharedMemory::start(start_mode mode) 
{ 
bool start_mut_locked = true; 
impl->running = true; 
impl->mode = mode; 

stateMetaStruct* state = impl->proc_state; 

boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> state_access_lock(state->state_access_mut); 

while(impl->running) 
{ 
    state->data_written = false; 
    while(!state->data_written) 
    { 
     if(start_mut_locked) 
     { 
      // We can now unlock and let other threads to send data. 
      impl->start_mut.unlock(); 
      start_mut_locked = false; 
     } 

     state->state_access_cond.wait(state_access_lock); // wait here upon sharedmemory's state change 
     boost::interprocess::offset_ptr<stateMetaStruct> s = impl->shm_obj.find<stateMetaStruct>(boost::interprocess::unique_instance).first; 
     state = s.get(); 

     if(!state->data_written) 
     { 
      // Spurious wakeup. 
      glm_debug("Spurious wakeup."); 
     } 

     if(this == state->data_written_by_proccess) 
     { 
      state->data_written = false; 
      glm_debug("Ignoring my proper event."); 
     } 
    } 

    if(impl->running) 
    { 
     // Got action from other process. 
     const interprocess_actions state_action = state->action; 

     if(DO_STOP == state_action) { 
     } 
     else if(DUMP_USERS_REQUEST == state_action) { 
      impl->stateChangedListener->onDumpUsersRequest(); 
     } 
     else if(DUMP_USERS_REPLY == state_action) { 
     } 
     else { 
      glm_err("Unexpected state."); 
     } 
    } 
    } 
} 

Второй поток попытаться отправить данные с помощью этого метода:

void CSharedMemory::sendDumpUsersRequest() 
{ 
// Ensure shm is started. 
boost::mutex::scoped_lock lk(impl->start_mut); 

glm_debug("%s", __FUNCTION__); 

boost::interprocess::offset_ptr<stateMetaStruct> s = impl->shm_obj.find<stateMetaStruct>(boost::interprocess::unique_instance).first; 
stateMetaStruct* state = s.get(); 

boost::interprocess::scoped_lock<boost::interprocess::interprocess_mutex> state_access_lock(state->state_access_mut); 

state->action = DUMP_USERS_REQUEST; 

state->data_written = true; 
state->data_written_by_proccess = this; 

// Send request. 
state->state_access_cond.notify_all(); 
} 

Поведение, второй блок нить при попытке приобрести scoped_mutex потому что первый ждет его.

+0

Отредактировал свой ответ. –