Update.
2003-04-01 Ulrich Drepper <drepper@redhat.com> * pthread_create.c (deallocate_tsd): Clear/free memory after the last round, not the first. Use specific_used flag instead of local found_nonzero variable. Use THREAD_[SG]ETMEM where possible. (__free_tcb): Don't call deallocate_tsd here. (start_thread): Call deallocate_tsd here. * pthread_setspecific.c: Set specific_used flag really only when needed. * Makefile (tests): Add tst-tsd3.c * tst-tsd3.c: New file.
This commit is contained in:
parent
fa36efe893
commit
6b4686a534
|
@ -1,3 +1,15 @@
|
||||||
|
2003-04-01 Ulrich Drepper <drepper@redhat.com>
|
||||||
|
|
||||||
|
* pthread_create.c (deallocate_tsd): Clear/free memory after the last
|
||||||
|
round, not the first. Use specific_used flag instead of local
|
||||||
|
found_nonzero variable. Use THREAD_[SG]ETMEM where possible.
|
||||||
|
(__free_tcb): Don't call deallocate_tsd here.
|
||||||
|
(start_thread): Call deallocate_tsd here.
|
||||||
|
* pthread_setspecific.c: Set specific_used flag really only when
|
||||||
|
needed.
|
||||||
|
* Makefile (tests): Add tst-tsd3.c
|
||||||
|
* tst-tsd3.c: New file.
|
||||||
|
|
||||||
2003-03-31 Ulrich Drepper <drepper@redhat.com>
|
2003-03-31 Ulrich Drepper <drepper@redhat.com>
|
||||||
|
|
||||||
* sysdeps/unix/sysv/linux/ia64/lowlevellock.h (__lll_mutex_lock):
|
* sysdeps/unix/sysv/linux/ia64/lowlevellock.h (__lll_mutex_lock):
|
||||||
|
|
|
@ -145,7 +145,7 @@ tests = tst-attr1 tst-attr2 \
|
||||||
tst-kill1 tst-kill2 tst-kill3 tst-kill4 tst-kill5 tst-kill6 \
|
tst-kill1 tst-kill2 tst-kill3 tst-kill4 tst-kill5 tst-kill6 \
|
||||||
tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 \
|
tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 \
|
||||||
tst-eintr1 \
|
tst-eintr1 \
|
||||||
tst-tsd1 tst-tsd2 \
|
tst-tsd1 tst-tsd2 tst-tsd3 \
|
||||||
tst-tls1 tst-tls2 \
|
tst-tls1 tst-tls2 \
|
||||||
tst-fork1 tst-fork2 tst-fork3 tst-fork4 \
|
tst-fork1 tst-fork2 tst-fork3 tst-fork4 \
|
||||||
tst-atfork1 \
|
tst-atfork1 \
|
||||||
|
|
|
@ -112,67 +112,78 @@ deallocate_tsd (struct pthread *pd)
|
||||||
{
|
{
|
||||||
/* Maybe no data was ever allocated. This happens often so we have
|
/* Maybe no data was ever allocated. This happens often so we have
|
||||||
a flag for this. */
|
a flag for this. */
|
||||||
if (pd->specific_used)
|
if (THREAD_GETMEM (pd, specific_used))
|
||||||
{
|
{
|
||||||
size_t round;
|
size_t round;
|
||||||
bool found_nonzero;
|
size_t cnt;
|
||||||
|
|
||||||
for (round = 0, found_nonzero = true;
|
round = 0;
|
||||||
found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS;
|
do
|
||||||
++round)
|
|
||||||
{
|
{
|
||||||
size_t cnt;
|
|
||||||
size_t idx;
|
size_t idx;
|
||||||
|
|
||||||
/* So far no new nonzero data entry. */
|
/* So far no new nonzero data entry. */
|
||||||
found_nonzero = false;
|
THREAD_SETMEM (pd, specific_used, false);
|
||||||
|
|
||||||
for (cnt = idx = 0; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
|
for (cnt = idx = 0; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
|
||||||
if (pd->specific[cnt] != NULL)
|
{
|
||||||
{
|
struct pthread_key_data *level2;
|
||||||
size_t inner;
|
|
||||||
|
|
||||||
for (inner = 0; inner < PTHREAD_KEY_2NDLEVEL_SIZE;
|
level2 = THREAD_GETMEM_NC (pd, specific, cnt);
|
||||||
++inner, ++idx)
|
|
||||||
{
|
|
||||||
void *data = pd->specific[cnt][inner].data;
|
|
||||||
|
|
||||||
if (data != NULL
|
if (level2 != NULL)
|
||||||
/* Make sure the data corresponds to a valid
|
{
|
||||||
key. This test fails if the key was
|
size_t inner;
|
||||||
deallocated and also if it was
|
|
||||||
re-allocated. It is the user's
|
|
||||||
responsibility to free the memory in this
|
|
||||||
case. */
|
|
||||||
&& (pd->specific[cnt][inner].seq
|
|
||||||
== __pthread_keys[idx].seq)
|
|
||||||
/* It is not necessary to register a destructor
|
|
||||||
function. */
|
|
||||||
&& __pthread_keys[idx].destr != NULL)
|
|
||||||
{
|
|
||||||
pd->specific[cnt][inner].data = NULL;
|
|
||||||
__pthread_keys[idx].destr (data);
|
|
||||||
found_nonzero = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cnt != 0)
|
for (inner = 0; inner < PTHREAD_KEY_2NDLEVEL_SIZE;
|
||||||
{
|
++inner, ++idx)
|
||||||
/* The first block is allocated as part of the thread
|
{
|
||||||
descriptor. */
|
void *data = level2[inner].data;
|
||||||
free (pd->specific[cnt]);
|
|
||||||
pd->specific[cnt] = NULL;
|
if (data != NULL
|
||||||
}
|
/* Make sure the data corresponds to a valid
|
||||||
else
|
key. This test fails if the key was
|
||||||
/* Clear the memory of the first block for reuse. */
|
deallocated and also if it was
|
||||||
memset (&pd->specific_1stblock, '\0',
|
re-allocated. It is the user's
|
||||||
sizeof (pd->specific_1stblock));
|
responsibility to free the memory in this
|
||||||
}
|
case. */
|
||||||
else
|
&& (level2[inner].seq
|
||||||
idx += PTHREAD_KEY_1STLEVEL_SIZE;
|
== __pthread_keys[idx].seq)
|
||||||
|
/* It is not necessary to register a destructor
|
||||||
|
function. */
|
||||||
|
&& __pthread_keys[idx].destr != NULL)
|
||||||
|
{
|
||||||
|
level2[inner].data = NULL;
|
||||||
|
__pthread_keys[idx].destr (data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
idx += PTHREAD_KEY_1STLEVEL_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (THREAD_GETMEM (pd, specific_used)
|
||||||
|
&& ++round < PTHREAD_DESTRUCTOR_ITERATIONS);
|
||||||
|
|
||||||
|
/* Clear the memory of the first block for reuse. */
|
||||||
|
memset (&pd->specific_1stblock, '\0', sizeof (pd->specific_1stblock));
|
||||||
|
|
||||||
|
/* Free the memory for the other blocks. */
|
||||||
|
for (cnt = 1; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
|
||||||
|
{
|
||||||
|
struct pthread_key_data *level2;
|
||||||
|
|
||||||
|
level2 = THREAD_GETMEM_NC (pd, specific, cnt);
|
||||||
|
if (level2 != NULL)
|
||||||
|
{
|
||||||
|
/* The first block is allocated as part of the thread
|
||||||
|
descriptor. */
|
||||||
|
free (level2);
|
||||||
|
THREAD_SETMEM_NC (pd, specific, cnt, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pd->specific_used = false;
|
THREAD_SETMEM (pd, specific_used, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,9 +204,6 @@ __free_tcb (struct pthread *pd)
|
||||||
running thread is gone. */
|
running thread is gone. */
|
||||||
abort ();
|
abort ();
|
||||||
|
|
||||||
/* Run the destructor for the thread-local data. */
|
|
||||||
deallocate_tsd (pd);
|
|
||||||
|
|
||||||
/* Queue the stack memory block for reuse and exit the process. The
|
/* Queue the stack memory block for reuse and exit the process. The
|
||||||
kernel will signal via writing to the address returned by
|
kernel will signal via writing to the address returned by
|
||||||
QUEUE-STACK when the stack is available. */
|
QUEUE-STACK when the stack is available. */
|
||||||
|
@ -232,6 +240,9 @@ start_thread (void *arg)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Run the destructor for the thread-local data. */
|
||||||
|
deallocate_tsd (pd);
|
||||||
|
|
||||||
/* Clean up any state libc stored in thread-local variables. */
|
/* Clean up any state libc stored in thread-local variables. */
|
||||||
__libc_thread_freeres ();
|
__libc_thread_freeres ();
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,10 @@ __pthread_setspecific (key, value)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
||||||
level2 = &self->specific_1stblock[key];
|
level2 = &self->specific_1stblock[key];
|
||||||
|
|
||||||
|
/* Remember that we stored at least one set of data. */
|
||||||
|
if (value != NULL)
|
||||||
|
THREAD_SETMEM (self, specific_used, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -76,6 +80,9 @@ __pthread_setspecific (key, value)
|
||||||
|
|
||||||
/* Pointer to the right array element. */
|
/* Pointer to the right array element. */
|
||||||
level2 = &level2[idx2nd];
|
level2 = &level2[idx2nd];
|
||||||
|
|
||||||
|
/* Remember that we stored at least one set of data. */
|
||||||
|
THREAD_SETMEM (self, specific_used, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store the data and the sequence number so that we can recognize
|
/* Store the data and the sequence number so that we can recognize
|
||||||
|
@ -83,9 +90,6 @@ __pthread_setspecific (key, value)
|
||||||
level2->seq = seq;
|
level2->seq = seq;
|
||||||
level2->data = (void *) value;
|
level2->data = (void *) value;
|
||||||
|
|
||||||
/* Remember that we stored at least one set of data. */
|
|
||||||
THREAD_SETMEM (self, specific_used, true);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
strong_alias (__pthread_setspecific, pthread_setspecific)
|
strong_alias (__pthread_setspecific, pthread_setspecific)
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||||
|
This file is part of the GNU C Library.
|
||||||
|
Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
|
||||||
|
|
||||||
|
The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with the GNU C Library; if not, write to the Free
|
||||||
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
|
02111-1307 USA. */
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
|
||||||
|
static pthread_key_t key1;
|
||||||
|
static pthread_key_t key2;
|
||||||
|
|
||||||
|
|
||||||
|
static int left;
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
destr1 (void *arg)
|
||||||
|
{
|
||||||
|
if (--left > 0)
|
||||||
|
{
|
||||||
|
puts ("set key2");
|
||||||
|
|
||||||
|
if (pthread_setspecific (key2, (void *) 1l) != 0)
|
||||||
|
{
|
||||||
|
puts ("destr1: setspecific failed");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
destr2 (void *arg)
|
||||||
|
{
|
||||||
|
if (--left > 0)
|
||||||
|
{
|
||||||
|
puts ("set key1");
|
||||||
|
|
||||||
|
if (pthread_setspecific (key1, (void *) 1l) != 0)
|
||||||
|
{
|
||||||
|
puts ("destr2: setspecific failed");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *
|
||||||
|
tf (void *arg)
|
||||||
|
{
|
||||||
|
/* Let the destructors work. */
|
||||||
|
left = 7;
|
||||||
|
|
||||||
|
if (pthread_setspecific (key1, (void *) 1l) != 0
|
||||||
|
|| pthread_setspecific (key2, (void *) 1l) != 0)
|
||||||
|
{
|
||||||
|
puts ("tf: setspecific failed");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
do_test (void)
|
||||||
|
{
|
||||||
|
/* Allocate two keys, both with destructors. */
|
||||||
|
if (pthread_key_create (&key1, destr1) != 0
|
||||||
|
|| pthread_key_create (&key2, destr2) != 0)
|
||||||
|
{
|
||||||
|
puts ("key_create failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t th;
|
||||||
|
if (pthread_create (&th, NULL, tf, NULL) != 0)
|
||||||
|
{
|
||||||
|
puts ("create failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_join (th, NULL) != 0)
|
||||||
|
{
|
||||||
|
puts ("join failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left != 0)
|
||||||
|
{
|
||||||
|
printf ("left == %d\n", left);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_getspecific (key1) != NULL)
|
||||||
|
{
|
||||||
|
puts ("key1 data != NULL");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (pthread_getspecific (key2) != NULL)
|
||||||
|
{
|
||||||
|
puts ("key2 data != NULL");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define TEST_FUNCTION do_test ()
|
||||||
|
#include "../test-skeleton.c"
|
Loading…
Reference in New Issue