Remove the dual-callback scheme for numeric and pointer thread IDs,
deprecate the original (numeric-only) scheme, and replace with the
CRYPTO_THREADID object. This hides the platform-specifics and should reduce
the possibility for programming errors (where failing to explicitly check
both thread ID forms could create subtle, platform-specific bugs).

Thanks to Bodo, for invaluable review and feedback.
diff --git a/CHANGES b/CHANGES
index 35bd37f..9b524dd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,30 @@
 
  Changes between 0.9.8i and 0.9.9  [xx XXX xxxx]
 
+  *) To cater for systems that provide a pointer-based thread ID rather
+     than numeric, deprecate the current numeric thread ID mechanism and
+     replace it with a structure and associated callback type. This
+     mechanism allows a numeric "hash" to be extracted from a thread ID in
+     either case, and on platforms where pointers are larger than 'long',
+     mixing is done to help ensure the numeric 'hash' is usable even if it
+     can't be guaranteed unique. The default mechanism is to use "&errno"
+     as a pointer-based thread ID to distinguish between threads.
+
+     Applications that want to provide their own thread IDs should now use
+     CRYPTO_THREADID_set_callback() to register a callback that will call
+     either CRYPTO_THREADID_set_numeric() or CRYPTO_THREADID_set_pointer().
+
+     (This new approach replaces the functions CRYPTO_set_idptr_callback(),
+     CRYPTO_get_idptr_callback(), and CRYPTO_thread_idptr() that existed in
+     OpenSSL 0.9.9-dev between June 2006 and August 2008. Also, if an
+     application was previously providing a numeric thread callback that
+     was inappropriate for distinguishing threads, then uniqueness might
+     have been obtained with &errno that happened immediately in the
+     intermediate development versions of OpenSSL; this is no longer the
+     case, the numeric thread callback will now override the automatic use
+     of &errno.)
+     [Geoff Thorpe, with help from Bodo Moeller]
+
   *) Initial support for different CRL issuing certificates. This covers a
      simple case where the self issued certificates in the chain exist and
      the real CRL issuer is higher in the existing chain.
@@ -307,30 +331,6 @@
      list-message-digest-algorithms and list-cipher-algorithms.
      [Steve Henson]
 
-  *) In addition to the numerical (unsigned long) thread ID, provide
-     for a pointer (void *) thread ID.  This helps accomodate systems
-     that do not provide an unsigned long thread ID.  OpenSSL assumes
-     it is in the same thread iff both the numerical and the pointer
-     thread ID agree; so applications are just required to define one
-     of them appropriately (e.g., by using a pointer to a per-thread
-     memory object malloc()ed by the application for the pointer-type
-     thread ID).  Exactly analoguous to the existing functions
-
-        void CRYPTO_set_id_callback(unsigned long (*func)(void));
-        unsigned long (*CRYPTO_get_id_callback(void))(void);
-        unsigned long CRYPTO_thread_id(void);
-
-     we now have additional functions
-
-        void CRYPTO_set_idptr_callback(void *(*func)(void));
-        void *(*CRYPTO_get_idptr_callback(void))(void);
-        void *CRYPTO_thread_idptr(void);
-
-     also in <openssl/crypto.h>.  The default value for
-     CRYPTO_thread_idptr() if the application has not provided its own
-     callback is &errno.
-     [Bodo Moeller]
-
   *) Change the array representation of binary polynomials: the list
      of degrees of non-zero coefficients is now terminated with -1.
      Previously it was terminated with 0, which was also part of the
diff --git a/FAQ b/FAQ
index 60a527f..fb997b6 100644
--- a/FAQ
+++ b/FAQ
@@ -717,9 +717,7 @@
 
 Multi-threaded applications must provide two callback functions to
 OpenSSL by calling CRYPTO_set_locking_callback() and
-CRYPTO_set_id_callback().  (For OpenSSL 0.9.9 or later, the new
-function CRYPTO_set_idptr_callback() may be used in place of
-CRYPTO_set_id_callback().)  This is described in the threads(3)
+CRYPTO_set_id_callback().  This is described in the threads(3)
 manpage.
 
 * I've compiled a program under Windows and it crashes: why?
diff --git a/apps/apps.h b/apps/apps.h
index 93d8749..bcf597f 100644
--- a/apps/apps.h
+++ b/apps/apps.h
@@ -181,7 +181,7 @@
 #    define apps_shutdown() \
 			do { CONF_modules_unload(1); destroy_ui_method(); \
 			OBJ_cleanup(); EVP_cleanup(); ENGINE_cleanup(); \
-			CRYPTO_cleanup_all_ex_data(); ERR_remove_state(0); \
+			CRYPTO_cleanup_all_ex_data(); ERR_remove_thread_state(NULL); \
 			ERR_free_strings(); COMP_zlib_cleanup();} while(0)
 #  else
 #    define apps_startup() \
@@ -191,7 +191,7 @@
 #    define apps_shutdown() \
 			do { CONF_modules_unload(1); destroy_ui_method(); \
 			OBJ_cleanup(); EVP_cleanup(); \
-			CRYPTO_cleanup_all_ex_data(); ERR_remove_state(0); \
+			CRYPTO_cleanup_all_ex_data(); ERR_remove_thread_state(NULL); \
 			ERR_free_strings(); } while(0)
 #  endif
 #endif
diff --git a/crypto/bn/bn.h b/crypto/bn/bn.h
index de2cfcf..4749c52 100644
--- a/crypto/bn/bn.h
+++ b/crypto/bn/bn.h
@@ -130,6 +130,7 @@
 #include <stdio.h> /* FILE */
 #endif
 #include <openssl/ossl_typ.h>
+#include <openssl/crypto.h>
 
 #ifdef  __cplusplus
 extern "C" {
@@ -564,10 +565,11 @@
 int BN_BLINDING_invert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx);
 int BN_BLINDING_convert_ex(BIGNUM *n, BIGNUM *r, BN_BLINDING *b, BN_CTX *);
 int BN_BLINDING_invert_ex(BIGNUM *n, const BIGNUM *r, BN_BLINDING *b, BN_CTX *);
+#ifndef OPENSSL_NO_DEPRECATED
 unsigned long BN_BLINDING_get_thread_id(const BN_BLINDING *);
 void BN_BLINDING_set_thread_id(BN_BLINDING *, unsigned long);
