From owner-freebsd-threads@FreeBSD.ORG Thu Jun 11 02:10:02 2009 Return-Path: Delivered-To: freebsd-threads@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 7B8C01065676 for ; Thu, 11 Jun 2009 02:10:02 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 55FF78FC16 for ; Thu, 11 Jun 2009 02:10:02 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.3/8.14.3) with ESMTP id n5B2A2wo041807 for ; Thu, 11 Jun 2009 02:10:02 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.3/8.14.3/Submit) id n5B2A2KZ041806; Thu, 11 Jun 2009 02:10:02 GMT (envelope-from gnats) Resent-Date: Thu, 11 Jun 2009 02:10:02 GMT Resent-Message-Id: <200906110210.n5B2A2KZ041806@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-threads@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Marc Unangst Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id C7D5A1065686 for ; Thu, 11 Jun 2009 02:03:52 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id B52188FC0A for ; Thu, 11 Jun 2009 02:03:52 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.3/8.14.3) with ESMTP id n5B23qM6059128 for ; Thu, 11 Jun 2009 02:03:52 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.3/8.14.3/Submit) id n5B23qJ8059126; Thu, 11 Jun 2009 02:03:52 GMT (envelope-from nobody) Message-Id: <200906110203.n5B23qJ8059126@www.freebsd.org> Date: Thu, 11 Jun 2009 02:03:52 GMT From: Marc Unangst To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: threads/135462: [PATCH] _thread_cleanupspecific() doesn't handle deleted keys X-BeenThere: freebsd-threads@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Threading on FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 11 Jun 2009 02:10:02 -0000 >Number: 135462 >Category: threads >Synopsis: [PATCH] _thread_cleanupspecific() doesn't handle deleted keys >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-threads >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Thu Jun 11 02:10:02 UTC 2009 >Closed-Date: >Last-Modified: >Originator: Marc Unangst >Release: FreeBSD 7.0-RELEASE amd64 >Organization: Panasas, Inc. >Environment: FreeBSD perf-x4 7.0-RELEASE FreeBSD 7.0-RELEASE #0: Sun Feb 24 10:35:36 UTC 2008 root@driscoll.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64 >Description: Programs that use thread-specific data occasionally exit with a warning like this: Thread 801202c70 has exited with leftover thread-specific data after 4 destructor iterations This occurs because the thread-specific data cleanup routine that runs when a thread exits (_thread_cleanupspecific()) doesn't handle cleaning up keys that were deleted via pthread_key_delete() while the exiting thread still had a non-NULL value set for that key. The test is if (_thread_keytable[key].allocated && (curthread->specific[key].data != NULL)) { i.e., if the key exists globally and the current thread has a non-NULL key. However, POSIX allows the application to delete the key without clearing the value in all threads; http://www.opengroup.org/onlinepubs/009695399/functions/pthread_key_delete.html says "The thread-specific data values associated with key need not be NULL at the time pthread_key_delete() is called." POSIX also specifies that in this case, the destructor routine (if one is defined) is not invoked. In this scenario, the cleanup routine should just set the thread-specific data value for that key to NULL and decrement curthread->specific_data_count. >How-To-Repeat: Compile and run the following program: /* * bug_93732.c * * @author mju * * Unit test for bug 93732. * * Compile like this: * gcc -Wall -pthread -o bug_93732 bug_93732.c */ #include #include #include #include #include void *test_thread(void *arg_ignored); int main(int argc, char **argv) { int ret; pthread_t thr; ret = pthread_create(&thr, NULL, test_thread, NULL); if (ret != 0) { fprintf(stderr, "pthread_create failed: %s\n", strerror(errno)); exit(1); } printf("Thread created, waiting for exit\n"); (void) pthread_join(thr, NULL); printf("Thread terminated, exiting\n"); exit(0); } void *test_thread(void *arg_ignored) { int ret; pthread_key_t key; int i = 2; ret = pthread_key_create(&key, NULL); if (ret != 0) { fprintf(stderr, "pthread_key_create failed: %s\n", strerror(errno)); exit(1); } printf("Created key %d\n", (int) key); ret = pthread_setspecific(key, &i); if (ret != 0) { fprintf(stderr, "pthread_setspecific failed: %s\n", strerror(errno)); exit(1); } printf("Set key to %p\n", &i); ret = pthread_key_delete(key); if (ret != 0) { fprintf(stderr, "pthread_key_delete failed: %s\n", strerror(errno)); exit(1); } printf("Deleted key %d\n", (int) key); return NULL; } >Fix: See attached patch file. Patch attached with submission follows: --- /net/paneast/sb7/mju/freebsd-svn/head/lib/libthr/thread/thr_spec.c 2009-05-26 17:45:27.000000000 -0400 +++ thr_spec.c 2009-06-10 14:57:06.000000000 -0400 @@ -131,9 +131,19 @@ curthread->specific[key].data = NULL; curthread->specific_data_count--; } + else if (curthread->specific[key].data != NULL) { + /* + * This can happen if the key is deleted via + * pthread_key_delete without first setting the value + * to NULL in all threads. POSIX says that the + * destructor is not invoked in this case. + */ + curthread->specific[key].data = NULL; + curthread->specific_data_count--; + } /* - * If there is a destructore, call it + * If there is a destructor, call it * with the key table entry unlocked: */ if (destructor != NULL) { >Release-Note: >Audit-Trail: >Unformatted: