+
+ // If a new tlv was added via tlv_atexit, then we need to immediately
+ // destroy it
+ struct TLVTerminatorList* newlist = (struct TLVTerminatorList*)pthread_getspecific(tlv_terminators_key);
+ if ( newlist != NULL ) {
+ // Set the list to NULL so that if yet another tlv is registered, we put it in a new list
+ pthread_setspecific(tlv_terminators_key, NULL);
+ tlv_finalize_list(newlist);
+ }
+ }
+ free(list);
+}
+
+// called by pthreads when the current thread is going away and
+// _tlv_atexit() has been called on the thread.
+static void tlv_finalize(void* storage)
+{
+ // Note, on entry to this function, _tlv_exit set the list to NULL. libpthread
+ // also set it to NULL before calling us. If new thread locals are added to our
+ // tlv_terminators_key, then they will be on a new list, but the list we have here
+ // is one we own and need to destroy it
+ tlv_finalize_list((struct TLVTerminatorList*)storage);
+}
+
+// <rdar://problem/13741816>
+// called by exit() before it calls cxa_finalize() so that thread_local
+// objects are destroyed before global objects.
+// Note this is only called on macOS, and by libc.
+// iOS only destroys tlv's when each thread is destroyed and libpthread calls
+// tlv_finalize as that is the pointer we provided when we created the key
+void _tlv_exit()
+{
+ void* termFuncs = pthread_getspecific(tlv_terminators_key);
+ if ( termFuncs != NULL ) {
+ // Clear the value so that calls to tlv_atexit during tlv_finalize
+ // will go on to a new list to destroy.
+ pthread_setspecific(tlv_terminators_key, NULL);
+ tlv_finalize(termFuncs);