-void *BN_BLINDING_get_thread_idptr(const BN_BLINDING *);
-void BN_BLINDING_set_thread_idptr(BN_BLINDING *, void *);
+#endif
+CRYPTO_THREADID *BN_BLINDING_thread_id(BN_BLINDING *);
 unsigned long BN_BLINDING_get_flags(const BN_BLINDING *);
 void BN_BLINDING_set_flags(BN_BLINDING *, unsigned long);
 BN_BLINDING *BN_BLINDING_create_param(BN_BLINDING *b,
diff --git a/crypto/bn/bn_blind.c b/crypto/bn/bn_blind.c
index e9b6173..e060592 100644
--- a/crypto/bn/bn_blind.c
+++ b/crypto/bn/bn_blind.c
@@ -121,10 +121,11 @@
 	BIGNUM *Ai;
 	BIGNUM *e;
 	BIGNUM *mod; /* just a reference */
+#ifndef OPENSSL_NO_DEPRECATED
 	unsigned long thread_id; /* added in OpenSSL 0.9.6j and 0.9.7b;
 				  * used only by crypto/rsa/rsa_eay.c, rsa_lib.c */
-	void *thread_idptr; /* added in OpenSSL 0.9.9;
-			     * used only by crypto/rsa/rsa_eay.c, rsa_lib.c */
+#endif
+	CRYPTO_THREADID tid;
 	unsigned int  counter;
 	unsigned long flags;
 	BN_MONT_CTX *m_ctx;
@@ -160,6 +161,7 @@
 		BN_set_flags(ret->mod, BN_FLG_CONSTTIME);
 
 	ret->counter = BN_BLINDING_COUNTER;
+	CRYPTO_THREADID_current(&ret->tid);
 	return(ret);
 err:
 	if (ret != NULL) BN_BLINDING_free(ret);
@@ -265,6 +267,7 @@
 	return(ret);
 	}
 
+#ifndef OPENSSL_NO_DEPRECATED
 unsigned long BN_BLINDING_get_thread_id(const BN_BLINDING *b)
 	{
 	return b->thread_id;
@@ -274,15 +277,11 @@
 	{
 	b->thread_id = n;
 	}
+#endif
 
-void *BN_BLINDING_get_thread_idptr(const BN_BLINDING *b)
+CRYPTO_THREADID *BN_BLINDING_thread_id(BN_BLINDING *b)
 	{
-	return b->thread_idptr;
-	}
-
-void BN_BLINDING_set_thread_idptr(BN_BLINDING *b, void *p)
-	{
-	b->thread_idptr = p;
+	return &b->tid;
 	}
 
 unsigned long BN_BLINDING_get_flags(const BN_BLINDING *b)
diff --git a/crypto/bn/exptest.c b/crypto/bn/exptest.c
index 89ebcc8..074a8e8 100644
--- a/crypto/bn/exptest.c
+++ b/crypto/bn/exptest.c
@@ -187,7 +187,7 @@
 	BN_free(b);
 	BN_free(m);
 	BN_CTX_free(ctx);
-	ERR_remove_state(0);
+	ERR_remove_thread_state(NULL);
 	CRYPTO_mem_leaks(out);
 	BIO_free(out);
 	printf(" done\n");
diff --git a/crypto/cryptlib.c b/crypto/cryptlib.c
index b89bd7a..fe5d306 100644
--- a/crypto/cryptlib.c
+++ b/crypto/cryptlib.c
@@ -184,8 +184,10 @@
 	const char *file,int line)=0;
 static int (MS_FAR *add_lock_callback)(int *pointer,int amount,
 	int type,const char *file,int line)=0;
+#ifndef OPENSSL_NO_DEPRECATED
 static unsigned long (MS_FAR *id_callback)(void)=0;
-static void *(MS_FAR *idptr_callback)(void)=0;
+#endif
+static void (MS_FAR *threadid_callback)(CRYPTO_THREADID *)=0;
 static struct CRYPTO_dynlock_value *(MS_FAR *dynlock_create_callback)
 	(const char *file,int line)=0;
 static void (MS_FAR *dynlock_lock_callback)(int mode,
@@ -414,6 +416,108 @@
 	add_lock_callback=func;
 	}
 
+/* the memset() here and in set_pointer() seem overkill, but for the sake of
+ * CRYPTO_THREADID_cmp() this avoids any platform silliness that might cause two
+ * "equal" THREADID structs to not be memcmp()-identical. */
+void CRYPTO_THREADID_set_numeric(CRYPTO_THREADID *id, unsigned long val)
+	{
+	memset(id, 0, sizeof(*id));
+	id->val = val;
+	}
+
+static const unsigned char hash_coeffs[] = { 3, 5, 7, 11, 13, 17, 19, 23 };
+void CRYPTO_THREADID_set_pointer(CRYPTO_THREADID *id, void *ptr)
+	{
+	unsigned char *dest = (void *)&id->val;
+	unsigned int accum = 0;
+	unsigned char dnum = sizeof(id->val);
+
+	memset(id, 0, sizeof(*id));
+	id->ptr = ptr;
+	if (sizeof(id->val) >= sizeof(id->ptr))
+		{
+		/* 'ptr' can be embedded in 'val' without loss of uniqueness */
+		id->val = (unsigned long)id->ptr;
+		return;
+		}
+	/* hash ptr ==> val. Each byte of 'val' gets the mod-256 total of a
+	 * linear function over the bytes in 'ptr', the co-efficients of which
+	 * are a sequence of low-primes (hash_coeffs is an 8-element cycle) -
+	 * the starting prime for the sequence varies for each byte of 'val'
+	 * (unique polynomials unless pointers are >64-bit). For added spice,
+	 * the totals accumulate rather than restarting from zero, and the index
+	 * of the 'val' byte is added each time (position dependence). If I was
+	 * a black-belt, I'd scan big-endian pointers in reverse to give
+	 * low-order bits more play, but this isn't crypto and I'd prefer nobody
+	 * mistake it as such. Plus I'm lazy. */
+	while (dnum--)
+		{
+		const unsigned char *src = (void *)&id->ptr;
+		unsigned char snum = sizeof(id->ptr);
+		while (snum--)
+			accum += *(src++) * hash_coeffs[(snum + dnum) & 7];
+		accum += dnum;
+		*(dest++) = accum & 255;
+		}
+	}
+
+int CRYPTO_THREADID_set_callback(void (*func)(CRYPTO_THREADID *))
+	{
+	if (threadid_callback)
+		return 0;
+	threadid_callback = func;
+	return 1;
+	}
+
+void (*CRYPTO_THREADID_get_callback(void))(CRYPTO_THREADID *)
+	{
+	return threadid_callback;
+	}
+
+void CRYPTO_THREADID_current(CRYPTO_THREADID *id)
+	{
+	if (threadid_callback)
+		{
+		threadid_callback(id);
+		return;
+		}
+#ifndef OPENSSL_NO_DEPRECATED
+	/* If the deprecated callback was set, fall back to that */
+	if (id_callback)
+		{
+		CRYPTO_THREADID_set_numeric(id, id_callback());
+		return;
+		}
+#endif
+	/* Else pick a backup */
+#ifdef OPENSSL_SYS_WIN16
+	CRYPTO_THREADID_set_numeric(id, (unsigned long)GetCurrentTask());
+#elif defined(OPENSSL_SYS_WIN32)
+	CRYPTO_THREADID_set_numeric(id, (unsigned long)GetCurrentThreadId());
+#elif defined(OPENSSL_SYS_BEOS)
+	CRYPTO_THREADID_set_numeric(id, (unsigned long)find_thread(NULL));
+#else
+	/* For everything else, default to using the address of 'errno' */
+	CRYPTO_THREADID_set_pointer(id, &errno);
+#endif
+	}
+
+int CRYPTO_THREADID_cmp(const CRYPTO_THREADID *a, const CRYPTO_THREADID *b)
+	{
+	return memcmp(a, b, sizeof(*a));
+	}
+
+void CRYPTO_THREADID_cpy(CRYPTO_THREADID *dest, const CRYPTO_THREADID *src)
+	{
+	memcpy(dest, src, sizeof(*src));
+	}
+
+unsigned long CRYPTO_THREADID_hash(const CRYPTO_THREADID *id)
+	{
+	return id->val;
+	}
+
+#ifndef OPENSSL_NO_DEPRECATED
 unsigned long (*CRYPTO_get_id_callback(void))(void)
 	{
 	return(id_callback);
@@ -446,33 +550,13 @@
 		ret=id_callback();
 	return(ret);
 	}
