Eu encontrei o shared_ptr e o fraco_ptr, um longo com uma lista, fez o trabalho que eu precisava. Meu problema era que eu tinha vários clientes que desejavam interagir com os dados internos de um host. Normalmente, o host atualiza os dados por conta própria; no entanto, se um cliente solicita, o host precisa parar de atualizar até que nenhum cliente esteja acessando os dados do host. Ao mesmo tempo, um cliente pode solicitar acesso exclusivo, para que nenhum outro cliente, nem o host, possa modificar os dados do host.
Como fiz isso, criei uma struct:
struct UpdateLock
{
typedef std::shared_ptr< UpdateLock > ptr;
};
Cada cliente teria um membro desses:
UpdateLock::ptr m_myLock;
Em seguida, o host teria um membro fraco_ptr para exclusividade e uma lista de fraco_ptrs para bloqueios não exclusivos:
std::weak_ptr< UpdateLock > m_exclusiveLock;
std::list< std::weak_ptr< UpdateLock > > m_locks;
Há uma função para ativar o bloqueio e outra função para verificar se o host está bloqueado:
UpdateLock::ptr LockUpdate( bool exclusive );
bool IsUpdateLocked( bool exclusive ) const;
Testo bloqueios em LockUpdate, IsUpdateLocked e periodicamente na rotina de atualização do host. Testar um bloqueio é tão simples quanto verificar se o fraco_ptr expirou e remover qualquer um que expirou da lista m_locks (eu só faço isso durante a atualização do host); posso verificar se a lista está vazia; ao mesmo tempo, recebo o desbloqueio automático quando um cliente redefine o shared_ptr no qual está pendurado, o que também acontece quando um cliente é destruído automaticamente.
O efeito geral é que, como os clientes raramente precisam de exclusividade (normalmente reservados apenas para adições e exclusões), na maioria das vezes uma solicitação para LockUpdate (false), ou seja, não exclusiva, é bem-sucedida desde que (! M_exclusiveLock). E um LockUpdate (true), um pedido de exclusividade, é bem-sucedido apenas quando ambos (! M_exclusiveLock) e (m_locks.empty ()).
Uma fila pode ser adicionada para atenuar entre bloqueios exclusivos e não exclusivos; no entanto, até o momento não tive colisões, então pretendo esperar até que isso aconteça para adicionar a solução (principalmente para que eu tenha uma condição de teste no mundo real).
Até agora, isso está funcionando bem para minhas necessidades; Eu posso imaginar a necessidade de expandir isso, e alguns problemas que podem surgir sobre o uso expandido, no entanto, isso foi rápido de implementar e exigiu muito pouco código personalizado.