Add ERR_remove_thread_state (for Android).

Change-Id: I908d207ccd3d529ec09c687effc2aeb4631127d9
Reviewed-on: https://boringssl-review.googlesource.com/1470
Reviewed-by: David Benjamin <davidben@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/err/err.c b/crypto/err/err.c
index 76d101a..9d29f1c 100644
--- a/crypto/err/err.c
+++ b/crypto/err/err.c
@@ -271,6 +271,29 @@
   state->top = state->bottom = 0;
 }
 
+void ERR_remove_thread_state(const CRYPTO_THREADID *tid) {
+  CRYPTO_THREADID current;
+  ERR_STATE *state;
+  unsigned i;
+
+  if (tid == NULL) {
+    CRYPTO_THREADID_current(&current);
+    tid = &current;
+  }
+
+  err_fns_check();
+  state = ERRFN(release_state)(tid);
+  if (state == NULL) {
+    return;
+  }
+
+  for (i = 0; i < ERR_NUM_ERRORS; i++) {
+    err_clear(&state->errors[i]);
+  }
+
+  OPENSSL_free(state);
+}
+
 int ERR_get_next_error_library() {
   err_fns_check();
   return ERRFN(get_next_library)();
diff --git a/crypto/err/err_impl.c b/crypto/err/err_impl.c
index 6544c7b..7554714 100644
--- a/crypto/err/err_impl.c
+++ b/crypto/err/err_impl.c
@@ -265,6 +265,23 @@
   return state;
 }
 
+static ERR_STATE *err_release_state(const CRYPTO_THREADID *tid) {
+  ERR_STATE pattern, *state;
+
+  CRYPTO_THREADID_cpy(&pattern.tid, tid);
+
+  CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+  if (state_hash == NULL) {
+    CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+    return NULL;
+  }
+
+  state = lh_ERR_STATE_delete(state_hash, &pattern);
+  CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+
+  return state;
+}
+
 static void err_shutdown(void) {
   CRYPTO_w_lock(CRYPTO_LOCK_ERR);
   if (error_hash) {
@@ -290,5 +307,6 @@
   err_set_item,
   err_del_item,
   err_get_state,
+  err_release_state,
   err_get_next_library,
 };
diff --git a/crypto/err/err_test.c b/crypto/err/err_test.c
index adfc9d0..22cbef7 100644
--- a/crypto/err/err_test.c
+++ b/crypto/err/err_test.c
@@ -105,11 +105,18 @@
   return 1;
 }
 
+static int test_release() {
+  ERR_put_error(1, 2, 3, "test", 4);
+  ERR_remove_thread_state(NULL);
+  return 0;
+}
+
 int main() {
   if (!test_overflow() ||
       !test_put_error() ||
       !test_clear_error() ||
-      !test_print()) {
+      !test_print() ||
+      !test_release()) {
     return 1;
   }
 
diff --git a/include/openssl/err.h b/include/openssl/err.h
index ddb094c..8ebd73f 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -256,6 +256,10 @@
 /* ERR_clear_error clears the error queue for the current thread. */
 OPENSSL_EXPORT void ERR_clear_error(void);
 
+/* ERR_remove_thread_state deletes the error queue for the given thread. If
+ * |tid| is NULL then the error queue for the current thread is deleted. */
+OPENSSL_EXPORT void ERR_remove_thread_state(const CRYPTO_THREADID *tid);
+
 
 /* Custom errors. */
 
@@ -482,6 +486,11 @@
    * never returns NULL. */
   ERR_STATE *(*get_state)(void);
 
+  /* release_state returns the |ERR_STATE| for the given thread, or NULL if
+   * none exists. It the return value is not NULL, it also returns ownership of
+   * the |ERR_STATE| and deletes it from its data structures. */
+  ERR_STATE *(*release_state)(const CRYPTO_THREADID *tid);
+
   /* get_next_library returns a unique value suitable for passing as the
    * |library| to error calls. It will be distinct from all built-in library
    * values. */