-
-void *(*CRYPTO_get_idptr_callback(void))(void)
-	{
-	return(idptr_callback);
-	}
-
-void CRYPTO_set_idptr_callback(void *(*func)(void))
-	{
-	idptr_callback=func;
-	}
-
-void *CRYPTO_thread_idptr(void)
-	{
-	void *ret=NULL;
-
-	if (idptr_callback == NULL)
-		ret = &errno;
-	else
-		ret = idptr_callback();
-
-	return ret;
-	}
+#endif
 
 void CRYPTO_lock(int mode, int type, const char *file, int line)
 	{
 #ifdef LOCK_DEBUG
 		{
+		CRYPTO_THREADID id;
 		char *rw_text,*operation_text;
 
 		if (mode & CRYPTO_LOCK)
@@ -489,8 +573,9 @@
 		else
 			rw_text="ERROR";
 
-		fprintf(stderr,"lock:%08lx/%08p:(%s)%s %-18s %s:%d\n",
-			CRYPTO_thread_id(), CRYPTO_thread_idptr(), rw_text, operation_text,
+		CRYPTO_THREADID_current(&id);
+		fprintf(stderr,"lock:%08lx:(%s)%s %-18s %s:%d\n",
+			CRYPTO_THREADID_hash(&id), rw_text, operation_text,
 			CRYPTO_get_lock_name(type), file, line);
 		}
 #endif
@@ -526,11 +611,14 @@
 
 		ret=add_lock_callback(pointer,amount,type,file,line);
 #ifdef LOCK_DEBUG
-		fprintf(stderr,"ladd:%08lx/%0xp:%2d+%2d->%2d %-18s %s:%d\n",
-			CRYPTO_thread_id(), CRYPTO_thread_idptr(),
-			before,amount,ret,
+		{
+		CRYPTO_THREADID id;
+		CRYPTO_THREADID_current(&id);
+		fprintf(stderr,"ladd:%08lx:%2d+%2d->%2d %-18s %s:%d\n",
+			CRYPTO_THREADID_hash(&id), before,amount,ret,
 			CRYPTO_get_lock_name(type),
 			file,line);
+		}
 #endif
 		}
 	else
@@ -539,11 +627,15 @@
 
 		ret= *pointer+amount;
 #ifdef LOCK_DEBUG
-		fprintf(stderr,"ladd:%08lx/%0xp:%2d+%2d->%2d %-18s %s:%d\n",
-			CRYPTO_thread_id(), CRYPTO_thread_idptr(),
+		{
+		CRYPTO_THREADID id;
+		CRYPTO_THREADID_current(&id);
+		fprintf(stderr,"ladd:%08lx:%2d+%2d->%2d %-18s %s:%d\n",
+			CRYPTO_THREADID_hash(&id),
 			*pointer,amount,ret,
 			CRYPTO_get_lock_name(type),
 			file,line);
+		}
 #endif
 		*pointer=ret;
 		CRYPTO_lock(CRYPTO_UNLOCK|CRYPTO_WRITE,type,file,line);
diff --git a/crypto/crypto.h b/crypto/crypto.h
index 8bc927b..bcc8ca4 100644
--- a/crypto/crypto.h
+++ b/crypto/crypto.h
@@ -421,12 +421,27 @@
 					      const char *file, int line));
 int (*CRYPTO_get_add_lock_callback(void))(int *num,int mount,int type,
 					  const char *file,int line);
+
+/* Don't use this structure directly. */
+typedef struct crypto_threadid_st
+	{
+	void *ptr;
+	unsigned long val;
+	} CRYPTO_THREADID;
+/* Only use CRYPTO_THREADID_set_[numeric|pointer]() within callbacks */
+void CRYPTO_THREADID_set_numeric(CRYPTO_THREADID *id, unsigned long val);
+void CRYPTO_THREADID_set_pointer(CRYPTO_THREADID *id, void *ptr);
+int CRYPTO_THREADID_set_callback(void (*threadid_func)(CRYPTO_THREADID *));
+void (*CRYPTO_THREADID_get_callback(void))(CRYPTO_THREADID *);
+void CRYPTO_THREADID_current(CRYPTO_THREADID *id);
+int CRYPTO_THREADID_cmp(const CRYPTO_THREADID *a, const CRYPTO_THREADID *b);
+void CRYPTO_THREADID_cpy(CRYPTO_THREADID *dest, const CRYPTO_THREADID *src);
+unsigned long CRYPTO_THREADID_hash(const CRYPTO_THREADID *id);
+#ifndef OPENSSL_NO_DEPRECATED
 void CRYPTO_set_id_callback(unsigned long (*func)(void));
 unsigned long (*CRYPTO_get_id_callback(void))(void);
 unsigned long CRYPTO_thread_id(void);
-void CRYPTO_set_idptr_callback(void *(*func)(void));
-void *(*CRYPTO_get_idptr_callback(void))(void);
-void *CRYPTO_thread_idptr(void);
+#endif
 
 const char *CRYPTO_get_lock_name(int type);
 int CRYPTO_add_lock(int *pointer,int amount,int type, const char *file,
diff --git a/crypto/dsa/dsatest.c b/crypto/dsa/dsatest.c
index 5a699ca..edffd24 100644
--- a/crypto/dsa/dsatest.c
+++ b/crypto/dsa/dsatest.c
@@ -222,7 +222,7 @@
 		ERR_print_errors(bio_err);
 	if (dsa != NULL) DSA_free(dsa);
 	CRYPTO_cleanup_all_ex_data();
-	ERR_remove_state(0);
+	ERR_remove_thread_state(NULL);
 	ERR_free_strings();
 	CRYPTO_mem_leaks(bio_err);
 	if (bio_err != NULL)
diff --git a/crypto/ec/ectest.c b/crypto/ec/ectest.c
index b74d643..7509cb9 100644
--- a/crypto/ec/ectest.c
+++ b/crypto/ec/ectest.c
@@ -1326,7 +1326,7 @@
 #endif
 	CRYPTO_cleanup_all_ex_data();
 	ERR_free_strings();
-	ERR_remove_state(0);
+	ERR_remove_thread_state(NULL);
 	CRYPTO_mem_leaks_fp(stderr);
 	
 	return 0;
diff --git a/crypto/ecdh/ecdhtest.c b/crypto/ecdh/ecdhtest.c
index 1575006..212a87e 100644
--- a/crypto/ecdh/ecdhtest.c
+++ b/crypto/ecdh/ecdhtest.c
@@ -343,7 +343,7 @@
 	if (ctx) BN_CTX_free(ctx);
 	BIO_free(out);
 	CRYPTO_cleanup_all_ex_data();
-	ERR_remove_state(0);
+	ERR_remove_thread_state(NULL);
 	CRYPTO_mem_leaks_fp(stderr);
 	EXIT(ret);
 	return(ret);
diff --git a/crypto/ecdsa/ecdsatest.c b/crypto/ecdsa/ecdsatest.c
index b07e312..aa4e148 100644
--- a/crypto/ecdsa/ecdsatest.c
+++ b/crypto/ecdsa/ecdsatest.c
@@ -490,7 +490,7 @@
 	if (ret)
 		ERR_print_errors(out);
 	CRYPTO_cleanup_all_ex_data();
-	ERR_remove_state(0);
+	ERR_remove_thread_state(NULL);
 	ERR_free_strings();
 	CRYPTO_mem_leaks(out);
 	if (out != NULL)
diff --git a/crypto/engine/enginetest.c b/crypto/engine/enginetest.c
index cf82f49..3c1d2b4 100644
--- a/crypto/engine/enginetest.c
+++ b/crypto/engine/enginetest.c
@@ -276,7 +276,7 @@
 	ENGINE_cleanup();
 	CRYPTO_cleanup_all_ex_data();
 	ERR_free_strings();
-	ERR_remove_state(0);
+	ERR_remove_thread_state(NULL);
 	CRYPTO_mem_leaks_fp(stderr);
 	return to_return;
 	}
diff --git a/crypto/err/err.c b/crypto/err/err.c
index f615cb4..69713a6 100644
--- a/crypto/err/err.c
+++ b/crypto/err/err.c
@@ -429,13 +429,13 @@
 
 static unsigned long err_state_hash(const ERR_STATE *a)
 	{
-	return (a->pid + (unsigned long)a->pidptr) * 13;
+	return CRYPTO_THREADID_hash(&a->tid) * 13;
 	}
 static IMPLEMENT_LHASH_HASH_FN(err_state, ERR_STATE)
 
 static int err_state_cmp(const ERR_STATE *a, const ERR_STATE *b)
 	{
-	return (a->pid != b->pid) || (a->pidptr != b->pidptr);
+	return CRYPTO_THREADID_cmp(&a->tid, &b->tid);
 	}
 static IMPLEMENT_LHASH_COMP_FN(err_state, ERR_STATE)
 
@@ -980,40 +980,37 @@
 	return((p == NULL)?NULL:p->string);
 	}
 
