Recentemente, fiz uma pergunta com o título "Is malloc thread safe?" , e dentro disso perguntei: "Malloc é reentrante?"
Fiquei com a impressão de que todos os reentrantes são seguros para discussão.
Esta suposição está errada?
fonte
Recentemente, fiz uma pergunta com o título "Is malloc thread safe?" , e dentro disso perguntei: "Malloc é reentrante?"
Fiquei com a impressão de que todos os reentrantes são seguros para discussão.
Esta suposição está errada?
Funções re-entrantes não dependem de variáveis globais que são expostas nos cabeçalhos da biblioteca C .. pegue strtok () vs strtok_r () por exemplo em C.
Algumas funções precisam de um local para armazenar um 'trabalho em andamento', funções reentrantes permitem que você especifique este ponteiro dentro do armazenamento do próprio thread, não em um global. Uma vez que este armazenamento é exclusivo para a função de chamada, ele pode ser interrompido e reinserido (reentrante) e, uma vez que na maioria dos casos, a exclusão mútua além do que a função implementa não é necessária para que isso funcione, eles são frequentemente considerados discussão segura . Isso não é, entretanto, garantido por definição.
errno, no entanto, é um caso ligeiramente diferente em sistemas POSIX (e tende a ser estranho em qualquer explicação de como tudo isso funciona) :)
Resumindo, reentrant geralmente significa thread safe (como em "use a versão reentrant dessa função se estiver usando threads"), mas thread safe nem sempre significa reentrante (ou o contrário). Quando você está olhando para thread-safety, a simultaneidade é o que você precisa pensar. Se você tiver que fornecer um meio de bloqueio e exclusão mútua para usar uma função, então a função não é inerentemente thread-safe.
Mas, nem todas as funções precisam ser examinadas para qualquer um. malloc()
não precisa ser reentrante, não depende de nada fora do escopo do ponto de entrada para qualquer thread (e é seguro para thread).
Funções que retornam valores alocados estaticamente não são thread-safe sem o uso de um mutex, futex ou outro mecanismo de bloqueio atômico. No entanto, eles não precisam ser reentrantes se não forem interrompidos.
ie:
static char *foo(unsigned int flags)
{
static char ret[2] = { 0 };
if (flags & FOO_BAR)
ret[0] = 'c';
else if (flags & BAR_FOO)
ret[0] = 'd';
else
ret[0] = 'e';
ret[1] = 'A';
return ret;
}
Então, como você pode ver, ter vários threads usando isso sem algum tipo de bloqueio seria um desastre ... mas não tem o propósito de ser reentrado. Você encontrará isso quando a memória alocada dinamicamente for um tabu em alguma plataforma embarcada.
Na programação puramente funcional, reentrant muitas vezes não implica thread-safe, dependeria do comportamento de funções definidas ou anônimas passadas para o ponto de entrada da função, recursão, etc.
A melhor maneira de colocar 'thread safe' é segura para acesso simultâneo , o que ilustra melhor a necessidade.
TL; DR: uma função pode ser reentrante, thread-safe, ambas ou nenhuma.
Os artigos da Wikipedia para thread-safety e reentrância valem a pena ler. Aqui estão algumas citações:
Uma função é thread-safe se:
Uma função é reentrante se:
Como exemplos de possíveis reentradas, a Wikipedia dá o exemplo de uma função projetada para ser chamada por interrupções do sistema: suponha que já esteja em execução quando outra interrupção ocorrer. Mas não pense que você está seguro apenas porque não codifica com interrupções do sistema: você pode ter problemas de reentrada em um programa de thread único se usar callbacks ou funções recursivas.
Exemplos
(Ligeiramente modificado dos artigos da Wikipedia)
Exemplo 1: não thread-safe, não reentrante
/* As this function uses a non-const global variable without any precaution, it is neither reentrant nor thread-safe. */ int t; void swap(int *x, int *y) { t = *x; *x = *y; *y = t; }
Exemplo 2: thread-safe, não reentrante
/* We use a thread local variable: the function is now thread-safe but still not reentrant (within the same thread). */ __thread int t; void swap(int *x, int *y) { t = *x; *x = *y; *y = t; }
Exemplo 3: não thread-safe, reentrante
/* We save the global state in a local variable and we restore it at the end of the function. The function is now reentrant but it is not thread safe. */ int t; void swap(int *x, int *y) { int s; s = t; t = *x; *x = *y; *y = t; t = s; }
Exemplo 4: thread-safe, reentrant
/* We use a local variable: the function is now thread-safe and reentrant, we have ascended to higher plane of existence. */ void swap(int *x, int *y) { int t; t = *x; *x = *y; *y = t; }
fonte
t = *x
, chamaswap()
, entãot
será substituído, levando a resultados inesperados.swap(5, 6)
ser interrompido por aswap(1, 2)
. Depoist=*x
,s=t_original
et=5
. Agora, após a interrupção,s=5
et=1
. No entanto, antes que o segundoswap
retorne, ele irá restaurar o contexto, fazendot=s=5
. Agora, voltamos ao primeiroswap
comt=5 and s=t_original
e continuamos depoist=*x
. Portanto, a função parece ser reentrante. Lembre-se de que cada chamada obtém sua própria cópia des
alocada na pilha.Depende da definição. Por exemplo Qt usa o seguinte:
mas também alertam:
fonte