diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -18,6 +18,7 @@
   include/apple_availability.h
   include/atomic_support.h
   include/config_elast.h
+  include/internal_threading_support.h
   include/refstring.h
   include/ryu/common.h
   include/ryu/d2fixed.h
@@ -113,6 +114,7 @@
     )
 elseif(ZOS)
   list(APPEND LIBCXX_SOURCES
+    include/ceeedb.h
     support/ibm/mbsnrtowcs.cpp
     support/ibm/wcsnrtombs.cpp
     support/ibm/xlocale_zos.cpp
diff --git a/libcxx/src/include/ceeedb.h b/libcxx/src/include/ceeedb.h
new file mode 100644
--- /dev/null
+++ b/libcxx/src/include/ceeedb.h
@@ -0,0 +1,55 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_SUPPORT_IBM_CEEEDB_H
+#define _LIBCPP_SUPPORT_IBM_CEEEDB_H
+
+#ifndef __MVS__
+#  error This header should only be included on z/OS
+#endif
+
+#include <__config>
+#include <__gfunc.h>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+static inline unsigned char __libcpp_ceeedb_flags() {
+  // Offsets can be found in the z/OS Language Environment Vendor Interfaces book
+  // https://www-01.ibm.com/servers/resourcelink/svc00100.nsf/pages/zOSV2R4sa380688?OpenDocument
+#ifndef __64BIT__
+  enum { OFFSET_CEECAAEDB = 0x2f0 };
+  enum { OFFSET_CEEEDBFLAG1 = 0x08 };
+#else
+  enum { OFFSET_CEECAAEDB = 0x388 };
+  enum { OFFSET_CEEEDBFLAG1 = 0x100 };
+#endif
+
+  // See the zOS Language Environment Vendor Interfaces book for a description
+  // of the Common Anchor Area (CEECAA), the Enclave Data Block (CEECAAEDB),
+  // and the EDB flags (CEEEDBFLAG1).
+  const unsigned char* CEECAA = static_cast<const unsigned char*>(__gtca());
+  const unsigned char* CEECAAEDB =
+      *static_cast<const unsigned char**>(const_cast<void*>(static_cast<const void*>(CEECAA + OFFSET_CEECAAEDB)));
+  const unsigned char* CEEEDBFLAG1 = (CEECAAEDB + OFFSET_CEEEDBFLAG1);
+  return *CEEEDBFLAG1;
+}
+
+static inline bool __libcpp_ceeedb_multithread() {
+  const unsigned char CEEEDBMULTITHREAD = 0x02;
+  return __libcpp_ceeedb_flags() & CEEEDBMULTITHREAD;
+}
+
+static inline bool __libcpp_ceeedb_posix() {
+  const unsigned char CEEEDB_POSIX = 0x04;
+  return __libcpp_ceeedb_flags() & CEEEDB_POSIX;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_SUPPORT_IBM_CEEEDB_H
diff --git a/libcxx/src/include/internal_threading_support.h b/libcxx/src/include/internal_threading_support.h
new file mode 100644
--- /dev/null
+++ b/libcxx/src/include/internal_threading_support.h
@@ -0,0 +1,223 @@
+//===----------------------------------------------------------------------===////
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===////
+
+#ifndef _LIBCPP_INTERNAL_THREADING_SUPPORT_H
+#define _LIBCPP_INTERNAL_THREADING_SUPPORT_H
+
+/** internal_threading_support.h - Equivalent to __threading_support, but for internal uses that are
+ * not related to the C++11 Threading Library. These should all be within code that is compiled into
+ * the libcxx library rather than potentially #included by user code.
+ *
+ * See libcxx/docs/DesignDocs/InternalThreadSynchronization.rst for details.
+ *
+ * Any platforms which cannot determine during compilation of libc++ the availability of a thread
+ * library such as pthreads should use wrappers definitions provided here and define 
+ * `__libcpp_is_threading_api_enabled()` and `__libcpp_might_have_multiple_threads()`
+ * following implementation on z/OS.
+ */
+
+#include <__threading_support>
+
+#ifndef _LIBCPP_HAS_NO_THREADS
+#  include <cassert>
+
+#  if defined(__MVS__)
+static inline bool __libcpp_is_threading_api_enabled();
+
+static inline bool __libcpp_might_have_multiple_threads();
+
+#  else
+static inline _LIBCPP_CONSTEXPR bool __libcpp_is_threading_api_enabled();
+
+static inline _LIBCPP_CONSTEXPR bool __libcpp_might_have_multiple_threads();
+#  endif // __MVS__
+
+#  if defined(__MVS__)
+#    include "ceeedb.h"
+/// On z/OS some posix functions can be enabled/disabled at runtime.
+/// However, the enabled/disabled status does not change over the life of the process.
+bool __libcpp_is_threading_api_enabled() {
+  static bool __posix_on = std::__libcpp_ceeedb_posix();
+  return __posix_on;
+}
+bool __libcpp_might_have_multiple_threads() { return std::__libcpp_ceeedb_multithread(); }
+#  else
+_LIBCPP_CONSTEXPR bool __libcpp_is_threading_api_enabled() { return true; }
+
+// Without any way of checking, we are forced to assume other threads have been spawned.
+_LIBCPP_CONSTEXPR bool __libcpp_might_have_multiple_threads() { return __libcpp_is_threading_api_enabled(); }
+#  endif // __MVS__
+
+//===----------------------------------------------------------------------===//
+//    Definitions which would normally be provided by <__threading_support>
+//    are defined here as wrappers to enable POSIX(OFF) on z/OS.
+//===----------------------------------------------------------------------===//
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace internal_thread {
+
+static inline int __libcpp_recursive_mutex_init(__libcpp_recursive_mutex_t* __m) {
+  if (!__libcpp_is_threading_api_enabled())
+    return 0;
+  return _VSTD::__libcpp_recursive_mutex_init(__m);
+}
+
+static inline int __libcpp_recursive_mutex_lock(__libcpp_recursive_mutex_t* __m) {
+  // See __libcpp_mutex_lock for an explaination of why this is safe
+  if (!__libcpp_might_have_multiple_threads())
+    return 0;
+  return _VSTD::__libcpp_recursive_mutex_lock(__m);
+}
+
+static inline bool __libcpp_recursive_mutex_trylock(__libcpp_recursive_mutex_t* __m) {
+  // See __libcpp_mutex_lock for an explaination of why this is safe
+  if (!__libcpp_might_have_multiple_threads())
+    return true;
+  return _VSTD::__libcpp_recursive_mutex_trylock(__m);
+}
+
+static inline int __libcpp_recursive_mutex_unlock(__libcpp_recursive_mutex_t* __m) {
+  // See __libcpp_mutex_lock for an explaination of why this is safe
+  if (!__libcpp_might_have_multiple_threads())
+    return 0;
+  return _VSTD::__libcpp_recursive_mutex_unlock(__m);
+}
+
+static inline int __libcpp_recursive_mutex_destroy(__libcpp_recursive_mutex_t* __m) {
+  if (!__libcpp_is_threading_api_enabled())
+    return 0;
+  return _VSTD::__libcpp_recursive_mutex_destroy(__m);
+}
+
+static inline int __libcpp_mutex_lock(__libcpp_mutex_t* __m) {
+  // All libcxx-internal locks are released before we run any code which could
+  // spawn a thread, so we can safely skip mutex acquisition when there's only
+  // one thread (even if the threading API is enabled).
+  if (!__libcpp_might_have_multiple_threads())
+    return 0;
+  return _VSTD::__libcpp_mutex_lock(__m);
+}
+
+static inline bool __libcpp_mutex_trylock(__libcpp_mutex_t* __m) {
+  // See __libcpp_mutex_lock for an explaination of why this is safe
+  if (!__libcpp_might_have_multiple_threads())
+    return true;
+  return _VSTD::__libcpp_mutex_trylock(__m);
+}
+
+static inline int __libcpp_mutex_unlock(__libcpp_mutex_t* __m) {
+  // See __libcpp_mutex_lock for an explaination of why this is safe
+  if (!__libcpp_might_have_multiple_threads())
+    return 0;
+  return _VSTD::__libcpp_mutex_unlock(__m);
+}
+
+static inline int __libcpp_mutex_destroy(__libcpp_mutex_t* __m) {
+  if (!__libcpp_is_threading_api_enabled())
+    return 0;
+  return _VSTD::__libcpp_mutex_destroy(__m);
+}
+
+// Condition Variable
+static inline int __libcpp_condvar_signal(__libcpp_condvar_t* __cv) {
+  // If we're the only thread, there's no one to signal to, skip it
+  if (!__libcpp_might_have_multiple_threads())
+    return 0;
+  return _VSTD::__libcpp_condvar_signal(__cv);
+}
+
+static inline int __libcpp_condvar_broadcast(__libcpp_condvar_t* __cv) {
+  // If we're the only thread, there's no one to broadcast to, skip it
+  if (!__libcpp_might_have_multiple_threads())
+    return 0;
+  return _VSTD::__libcpp_condvar_broadcast(__cv);
+}
+
+static inline int __libcpp_condvar_wait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m) {
+  // If we're the only thread, there's no one to wake us up, so this is a deadlock
+  assert(__libcpp_might_have_multiple_threads());
+  return _VSTD::__libcpp_condvar_wait(__cv, __m);
+}
+
+static inline int __libcpp_condvar_timedwait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m,
+                                             __libcpp_timespec_t* __ts) {
+  if (!__libcpp_is_threading_api_enabled()) {
+    // With nobody to wake us up, this is equivalent to a sleep
+    // TODO: actually wait until __ts, and replace this with
+    // `if(!__libcpp_might_have_multiple_threads())`
+    return ETIMEDOUT;
+  }
+  return _VSTD::__libcpp_condvar_timedwait(__cv, __m, __ts);
+}
+
+static inline int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv) {
+  if (!__libcpp_is_threading_api_enabled())
+    return 0;
+  return _VSTD::__libcpp_condvar_destroy(__cv);
+}
+
+// Execute once
+static inline int __libcpp_execute_once(__libcpp_exec_once_flag* flag, void (*init_routine)()) {
+
+  // The __libcpp_is_threading_api_enabled() cannot change during program execution on z/OS.
+  if (!__libcpp_is_threading_api_enabled()) {
+#  if defined(__MVS__)
+    if (*flag == _LIBCPP_EXEC_ONCE_INITIALIZER) {
+      init_routine();
+      *flag = 1;
+    }
+#  else
+    // TODO: Need a specific implementation for each platform which will allow
+    // __libcpp_is_threading_api_enabled() to return false.
+    // In order for this to work when __libcpp_is_threading_api_enabled() can change during
+    // program execution, we have to write the same value pthread_once would.
+    _VSTD::abort();
+#  endif
+    return 0;
+  }
+
+  return _VSTD::__libcpp_execute_once(flag, init_routine);
+}
+
+// Thread id
+static inline int __libcpp_thread_create(__libcpp_thread_t* __t, void* (*__func)(void*), void* __arg) {
+  assert(__libcpp_is_threading_api_enabled());
+  return _VSTD::__libcpp_thread_create(__t, __func, __arg);
+}
+
+static inline __libcpp_thread_id __libcpp_thread_get_current_id() {
+  assert(__libcpp_is_threading_api_enabled());
+  return _VSTD::__libcpp_thread_get_current_id();
+}
+
+static inline int __libcpp_thread_join(__libcpp_thread_t* __t) {
+  assert(__libcpp_is_threading_api_enabled());
+  return _VSTD::__libcpp_thread_join(__t);
+}
+
+static inline int __libcpp_thread_detach(__libcpp_thread_t* __t) {
+  assert(__libcpp_is_threading_api_enabled());
+  return _VSTD::__libcpp_thread_detach(__t);
+}
+
+static inline void __libcpp_thread_yield() {
+  if (!__libcpp_might_have_multiple_threads())
+    return;
+  _VSTD::__libcpp_thread_yield();
+}
+
+static inline void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns) {
+  assert(__libcpp_is_threading_api_enabled());
+  _VSTD::__libcpp_thread_sleep_for(__ns);
+}
+
+} // end namespace internal_thread
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !_LIBCPP_HAS_NO_THREADS
+#endif // _LIBCPP_INTERNAL_THREADING_SUPPORT_H
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -25,6 +25,8 @@
 #include <typeinfo>
 #include <vector>
 
