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:
Ulrich Drepper 2003-04-02 03:51:24 +00:00
parent fa36efe893
commit 6b4686a534
5 changed files with 210 additions and 54 deletions

View File

@ -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):

View File

@ -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 \

View File

@ -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 ();

View File

@ -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)

129
nptl/tst-tsd3.c Normal file
View File

@ -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"