-void ERR_remove_state(unsigned long pid)
+void ERR_remove_thread_state(const CRYPTO_THREADID *id)
 	{
 	ERR_STATE tmp;
-	void *pidptr;
 
-	err_fns_check();
-	if (pid != 0)
-		pidptr = &errno;
+	if (id)
+		CRYPTO_THREADID_cpy(&tmp.tid, id);
 	else
-		{
-		pid = CRYPTO_thread_id();
-		pidptr = CRYPTO_thread_idptr();
-		}
-	
-	tmp.pid=pid;
-	tmp.pidptr=pidptr;
+		CRYPTO_THREADID_current(&tmp.tid);
+	err_fns_check();
 	/* thread_del_item automatically destroys the LHASH if the number of
 	 * items reaches zero. */
 	ERRFN(thread_del_item)(&tmp);
 	}
 
+#ifndef OPENSSL_NO_DEPRECATED
+void ERR_remove_state(unsigned long pid)
+	{
+	ERR_remove_thread_state(NULL);
+	}
+#endif
+
 ERR_STATE *ERR_get_state(void)
 	{
 	static ERR_STATE fallback;
 	ERR_STATE *ret,tmp,*tmpp=NULL;
 	int i;
-	unsigned long pid;
-	void *pidptr;
+	CRYPTO_THREADID tid;
 
 	err_fns_check();
-	pid = CRYPTO_thread_id();
-	pidptr = CRYPTO_thread_idptr();
-	tmp.pid = pid;
-	tmp.pidptr = pidptr;
+	CRYPTO_THREADID_current(&tid);
+	CRYPTO_THREADID_cpy(&tmp.tid, &tid);
 	ret=ERRFN(thread_get_item)(&tmp);
 
 	/* ret == the error state, if NULL, make a new one */
@@ -1021,8 +1018,7 @@
 		{
 		ret=(ERR_STATE *)OPENSSL_malloc(sizeof(ERR_STATE));
 		if (ret == NULL) return(&fallback);
-		ret->pid=pid;
-		ret->pidptr=pidptr;
+		CRYPTO_THREADID_cpy(&ret->tid, &tid);
 		ret->top=0;
 		ret->bottom=0;
 		for (i=0; i<ERR_NUM_ERRORS; i++)
diff --git a/crypto/err/err.h b/crypto/err/err.h
index 942f820..006351d 100644
--- a/crypto/err/err.h
+++ b/crypto/err/err.h
@@ -147,8 +147,7 @@
 #define ERR_NUM_ERRORS	16
 typedef struct err_state_st
 	{
-	unsigned long pid;
-	void *pidptr; /* new in OpenSSL 0.9.9 */
+	CRYPTO_THREADID tid;
 	int err_flags[ERR_NUM_ERRORS];
 	unsigned long err_buffer[ERR_NUM_ERRORS];
 	char *err_data[ERR_NUM_ERRORS];
@@ -351,7 +350,10 @@
 void ERR_load_crypto_strings(void);
 void ERR_free_strings(void);
 
+void ERR_remove_thread_state(const CRYPTO_THREADID *tid);
+#ifndef OPENSSL_NO_DEPRECATED
 void ERR_remove_state(unsigned long pid); /* if zero we look it up */
+#endif
 ERR_STATE *ERR_get_state(void);
 
 #ifndef OPENSSL_NO_LHASH
diff --git a/crypto/err/err_prn.c b/crypto/err/err_prn.c
index 2224a90..de32f33 100644
--- a/crypto/err/err_prn.c
+++ b/crypto/err/err_prn.c
@@ -72,8 +72,10 @@
 	const char *file,*data;
 	int line,flags;
 	unsigned long es;
+	CRYPTO_THREADID cur;
 
-	es=CRYPTO_thread_id();
+	CRYPTO_THREADID_current(&cur);
+	es=CRYPTO_THREADID_hash(&cur);
 	while ((l=ERR_get_error_line_data(&file,&line,&data,&flags)) != 0)
 		{
 		ERR_error_string_n(l, buf, sizeof buf);
diff --git a/crypto/evp/evp_test.c b/crypto/evp/evp_test.c
index bb6f02c..b8c9235 100644
--- a/crypto/evp/evp_test.c
+++ b/crypto/evp/evp_test.c
@@ -441,7 +441,7 @@
 #endif
     EVP_cleanup();
     CRYPTO_cleanup_all_ex_data();
-    ERR_remove_state(0);
+    ERR_remove_thread_state(NULL);
     ERR_free_strings();
     CRYPTO_mem_leaks_fp(stderr);
 
diff --git a/crypto/mem_dbg.c b/crypto/mem_dbg.c
index ad5209b..131669a 100644
--- a/crypto/mem_dbg.c
+++ b/crypto/mem_dbg.c
@@ -150,8 +150,7 @@
  *   CRYPTO_remove_all_info()    to pop all entries.
  */
 	{
-	unsigned long thread_id;
-	void *thread_idptr;
+	CRYPTO_THREADID threadid;
 	const char *file;
 	int line;
 	const char *info;
@@ -176,8 +175,7 @@
 	int num;
 	const char *file;
 	int line;
-	unsigned long thread_id;
-	void *thread_idptr;
+	CRYPTO_THREADID threadid;
 	unsigned long order;
 	time_t time;
 	APP_INFO *app_info;
@@ -198,12 +196,10 @@
                                       * mh_mode == CRYPTO_MEM_CHECK_ON (w/o ..._ENABLE)
                                       */
 
-/* The following two variables, disabling_thread_id and disabling_thread_idptr,
- * are valid iff num_disable > 0.  CRYPTO_LOCK_MALLOC2 is locked exactly in
- * this case (by the thread named in disabling_thread_id / disabling_thread_idptr).
+/* Valid iff num_disable > 0.  CRYPTO_LOCK_MALLOC2 is locked exactly in this
+ * case (by the thread named in disabling_thread).
  */
-static unsigned long disabling_thread_id = 0;
-static void *disabling_thread_idptr = NULL;
+static CRYPTO_THREADID disabling_threadid;
 
 static void app_info_free(APP_INFO *inf)
 	{
@@ -240,9 +236,9 @@
 	case CRYPTO_MEM_CHECK_DISABLE: /* aka MemCheck_off() */
 		if (mh_mode & CRYPTO_MEM_CHECK_ON)
 			{
-			if (!num_disable
-			    || (disabling_thread_id != CRYPTO_thread_id())
-			    || (disabling_thread_idptr != CRYPTO_thread_idptr())) /* otherwise we already have the MALLOC2 lock */
+			CRYPTO_THREADID cur;
+			CRYPTO_THREADID_current(&cur);
+			if (!num_disable || CRYPTO_THREADID_cmp(&disabling_threadid, &cur)) /* otherwise we already have the MALLOC2 lock */
 				{
 				/* Long-time lock CRYPTO_LOCK_MALLOC2 must not be claimed while
 				 * we're holding CRYPTO_LOCK_MALLOC, or we'll deadlock if
@@ -260,8 +256,7 @@
 				CRYPTO_w_lock(CRYPTO_LOCK_MALLOC2);
 				CRYPTO_w_lock(CRYPTO_LOCK_MALLOC);
 				mh_mode &= ~CRYPTO_MEM_CHECK_ENABLE;
-				disabling_thread_id=CRYPTO_thread_id();
-				disabling_thread_idptr=CRYPTO_thread_idptr();
+				CRYPTO_THREADID_cpy(&disabling_threadid, &cur);
 				}
 			num_disable++;
 			}
@@ -294,11 +289,12 @@
 
 	if (mh_mode & CRYPTO_MEM_CHECK_ON)
 		{
+		CRYPTO_THREADID cur;
+		CRYPTO_THREADID_current(&cur);
 		CRYPTO_r_lock(CRYPTO_LOCK_MALLOC);
 
 		ret = (mh_mode & CRYPTO_MEM_CHECK_ENABLE)
-		        || (disabling_thread_id != CRYPTO_thread_id())
-		        || (disabling_thread_idptr != CRYPTO_thread_idptr());
+		        || CRYPTO_THREADID_cmp(&disabling_threadid, &cur);
 
 		CRYPTO_r_unlock(CRYPTO_LOCK_MALLOC);
 		}
@@ -344,20 +340,17 @@
 /* static int app_info_cmp(APP_INFO *a, APP_INFO *b) */
 static int app_info_cmp(const void *a_void, const void *b_void)
 	{
-	return (((const APP_INFO *)a_void)->thread_id != ((const APP_INFO *)b_void)->thread_id)
-	       || (((const APP_INFO *)a_void)->thread_idptr != ((const APP_INFO *)b_void)->thread_idptr);
+	return CRYPTO_THREADID_cmp(&((const APP_INFO *)a_void)->threadid,
+				&((const APP_INFO *)b_void)->threadid);
 	}
 static IMPLEMENT_LHASH_COMP_FN(app_info, APP_INFO)
 
 static unsigned long app_info_hash(const APP_INFO *a)
 	{
-	unsigned long id1, id2;
 	unsigned long ret;
 
-	id1=(unsigned long)a->thread_id;
-	id2=(unsigned long)a->thread_idptr;
-	ret = id1 + id2;
-
+	ret = CRYPTO_THREADID_hash(&a->threadid);
+	/* This is left in as a "who am I to question legacy?" measure */
 	ret=ret*17851+(ret>>14)*7+(ret>>4)*251;
 	return(ret);
 	}
@@ -370,8 +363,7 @@
 
 	if (amih != NULL)
 		{
-		tmp.thread_id=CRYPTO_thread_id();
-		tmp.thread_idptr=CRYPTO_thread_idptr();
+		CRYPTO_THREADID_current(&tmp.threadid);
 		if ((ret=lh_APP_INFO_delete(amih,&tmp)) != NULL)
 			{
 			APP_INFO *next=ret->next;
@@ -382,10 +374,11 @@
 				(void)lh_APP_INFO_insert(amih,next);
 				}
 #ifdef LEVITTE_DEBUG_MEM
-			if (ret->thread_id != tmp.thread_id || ret->thread_idptr != tmp.thread_idptr)
+			if (CRYPTO_THREADID_cmp(&ret->threadid, &tmp.threadid))
 				{
-				fprintf(stderr, "pop_info(): deleted info has other thread ID (%lu/%p) than the current thread (%lu/%p)!!!!\n",
-					ret->thread_id, ret->thread_idptr, tmp.thread_id, tmp.thread_idptr);
+				fprintf(stderr, "pop_info(): deleted info has other thread ID (%lu) than the current thread (%lu)!!!!\n",
+					CRYPTO_THREADID_hash(&ret->threadid),
+					CRYPTO_THREADID_hash(&tmp.threadid));
 				abort();
 				}
 #endif
@@ -425,8 +418,7 @@
 				}
 			}
 
-		ami->thread_id=CRYPTO_thread_id();
-		ami->thread_idptr=CRYPTO_thread_idptr();
+		CRYPTO_THREADID_current(&ami->threadid);
 		ami->file=file;
 		ami->line=line;
 		ami->info=info;
@@ -436,10 +428,11 @@
 		if ((amim=lh_APP_INFO_insert(amih,ami)) != NULL)
 			{
 #ifdef LEVITTE_DEBUG_MEM
-			if (ami->thread_id != amim->thread_id || ami->thread_idptr != amim->thread_idptr)
+			if (CRYPTO_THREADID_cmp(&ami->threadid, &amim->threadid))
 				{
-				fprintf(stderr, "CRYPTO_push_info(): previous info has other thread ID (%lu/%p) than the current thread (%lu/%p)!!!!\n",
-					amim->thread_id, amim->thread_idptr, ami->thread_id, ami->thread_idptr);
+				fprintf(stderr, "CRYPTO_push_info(): previous info has other thread ID (%lu) than the current thread (%lu)!!!!\n",
+					CRYPTO_THREADID_hash(&amim->threadid),
+					CRYPTO_THREADID_hash(&ami->threadid));
 				abort();
 				}
 #endif
@@ -525,15 +518,9 @@
 			m->line=line;
 			m->num=num;
 			if (options & V_CRYPTO_MDEBUG_THREAD)
-				{
-				m->thread_id=CRYPTO_thread_id();
-				m->thread_idptr=CRYPTO_thread_idptr();
-				}
+				CRYPTO_THREADID_current(&m->threadid);
 			else
-				{
-				m->thread_id=0;
-				m->thread_idptr=NULL;
-				}
+				memset(&m->threadid, 0, sizeof(m->threadid));
 
 			if (order == break_order_num)
 				{
@@ -552,8 +539,7 @@
 			else
 				m->time=0;
 
-			tmp.thread_id=CRYPTO_thread_id();
-			tmp.thread_idptr=CRYPTO_thread_idptr();
+			CRYPTO_THREADID_current(&tmp.threadid);
 			m->app_info=NULL;
 			if (amih != NULL
 			    && (amim=lh_APP_INFO_retrieve(amih,&tmp)) != NULL)
@@ -682,8 +668,7 @@
 	APP_INFO *amip;
 	int ami_cnt;
 	struct tm *lcl = NULL;
-	unsigned long ti;
-	void *tip;
+	CRYPTO_THREADID ti;
 
 #define BUF_REMAIN (sizeof buf - (size_t)(bufp - buf))
 
@@ -705,7 +690,8 @@
 
 	if (options & V_CRYPTO_MDEBUG_THREAD)
 		{
-		BIO_snprintf(bufp, BUF_REMAIN, "thread=%lu/%p, ", m->thread_id, m->thread_idptr);
+		BIO_snprintf(bufp, BUF_REMAIN, "thread=%lu, ",
+			CRYPTO_THREADID_hash(&m->threadid));
 		bufp += strlen(bufp);
 		}
 
@@ -722,9 +708,8 @@
 	ami_cnt=0;
 	if (!amip)
 		return;
-	ti=amip->thread_id;
-	tip=amip->thread_idptr;
-	
+	CRYPTO_THREADID_cpy(&ti, &amip->threadid);
+
 	do
 		{
 		int buf_len;
@@ -733,8 +718,9 @@
 		ami_cnt++;
 		memset(buf,'>',ami_cnt);
 		BIO_snprintf(buf + ami_cnt, sizeof buf - ami_cnt,
-			" thread=%lu/%p, file=%s, line=%d, info=\"",
-			amip->thread_id, amip->thread_idptr, amip->file, amip->line);
+			" thread=%lu, file=%s, line=%d, info=\"",
+			CRYPTO_THREADID_hash(&amip->threadid), amip->file,
+			amip->line);
 		buf_len=strlen(buf);
 		info_len=strlen(amip->info);
 		if (128 - buf_len - 3 < info_len)
@@ -754,7 +740,7 @@
 
 		amip = amip->next;
 		}
-	while(amip && amip->thread_id == ti && amip->thread_idptr == tip);
+	while(amip && !CRYPTO_THREADID_cmp(&amip->threadid, &ti));
 
 #ifdef LEVITTE_DEBUG_MEM
 	if (amip)
diff --git a/crypto/rand/md_rand.c b/crypto/rand/md_rand.c
index cfc7877..810b4c2 100644
--- a/crypto/rand/md_rand.c
+++ b/crypto/rand/md_rand.c
@@ -145,8 +145,7 @@
                                            * holds CRYPTO_LOCK_RAND
                                            * (to prevent double locking) */
 /* access to lockin_thread is synchronized by CRYPTO_LOCK_RAND2 */
-static unsigned long locking_thread_id = 0; /* valid iff crypto_lock_rand is set */
-static void *locking_thread_idptr = NULL; /* valid iff crypto_lock_rand is set */
+static CRYPTO_THREADID locking_threadid; /* valid iff crypto_lock_rand is set */
 
 
 #ifdef PREDICT
@@ -214,8 +213,10 @@
 	/* check if we already have the lock */
 	if (crypto_lock_rand)
 		{
+		CRYPTO_THREADID cur;
+		CRYPTO_THREADID_current(&cur);
 		CRYPTO_r_lock(CRYPTO_LOCK_RAND2);
-		do_not_lock = (locking_thread_id == CRYPTO_thread_id()) && (locking_thread_idptr == CRYPTO_thread_idptr());
+		do_not_lock = !CRYPTO_THREADID_cmp(&locking_threadid, &cur);
 		CRYPTO_r_unlock(CRYPTO_LOCK_RAND2);
 		}
 	else
@@ -373,8 +374,7 @@
 
 	/* prevent ssleay_rand_bytes() from trying to obtain the lock again */
 	CRYPTO_w_lock(CRYPTO_LOCK_RAND2);
-	locking_thread_id = CRYPTO_thread_id();
-	locking_thread_idptr = CRYPTO_thread_idptr();
+	CRYPTO_THREADID_current(&locking_threadid);
 	CRYPTO_w_unlock(CRYPTO_LOCK_RAND2);
 	crypto_lock_rand = 1;
 
@@ -529,15 +529,17 @@
 
 static int ssleay_rand_status(void)
 	{
+	CRYPTO_THREADID cur;
 	int ret;
 	int do_not_lock;
 
+	CRYPTO_THREADID_current(&cur);
 	/* check if we already have the lock
 	 * (could happen if a RAND_poll() implementation calls RAND_status()) */
 	if (crypto_lock_rand)
 		{
 		CRYPTO_r_lock(CRYPTO_LOCK_RAND2);
-		do_not_lock = (locking_thread_id == CRYPTO_thread_id()) && (locking_thread_idptr == CRYPTO_thread_idptr());
+		do_not_lock = !CRYPTO_THREADID_cmp(&locking_threadid, &cur);
 		CRYPTO_r_unlock(CRYPTO_LOCK_RAND2);
 		}
 	else
@@ -549,8 +551,7 @@
 		
 		/* prevent ssleay_rand_bytes() from trying to obtain the lock again */
 		CRYPTO_w_lock(CRYPTO_LOCK_RAND2);
-		locking_thread_id = CRYPTO_thread_id();
-		locking_thread_idptr = CRYPTO_thread_idptr();
+		CRYPTO_THREADID_cpy(&locking_threadid, &cur);
 		CRYPTO_w_unlock(CRYPTO_LOCK_RAND2);
 		crypto_lock_rand = 1;
 		}
diff --git a/crypto/rsa/rsa_eay.c b/crypto/rsa/rsa_eay.c
index a1ecd6d..7321349 100644
--- a/crypto/rsa/rsa_eay.c
+++ b/crypto/rsa/rsa_eay.c
@@ -264,6 +264,7 @@
 {
 	BN_BLINDING *ret;
 	int got_write_lock = 0;
+	CRYPTO_THREADID cur;
 
 	CRYPTO_r_lock(CRYPTO_LOCK_RSA);
 
@@ -281,7 +282,8 @@
 	if (ret == NULL)
 		goto err;
 
-	if ((BN_BLINDING_get_thread_id(ret) == CRYPTO_thread_id()) && (BN_BLINDING_get_thread_idptr(ret) == CRYPTO_thread_idptr()))
+	CRYPTO_THREADID_current(&cur);
+	if (!CRYPTO_THREADID_cmp(&cur, BN_BLINDING_thread_id(ret)))
 		{
 		/* rsa->blinding is ours! */
 
diff --git a/crypto/rsa/rsa_lib.c b/crypto/rsa/rsa_lib.c
index dd09609..6b1b029 100644
--- a/crypto/rsa/rsa_lib.c
+++ b/crypto/rsa/rsa_lib.c
@@ -417,8 +417,7 @@
 		RSAerr(RSA_F_RSA_SETUP_BLINDING, ERR_R_BN_LIB);
 		goto err;
 		}
-	BN_BLINDING_set_thread_id(ret, CRYPTO_thread_id());
-	BN_BLINDING_set_thread_idptr(ret, CRYPTO_thread_idptr());
+	CRYPTO_THREADID_current(BN_BLINDING_thread_id(ret));
 err:
 	BN_CTX_end(ctx);
 	if (in_ctx == NULL)
diff --git a/crypto/rsa/rsa_test.c b/crypto/rsa/rsa_test.c
index 4080de8..c8705a0 100644
--- a/crypto/rsa/rsa_test.c
+++ b/crypto/rsa/rsa_test.c
@@ -328,7 +328,7 @@
 	}
 
     CRYPTO_cleanup_all_ex_data();
-    ERR_remove_state(0);
+    ERR_remove_thread_state(NULL);
 
     CRYPTO_mem_leaks_fp(stderr);
 
diff --git a/doc/crypto/BN_BLINDING_new.pod b/doc/crypto/BN_BLINDING_new.pod
index 7b087f7..5e3fe1d 100644
--- a/doc/crypto/BN_BLINDING_new.pod
+++ b/doc/crypto/BN_BLINDING_new.pod
@@ -22,8 +22,11 @@
 	BN_CTX *ctx);
  int BN_BLINDING_invert_ex(BIGNUM *n, const BIGNUM *r, BN_BLINDING *b,
 	BN_CTX *ctx);
+ #ifndef OPENSSL_NO_DEPRECATED
  unsigned long BN_BLINDING_get_thread_id(const BN_BLINDING *);
  void BN_BLINDING_set_thread_id(BN_BLINDING *, unsigned long);
+ #endif
+ CRYPTO_THREADID *BN_BLINDING_thread_id(BN_BLINDING *);
  unsigned long BN_BLINDING_get_flags(const BN_BLINDING *);
  void BN_BLINDING_set_flags(BN_BLINDING *, unsigned long);
  BN_BLINDING *BN_BLINDING_create_param(BN_BLINDING *b,
@@ -54,11 +57,11 @@
 functions for BN_BLINDING_convert_ex() and BN_BLINDING_invert_ex()
 with B<r> set to NULL.
 
-BN_BLINDING_set_thread_id() and BN_BLINDING_get_thread_id()
-set and get the "thread id" value of the B<BN_BLINDING> structure,
-a field provided to users of B<BN_BLINDING> structure to help them
-provide proper locking if needed for multi-threaded use. The 
-"thread id" of a newly allocated B<BN_BLINDING> structure is zero.
+BN_BLINDING_thread_id() provides access to the B<CRYPTO_THREADID>
+object within the B<BN_BLINDING> structure. This is to help users
+provide proper locking if needed for multi-threaded use. The "thread
+id" object of a newly allocated B<BN_BLINDING> structure is
+initialised to the thread id in which BN_BLINDING_new() was called.
 
 BN_BLINDING_get_flags() returns the BN_BLINDING flags. Currently
 there are two supported flags: B<BN_BLINDING_NO_UPDATE> and
@@ -83,8 +86,8 @@
 BN_BLINDING_convert_ex() and BN_BLINDING_invert_ex() return 1 on
 success and 0 if an error occured.
 
-BN_BLINDING_get_thread_id() returns the thread id (a B<unsigned long>
-value) or 0 if not set.
+BN_BLINDING_thread_id() returns a pointer to the thread id object
+within a B<BN_BLINDING> object.
 
 BN_BLINDING_get_flags() returns the currently set B<BN_BLINDING> flags
 (a B<unsigned long> value).
@@ -98,6 +101,9 @@
 
 =head1 HISTORY
 
+BN_BLINDING_thread_id was first introduced in OpenSSL 0.9.9, and it
+deprecates BN_BLINDING_set_thread_id and BN_BLINDING_get_thread_id.
+
 BN_BLINDING_convert_ex, BN_BLINDIND_invert_ex, BN_BLINDING_get_thread_id,
 BN_BLINDING_set_thread_id, BN_BLINDING_set_flags, BN_BLINDING_get_flags
 and BN_BLINDING_create_param were first introduced in OpenSSL 0.9.8
diff --git a/doc/crypto/threads.pod b/doc/crypto/threads.pod
index 230cbe8..e6266a1 100644
--- a/doc/crypto/threads.pod
+++ b/doc/crypto/threads.pod
@@ -2,8 +2,9 @@
 
 =head1 NAME
 
-CRYPTO_set_locking_callback, CRYPTO_set_id_callback,
-CRYPTO_set_idptr_callback, CRYPTO_num_locks,
+CRYPTO_THREADID_set_callback, CRYPTO_THREADID_get_callback,
+CRYPTO_THREADID_current, CRYPTO_THREADID_cmp, CRYPTO_THREADID_cpy,
+CRYPTO_THREADID_hash, CRYPTO_set_locking_callback, CRYPTO_num_locks,
 CRYPTO_set_dynlock_create_callback, CRYPTO_set_dynlock_lock_callback,
 CRYPTO_set_dynlock_destroy_callback, CRYPTO_get_new_dynlockid,
 CRYPTO_destroy_dynlockid, CRYPTO_lock - OpenSSL thread support
@@ -12,16 +13,26 @@
 
  #include <openssl/crypto.h>
 
- void CRYPTO_set_locking_callback(void (*locking_function)(int mode,
-        int n, const char *file, int line));
-
- void CRYPTO_set_id_callback(unsigned long (*id_function)(void));
-
- void CRYPTO_set_idptr_callback(void *(*idptr_function)(void));
+ /* Don't use this structure directly. */
+ typedef struct crypto_threadid_st
+         {
+         void *ptr;
+         unsigned long val;
+         } CRYPTO_THREADID;
+ /* Only use CRYPTO_THREADID_set_[numeric|pointer]() within callbacks */
+ void CRYPTO_THREADID_set_numeric(CRYPTO_THREADID *id, unsigned long val);
+ void CRYPTO_THREADID_set_pointer(CRYPTO_THREADID *id, void *ptr);
+ int CRYPTO_THREADID_set_callback(void (*threadid_func)(CRYPTO_THREADID *));
+ void (*CRYPTO_THREADID_get_callback(void))(CRYPTO_THREADID *);
+ void CRYPTO_THREADID_current(CRYPTO_THREADID *id);
+ int CRYPTO_THREADID_cmp(const CRYPTO_THREADID *a,
+                         const CRYPTO_THREADID *b);
+ void CRYPTO_THREADID_cpy(CRYPTO_THREADID *dest,
+                          const CRYPTO_THREADID *src);
+ unsigned long CRYPTO_THREADID_hash(const CRYPTO_THREADID *id);
 
  int CRYPTO_num_locks(void);
 
-
  /* struct CRYPTO_dynlock_value needs to be defined by the user */
  struct CRYPTO_dynlock_value;
 
@@ -53,7 +64,8 @@
 =head1 DESCRIPTION
 
 OpenSSL can safely be used in multi-threaded applications provided
-that at least two callback functions are set.
+that at least two callback functions are set, locking_function and
+threadid_func.
 
 locking_function(int mode, int n, const char *file, int line) is
 needed to perform locking on shared data structures. 
@@ -68,17 +80,42 @@
 B<file> and B<line> are the file number of the function setting the
 lock. They can be useful for debugging.
 
-id_function(void) is a function that returns a numerical thread ID,
-for example pthread_self() if it returns an integer (see NOTES below).
-By OpenSSL's defaults, this is not needed on Windows nor on platforms
-where getpid() returns a different ID for each thread (see NOTES
-below).
+threadid_func(CRYPTO_THREADID *id) is needed to record the currently-executing
+thread's identifier into B<id>. The implementation of this callback should not
+fill in B<id> directly, but should use CRYPTO_THREADID_set_numeric() if thread
+IDs are numeric, or CRYPTO_THREADID_set_pointer() if they are pointer-based.
+If the application does not register such a callback using
+CRYPTO_THREADID_set_callback(), then a default implementation is used - on
+Windows and BeOS this uses the system's default thread identifying APIs, and on
+all other platforms it uses the address of B<errno>. The latter is satisfactory
+for thread-safety if and only if the platform has a thread-local error number
+facility.
 
-idptr_function(void) is a function that similarly returns a thread ID,
-but of type void *.  This is not needed on platforms where &errno is
-different for each thread.  OpenSSL assumes that it is in the same
-thread iff both the numerical and the pointer thread ID agree, so it
-suffices to define one of these two callback functions appropriately.
+Once threadid_func() is registered, or if the built-in default implementation is
+to be used;
+
+=over 4
+
+=item *
+CRYPTO_THREADID_current() records the currently-executing thread ID into the
+given B<id> object.
+
+=item *
+CRYPTO_THREADID_cmp() compares two thread IDs (returning zero for equality, ie.
+the same semantics as memcmp()).
+
+=item *
+CRYPTO_THREADID_cpy() duplicates a thread ID value,
+
+=item *
+CRYPTO_THREADID_hash() returns a numeric value usable as a hash-table key. This
+is usually the exact numeric or pointer-based thread ID used internally, however
+this also handles the unusual case where pointers are larger than 'long'
+variables and the platform's thread IDs are pointer-based - in this case, mixing
+is done to attempt to produce a unique numeric value even though it is not as
+wide as the platform's true thread IDs.
+
+=back
 
 Additionally, OpenSSL supports dynamic locks, and sometimes, some parts
 of OpenSSL need it for better performance.  To enable this, the following
@@ -150,24 +187,6 @@
 Also, dynamic locks are currently not used internally by OpenSSL, but
 may do so in the future.
 
-Defining id_function(void) has it's own issues.  Generally speaking,
-pthread_self() should be used, even on platforms where getpid() gives
-different answers in each thread, since that may depend on the machine
-the program is run on, not the machine where the program is being
-compiled.  For instance, Red Hat 8 Linux and earlier used
-LinuxThreads, whose getpid() returns a different value for each
-thread.  Red Hat 9 Linux and later use NPTL, which is
-Posix-conformant, and has a getpid() that returns the same value for
-all threads in a process.  A program compiled on Red Hat 8 and run on
-Red Hat 9 will therefore see getpid() returning the same value for
-all threads.
-
-There is still the issue of platforms where pthread_self() returns
-something other than an integer.  It is for cases like this that
-CRYPTO_set_idptr_callback() comes in handy.  (E.g., call malloc(1)
-once in each thread, and have idptr_function() return a pointer to
-this object.)
-
 =head1 EXAMPLES
 
 B<crypto/threads/mttest.c> shows examples of the callback functions on
@@ -179,8 +198,10 @@
 available in all versions of SSLeay and OpenSSL.
 CRYPTO_num_locks() was added in OpenSSL 0.9.4.
 All functions dealing with dynamic locks were added in OpenSSL 0.9.5b-dev.
-
-CRYPTO_set_idptr_callback() was added in OpenSSL 0.9.9.
+B<CRYPTO_THREADID> and associated functions were introduced in OpenSSL 0.9.9
+to replace (actually, deprecate) the previous CRYPTO_set_id_callback(),
+CRYPTO_get_id_callback(), and CRYPTO_thread_id() functions which assumed
+thread IDs to always be represented by 'unsigned long'.
 
 =head1 SEE ALSO
 
diff --git a/ssl/ssltest.c b/ssl/ssltest.c
index 83a571d..b20ab0f 100644
--- a/ssl/ssltest.c
+++ b/ssl/ssltest.c
@@ -1014,7 +1014,7 @@
 #endif
 	CRYPTO_cleanup_all_ex_data();
 	ERR_free_strings();
-	ERR_remove_state(0);
+	ERR_remove_thread_state(NULL);
 	EVP_cleanup();
 	CRYPTO_mem_leaks(bio_err);
 	if (bio_err != NULL) BIO_free(bio_err);