+#include "include/internal_threading_support.h"
+
 #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
 #   include <cwctype>
 #endif
@@ -725,6 +727,12 @@
 long
 locale::id::__get()
 {
+#ifndef _LIBCPP_HAS_NO_THREADS
+    if (!__libcpp_is_threading_api_enabled()) {
+        __id_ = ++__next_id;
+        return __id_-1;
+    }
+#endif
     call_once(__flag_, __fake_bind(&locale::id::__init, this));
     return __id_ - 1;
 }
diff --git a/libcxxabi/src/cxa_exception_storage.cpp b/libcxxabi/src/cxa_exception_storage.cpp
--- a/libcxxabi/src/cxa_exception_storage.cpp
+++ b/libcxxabi/src/cxa_exception_storage.cpp
@@ -12,7 +12,7 @@
 
 #include "cxa_exception.h"
 
-#include <__threading_support>
+#include "include/internal_threading_support.h" // from libc++
 
 #if defined(_LIBCXXABI_HAS_NO_THREADS)
 
@@ -92,6 +92,11 @@
     // to the Itanium ABI and is taken advantage of in several places in
     // libc++abi.
     __cxa_eh_globals *__cxa_get_globals_fast() {
+        // If threads are disabled at runtime, revert to single-threaded implementation.
+        if (!__libcpp_is_threading_api_enabled()) {
+            static __cxa_eh_globals eh_globals;
+            return &eh_globals;
+        }
         // First time through, create the key.
         if (0 != std::__libcpp_execute_once(&flag_, construct_))
             abort_message("execute once failure in __cxa_get_globals_fast()");
diff --git a/libcxxabi/src/cxa_guard_impl.h b/libcxxabi/src/cxa_guard_impl.h
--- a/libcxxabi/src/cxa_guard_impl.h
+++ b/libcxxabi/src/cxa_guard_impl.h
@@ -56,7 +56,7 @@
 
 #include <limits.h>
 #include <stdlib.h>
-#include <__threading_support>
+#include "include/internal_threading_support.h" // from libc++
 #ifndef _LIBCXXABI_HAS_NO_THREADS
 #  if defined(__ELF__) && defined(_LIBCXXABI_LINK_PTHREAD_LIB)
 #    pragma comment(lib, "pthread")
@@ -152,7 +152,7 @@
 #if defined(__APPLE__) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
 uint32_t PlatformThreadID() {
   static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "");
-  return static_cast<uint32_t>(pthread_mach_thread_np(std::__libcpp_thread_get_current_id()));
+  return static_cast<uint32_t>(pthread_mach_thread_np(std::internal_thread::__libcpp_thread_get_current_id()));
 }
 #elif defined(SYS_gettid) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
 uint32_t PlatformThreadID() {
@@ -279,8 +279,8 @@
   LibcppMutex(LibcppMutex const&) = delete;
   LibcppMutex& operator=(LibcppMutex const&) = delete;
 
-  bool lock() { return std::__libcpp_mutex_lock(&mutex); }
-  bool unlock() { return std::__libcpp_mutex_unlock(&mutex); }
+  bool lock() { return std::internal_thread::__libcpp_mutex_lock(&mutex); }
+  bool unlock() { return std::internal_thread::__libcpp_mutex_unlock(&mutex); }
 
 private:
   friend struct LibcppCondVar;
@@ -292,8 +292,8 @@
   LibcppCondVar(LibcppCondVar const&) = delete;
   LibcppCondVar& operator=(LibcppCondVar const&) = delete;
 
-  bool wait(LibcppMutex& mut) { return std::__libcpp_condvar_wait(&cond, &mut.mutex); }
-  bool broadcast() { return std::__libcpp_condvar_broadcast(&cond); }
+  bool wait(LibcppMutex& mut) { return std::internal_thread::__libcpp_condvar_wait(&cond, &mut.mutex); }
+  bool broadcast() { return std::internal_thread::__libcpp_condvar_broadcast(&cond); }
 
 private:
   std::__libcpp_condvar_t cond = _LIBCPP_CONDVAR_INITIALIZER;
diff --git a/libcxxabi/test/test_exception_storage.pass.cpp b/libcxxabi/test/test_exception_storage.pass.cpp
--- a/libcxxabi/test/test_exception_storage.pass.cpp
+++ b/libcxxabi/test/test_exception_storage.pass.cpp
@@ -9,7 +9,7 @@
 #include <algorithm>
 #include <cstdio>
 #include <cstdlib>
-#include <__threading_support>
+#include "include/internal_threading_support.h" // from libc++
 #include <unistd.h>
 
 #include "../src/cxa_exception.h"
@@ -17,22 +17,22 @@
 typedef __cxxabiv1::__cxa_eh_globals globals_t ;
 
 void *thread_code (void *parm) {
-    size_t *result = (size_t *) parm;
-    globals_t *glob1, *glob2;
+  size_t* result = (size_t*)parm;
+  globals_t *glob1, *glob2;
 
-    glob1 = __cxxabiv1::__cxa_get_globals ();
-    if ( NULL == glob1 )
-        std::printf("Got null result from __cxa_get_globals\n");
+  glob1 = __cxxabiv1::__cxa_get_globals();
+  if (NULL == glob1)
+    std::printf("Got null result from __cxa_get_globals\n");
 
-    glob2 = __cxxabiv1::__cxa_get_globals_fast ();
-    if ( glob1 != glob2 )
-        std::printf("Got different globals!\n");
+  glob2 = __cxxabiv1::__cxa_get_globals_fast();
+  if (glob1 != glob2)
+    std::printf("Got different globals!\n");
 
-    *result = (size_t) glob1;
+  *result = (size_t)glob1;
 #ifndef _LIBCXXABI_HAS_NO_THREADS
-    sleep ( 1 );
+  sleep(1);
 #endif
-    return parm;
+  return parm;
 }
 
 #ifndef _LIBCXXABI_HAS_NO_THREADS
@@ -42,33 +42,40 @@
 #endif
 
 int main() {
-#ifndef _LIBCXXABI_HAS_NO_THREADS
-//  Make the threads, let them run, and wait for them to finish
-    for ( int i = 0; i < NUMTHREADS; ++i )
-        std::__libcpp_thread_create ( threads + i, thread_code, (void *) (thread_globals + i));
-    for ( int i = 0; i < NUMTHREADS; ++i )
-        std::__libcpp_thread_join ( &threads [ i ] );
+#ifdef _LIBCXXABI_HAS_NO_THREADS
+  size_t thread_globals;
+  thread_code(&thread_globals);
+  // Check that __cxa_get_globals() is not NULL.
+  return (thread_globals == 0) ? 1 : 0;
+#else  // !_LIBCXXABI_HAS_NO_THREADS
+  // If threads are disabled at runtime, revert to single-threaded test.
+  if (!__libcpp_is_threading_api_enabled()) {
+    thread_code((void*)thread_globals);
+    // Check that __cxa_get_globals() is not NULL.
+    return (thread_globals[0] == 0) ? 1 : 0;
+  }
 
-    int retVal = 0;
-    for ( int i = 0; i < NUMTHREADS; ++i ) {
-        if ( 0 == thread_globals [ i ] ) {
-            std::printf("Thread #%d had a zero global\n", i);
-            retVal = 1;
-        }
+  //  Make the threads, let them run, and wait for them to finish
+  for (int i = 0; i < NUMTHREADS; ++i)
+    std::__libcpp_thread_create(threads + i, thread_code, (void*)(thread_globals + i));
+  for (int i = 0; i < NUMTHREADS; ++i)
+    std::__libcpp_thread_join(&threads[i]);
+
+  int retVal = 0;
+  for (int i = 0; i < NUMTHREADS; ++i) {
+    if (0 == thread_globals[i]) {
+      std::printf("Thread #%d had a zero global\n", i);
+      retVal = 1;
     }
+  }
 
-    std::sort ( thread_globals, thread_globals + NUMTHREADS );
-    for ( int i = 1; i < NUMTHREADS; ++i ) {
-        if ( thread_globals [ i - 1 ] == thread_globals [ i ] ) {
-            std::printf("Duplicate thread globals (%d and %d)\n", i-1, i);
-            retVal = 2;
-        }
+  std::sort(thread_globals, thread_globals + NUMTHREADS);
+  for (int i = 1; i < NUMTHREADS; ++i) {
+    if (thread_globals[i - 1] == thread_globals[i]) {
+      std::printf("Duplicate thread globals (%d and %d)\n", i - 1, i);
+      retVal = 2;
     }
-    return retVal;
-#else // _LIBCXXABI_HAS_NO_THREADS
-    size_t thread_globals;
-    thread_code(&thread_globals);
-    // Check that __cxa_get_globals() is not NULL.
-    return (thread_globals == 0) ? 1 : 0;
+  }
+  return retVal;
 #endif // !_LIBCXXABI_HAS_NO_THREADS
 }