From 0e3594b9d0413f02ad8f7094b43dea50b7279273 Mon Sep 17 00:00:00 2001
From: Kevin Schoedel <67607049+kpschoedel@users.noreply.github.com>
Date: Mon, 19 Jul 2021 15:44:08 -0400
Subject: [PATCH] Move WakeIOThread() functionality to WatchableEventManager
 (#8456)

#### Problem

System::Layer contains `WakeIOThread()` and `mWakeEvent` which are
really implementation details of `WatchableEventManager`.

#### Change overview

- Replace `System::Layer::WakeIOThread()` with
  `WatchableEventManager::Signal()`, because not every implementation
  will necessarily implement the nudge by waking a thread.

- Split `SystemSockets.h` into `WatchableEventManager.h`,
  `WatchableSocket.h`, and `WakeEvent.h`; likewise split the
  various implementation files.

- Make the `System::WakeEvent` public API independent of the
  file-descriptor implementation.

For the present, `mHandleSelectThread` remains in `System::Layer`
until timers are integrated and the transitional `HandleTimeout()`
is removed.

#### Testing

Sanity check with chip-tool. CI should verify that functionality does
not regress.
---
 src/controller/CHIPDeviceController.cpp       |   6 +-
 .../java/CHIPDeviceController-JNI.cpp         |   2 +-
 .../GenericPlatformManagerImpl_POSIX.cpp      |   6 +-
 .../GenericPlatformManagerImpl_Zephyr.cpp     |   2 +-
 src/inet/EndPointBasis.h                      |   2 +-
 src/inet/RawEndPoint.cpp                      |   2 +-
 src/inet/TCPEndPoint.cpp                      |   6 +-
 src/platform/Linux/MdnsImpl.h                 |   2 +-
 src/platform/mbed/PlatformManagerImpl.cpp     |   7 +-
 src/system/BUILD.gn                           |   9 +-
 src/system/SystemConfig.h                     |   8 -
 src/system/SystemLayer.cpp                    |  45 ----
 src/system/SystemLayer.h                      |  12 +-
 src/system/SystemTimer.cpp                    |   8 +-
 .../{SystemSockets.cpp => WakeEvent.cpp}      |   5 +-
 src/system/WakeEvent.h                        |  69 ++++++
 src/system/WatchableEventManager.h            |  72 ++++++
 src/system/WatchableEventManagerLibevent.cpp  | 196 +++++++++++++++
 src/system/WatchableEventManagerLibevent.h    |  73 ++++++
 src/system/WatchableEventManagerSelect.cpp    | 233 ++++++++++++++++++
 src/system/WatchableEventManagerSelect.h      |  92 +++++++
 .../{SystemSockets.h => WatchableSocket.h}    |  63 +----
 src/system/WatchableSocketLibevent.cpp        | 148 +----------
 src/system/WatchableSocketLibevent.h          |  42 +---
 src/system/WatchableSocketSelect.cpp          | 205 ++-------------
 src/system/WatchableSocketSelect.h            |  69 +-----
 src/system/tests/TestSystemWakeEvent.cpp      |  27 +-
 27 files changed, 845 insertions(+), 566 deletions(-)
 rename src/system/{SystemSockets.cpp => WakeEvent.cpp} (97%)
 create mode 100644 src/system/WakeEvent.h
 create mode 100644 src/system/WatchableEventManager.h
 create mode 100644 src/system/WatchableEventManagerLibevent.cpp
 create mode 100644 src/system/WatchableEventManagerLibevent.h
 create mode 100644 src/system/WatchableEventManagerSelect.cpp
 create mode 100644 src/system/WatchableEventManagerSelect.h
 rename src/system/{SystemSockets.h => WatchableSocket.h} (77%)

diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp
index 282c8e1887276e..a41dec0c091a9e 100644
--- a/src/controller/CHIPDeviceController.cpp
+++ b/src/controller/CHIPDeviceController.cpp
@@ -549,11 +549,11 @@ CHIP_ERROR DeviceController::ServiceEventSignal()
 {
     VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);
 
-#if CONFIG_DEVICE_LAYER && CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-    DeviceLayer::SystemLayer.WakeIOThread();
+#if CONFIG_DEVICE_LAYER && (CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK)
+    DeviceLayer::SystemLayer.WatchableEvents().Signal();
 #else
     ReturnErrorOnFailure(CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
-#endif // CONFIG_DEVICE_LAYER && CHIP_SYSTEM_CONFIG_USE_IO_THREAD
+#endif // CONFIG_DEVICE_LAYER && (CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK)
 
     return CHIP_NO_ERROR;
 }
diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp
index 262d7402705886..c1baa070b74639 100644
--- a/src/controller/java/CHIPDeviceController-JNI.cpp
+++ b/src/controller/java/CHIPDeviceController-JNI.cpp
@@ -206,7 +206,7 @@ void JNI_OnUnload(JavaVM * jvm, void * reserved)
     if (sIOThread != PTHREAD_NULL)
     {
         sShutdown = true;
-        sSystemLayer.WakeIOThread();
+        sSystemLayer.WatchableEvents().Signal();
 
         StackUnlockGuard unlockGuard(JniReferences::GetInstance().GetStackLock());
         pthread_join(sIOThread, NULL);
diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp
index c48eb5e3e39eca..44200995b8a569 100644
--- a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp
+++ b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp
@@ -127,9 +127,7 @@ void GenericPlatformManagerImpl_POSIX<ImplClass>::_PostEvent(const ChipDeviceEve
 {
     mChipEventQueue.Push(*event);
 
-#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-    SystemLayer.WakeIOThread(); // Trigger wake select on CHIP thread
-#endif                          // CHIP_SYSTEM_CONFIG_USE_IO_THREAD
+    SystemLayer.WatchableEvents().Signal(); // Trigger wake select on CHIP thread
 }
 
 template <class ImplClass>
@@ -259,7 +257,7 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX<ImplClass>::_StopEventLoopTask()
         // SystemLayer.
         //
         Impl()->LockChipStack();
-        SystemLayer.WakeIOThread();
+        SystemLayer.WatchableEvents().Signal();
         Impl()->UnlockChipStack();
 
         pthread_mutex_lock(&mStateLock);
diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp b/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp
index fb67c1a44fdb24..749ece712f3e0c 100644
--- a/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp
+++ b/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp
@@ -109,7 +109,7 @@ void GenericPlatformManagerImpl_Zephyr<ImplClass>::_PostEvent(const ChipDeviceEv
     // k_msgq_put takes `void*` instead of `const void*`. Nonetheless, it should be safe to
     // const_cast here and there are components in Zephyr itself which do the same.
     if (k_msgq_put(&mChipEventQueue, const_cast<ChipDeviceEvent *>(event), K_NO_WAIT) == 0)
-        SystemLayer.WakeIOThread(); // Trigger wake on CHIP thread
+        SystemLayer.WatchableEvents().Signal(); // Trigger wake on CHIP thread
     else
         ChipLogError(DeviceLayer, "Failed to post event to CHIP Platform event queue");
 }
diff --git a/src/inet/EndPointBasis.h b/src/inet/EndPointBasis.h
index 0baa1a8be6260c..2d7411351e7c42 100644
--- a/src/inet/EndPointBasis.h
+++ b/src/inet/EndPointBasis.h
@@ -35,7 +35,7 @@
 #include <support/DLLUtil.h>
 
 #if CHIP_SYSTEM_CONFIG_USE_SOCKETS
-#include <system/SystemSockets.h>
+#include <system/WatchableSocket.h>
 #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
 
 #if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
diff --git a/src/inet/RawEndPoint.cpp b/src/inet/RawEndPoint.cpp
index 16d461d7675d6f..c1df86b95721ed 100644
--- a/src/inet/RawEndPoint.cpp
+++ b/src/inet/RawEndPoint.cpp
@@ -48,7 +48,7 @@
 #endif                             // CHIP_SYSTEM_CONFIG_USE_LWIP
 
 #if CHIP_SYSTEM_CONFIG_USE_SOCKETS
-#include <system/SystemSockets.h>
+#include <system/WatchableSocket.h>
 #if HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 #endif // HAVE_SYS_SOCKET_H
diff --git a/src/inet/TCPEndPoint.cpp b/src/inet/TCPEndPoint.cpp
index 5d488352b4063c..1a24f17e39937c 100644
--- a/src/inet/TCPEndPoint.cpp
+++ b/src/inet/TCPEndPoint.cpp
@@ -833,10 +833,10 @@ void TCPEndPoint::EnableReceive()
 
     DriveReceiving();
 
-#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD
+#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
     // Wake the thread waiting for I/O so that it can include the socket.
-    SystemLayer().WakeIOThread();
-#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD
+    SystemLayer().WatchableEvents().Signal();
+#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
 }
 
 /**
diff --git a/src/platform/Linux/MdnsImpl.h b/src/platform/Linux/MdnsImpl.h
index 042aa214626877..7360c148b6ed4b 100644
--- a/src/platform/Linux/MdnsImpl.h
+++ b/src/platform/Linux/MdnsImpl.h
@@ -34,7 +34,7 @@
 #include <avahi-common/watch.h>
 
 #include "lib/mdns/platform/Mdns.h"
-#include "system/SystemSockets.h"
+#include "system/WatchableSocket.h"
 
 struct AvahiWatch
 {
diff --git a/src/platform/mbed/PlatformManagerImpl.cpp b/src/platform/mbed/PlatformManagerImpl.cpp
index 2b0384772d1134..cfe4b7c8d55351 100644
--- a/src/platform/mbed/PlatformManagerImpl.cpp
+++ b/src/platform/mbed/PlatformManagerImpl.cpp
@@ -46,8 +46,9 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack(void)
         mQueue.~EventQueue();
         new (&mQueue) events::EventQueue(event_size * CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE);
 
-        mQueue.background(
-            [&](int t) { MbedEventTimeout::AttachTimeout([&] { SystemLayer.WakeIOThread(); }, std::chrono::milliseconds{ t }); });
+        mQueue.background([&](int t) {
+            MbedEventTimeout::AttachTimeout([&] { SystemLayer.WatchableEvents().Signal(); }, std::chrono::milliseconds{ t });
+        });
 
         // Reinitialize the Mutexes
         mThisStateMutex.~Mutex();
@@ -211,7 +212,7 @@ CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask()
 
     // Wake from select so it unblocks processing
     LockChipStack();
-    SystemLayer.WakeIOThread();
+    SystemLayer.WatchableEvents().Signal();
     UnlockChipStack();
 
     osStatus err = osOK;
diff --git a/src/system/BUILD.gn b/src/system/BUILD.gn
index 3695867d85c400..387ea3c89465aa 100644
--- a/src/system/BUILD.gn
+++ b/src/system/BUILD.gn
@@ -96,6 +96,7 @@ buildconfig_header("system_buildconfig") {
   }
 
   if (chip_system_config_use_sockets) {
+    defines += [ "CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE=<system/WatchableEventManager${chip_system_config_sockets_event_loop}.h>" ]
     defines += [ "CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE=<system/WatchableSocket${chip_system_config_sockets_event_loop}.h>" ]
   }
 }
@@ -142,8 +143,6 @@ static_library("system") {
     "SystemObject.h",
     "SystemPacketBuffer.cpp",
     "SystemPacketBuffer.h",
-    "SystemSockets.cpp",
-    "SystemSockets.h",
     "SystemStats.cpp",
     "SystemStats.h",
     "SystemTimer.cpp",
@@ -151,6 +150,10 @@ static_library("system") {
     "TLVPacketBufferBackingStore.cpp",
     "TLVPacketBufferBackingStore.h",
     "TimeSource.h",
+    "WakeEvent.cpp",
+    "WakeEvent.h",
+    "WatchableEventManager.h",
+    "WatchableSocket.h",
   ]
 
   cflags = [ "-Wconversion" ]
@@ -165,6 +168,8 @@ static_library("system") {
 
   if (chip_system_config_use_sockets) {
     sources += [
+      "WatchableEventManager${chip_system_config_sockets_event_loop}.cpp",
+      "WatchableEventManager${chip_system_config_sockets_event_loop}.h",
       "WatchableSocket${chip_system_config_sockets_event_loop}.cpp",
       "WatchableSocket${chip_system_config_sockets_event_loop}.h",
     ]
diff --git a/src/system/SystemConfig.h b/src/system/SystemConfig.h
index 2ad29e073af22c..35573ec09ba594 100644
--- a/src/system/SystemConfig.h
+++ b/src/system/SystemConfig.h
@@ -111,14 +111,6 @@
 
 // clang-format off
 
-#ifndef CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
-#define CHIP_SYSTEM_CONFIG_USE_IO_THREAD 1
-#else
-#define CHIP_SYSTEM_CONFIG_USE_IO_THREAD 0
-#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
-#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-
 /**
  *  @def CHIP_SYSTEM_CONFIG_TRANSFER_INETLAYER_PROJECT_CONFIGURATION
  *
diff --git a/src/system/SystemLayer.cpp b/src/system/SystemLayer.cpp
index 681e8288db1d19..a527234dd23918 100644
--- a/src/system/SystemLayer.cpp
+++ b/src/system/SystemLayer.cpp
@@ -133,11 +133,6 @@ CHIP_ERROR Layer::Init()
     this->AddEventHandlerDelegate(sSystemEventHandlerDelegate);
 #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
 
-#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
-    // Create an event to allow an arbitrary thread to wake the thread in the select loop.
-    ReturnErrorOnFailure(this->mWakeEvent.Open(mWatchableEvents));
-#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
-
     this->mLayerState = kLayerState_Initialized;
 
     return CHIP_NO_ERROR;
@@ -148,10 +143,6 @@ CHIP_ERROR Layer::Shutdown()
     if (this->mLayerState == kLayerState_NotInitialized)
         return CHIP_ERROR_INCORRECT_STATE;
 
-#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
-    ReturnErrorOnFailure(mWakeEvent.Close());
-#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
-
     for (size_t i = 0; i < Timer::sPool.Size(); ++i)
     {
         Timer * lTimer = Timer::sPool.Get(*this, i);
@@ -455,42 +446,6 @@ void Layer::HandleTimeout()
 
 #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
 
-#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-
-/**
- * Wake up the I/O thread by writing a single byte to the wake pipe.
- *
- *  @note
- *      If @p WakeIOThread() is being called from within an I/O event callback, then writing to the wake pipe can be skipped,
- * since the I/O thread is already awake.
- *
- *      Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which
- *      case the select calling thread is going to wake up anyway.
- */
-void Layer::WakeIOThread()
-{
-    CHIP_ERROR lReturn;
-
-    if (this->State() != kLayerState_Initialized)
-        return;
-
-#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
-    if (pthread_equal(this->mHandleSelectThread, pthread_self()))
-    {
-        return;
-    }
-#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
-
-    // Send notification to wake up the select call.
-    lReturn = this->mWakeEvent.Notify();
-    if (lReturn != CHIP_NO_ERROR)
-    {
-        ChipLogError(chipSystemLayer, "System wake event notify failed: %s", ErrorStr(lReturn));
-    }
-}
-
-#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-
 #if CHIP_SYSTEM_CONFIG_USE_LWIP
 LwIPEventHandlerDelegate Layer::sSystemEventHandlerDelegate;
 
diff --git a/src/system/SystemLayer.h b/src/system/SystemLayer.h
index e2eba02ed60e94..7ab9ae174d5fd7 100644
--- a/src/system/SystemLayer.h
+++ b/src/system/SystemLayer.h
@@ -39,7 +39,8 @@
 
 // Include dependent headers
 #if CHIP_SYSTEM_CONFIG_USE_SOCKETS
-#include <system/SystemSockets.h>
+#include <system/WakeEvent.h>
+#include <system/WatchableEventManager.h>
 #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
 
 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
@@ -136,14 +137,11 @@ class DLL_EXPORT Layer
 
     Clock & GetClock() { return mClock; }
 
-#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
+#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
     WatchableEventManager & WatchableEvents() { return mWatchableEvents; }
     bool GetTimeout(struct timeval & aSleepTime); // TODO(#5556): Integrate timer platform details with WatchableEventManager.
     void HandleTimeout();                         // TODO(#5556): Integrate timer platform details with WatchableEventManager.
-#endif                                            // CHIP_SYSTEM_CONFIG_USE_SOCKETS
-#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-    void WakeIOThread();
-#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD
+#endif                                            // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
 
 #if CHIP_SYSTEM_CONFIG_USE_LWIP
     typedef CHIP_ERROR (*EventHandler)(Object & aTarget, EventType aEventType, uintptr_t aArgument);
@@ -172,8 +170,8 @@ class DLL_EXPORT Layer
 
 #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
     WatchableEventManager mWatchableEvents;
-    WakeEvent mWakeEvent;
 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
+    friend class WatchableEventManager;
     std::atomic<pthread_t> mHandleSelectThread;
 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
 #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
diff --git a/src/system/SystemTimer.cpp b/src/system/SystemTimer.cpp
index e55c659f70f333..1191f6135626b7 100644
--- a/src/system/SystemTimer.cpp
+++ b/src/system/SystemTimer.cpp
@@ -184,9 +184,9 @@ CHIP_ERROR Timer::Start(uint32_t aDelayMilliseconds, OnCompleteFunct aOnComplete
     }
 #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
 
-#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-    lLayer.WakeIOThread();
-#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD
+#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
+    lLayer.WatchableEvents().Signal();
+#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
 #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
 
     return CHIP_NO_ERROR;
@@ -220,7 +220,7 @@ CHIP_ERROR Timer::ScheduleWork(OnCompleteFunct aOnComplete, void * aAppState)
     else
     {
 #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
-        lLayer.WakeIOThread();
+        lLayer.WatchableEvents().Signal();
 #if CHIP_SYSTEM_CONFIG_USE_DISPATCH
     }
 #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
diff --git a/src/system/SystemSockets.cpp b/src/system/WakeEvent.cpp
similarity index 97%
rename from src/system/SystemSockets.cpp
rename to src/system/WakeEvent.cpp
index bea24b6536aca2..e0e387a7c273ca 100644
--- a/src/system/SystemSockets.cpp
+++ b/src/system/WakeEvent.cpp
@@ -21,12 +21,15 @@
  *      data stream built on top of two file descriptors.
  */
 
-#include <system/SystemSockets.h>
+#include <system/SystemConfig.h>
 
 #if CHIP_SYSTEM_CONFIG_USE_SOCKETS
 
+#include <system/WakeEvent.h>
+
 // Include additional CHIP headers
 #include <support/CodeUtils.h>
+#include <system/SystemError.h>
 
 // Include system and language headers
 #include <errno.h>
diff --git a/src/system/WakeEvent.h b/src/system/WakeEvent.h
new file mode 100644
index 00000000000000..54465ede3bf3ce
--- /dev/null
+++ b/src/system/WakeEvent.h
@@ -0,0 +1,69 @@
+/*
+ *
+ *    Copyright (c) 2020-2021 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file declares the abstraction of socket (file descriptor) events.
+ */
+
+#pragma once
+
+// Include configuration headers
+#include <system/SystemConfig.h>
+
+#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
+
+#include <core/CHIPError.h>
+#include <system/WatchableSocket.h>
+
+namespace chip {
+namespace System {
+
+class WakeEventTest;
+class WatchableEventManager;
+
+/**
+ * @class WakeEvent
+ *
+ * An instance of this type can be used by a WatchableEventManager to allow other threads
+ * to wake its event loop thread via WatchableEventManager::Signal().
+ */
+class WakeEvent
+{
+public:
+    CHIP_ERROR Open(WatchableEventManager & watchState); /**< Initialize the pipeline */
+    CHIP_ERROR Close();                                  /**< Close both ends of the pipeline. */
+
+    CHIP_ERROR Notify(); /**< Set the event. */
+    void Confirm();      /**< Clear the event. */
+
+private:
+    friend class WakeEventTest;
+
+    int GetReadFD() const { return mFD.GetFD(); }
+    static void Confirm(WatchableSocket & socket) { reinterpret_cast<WakeEvent *>(socket.GetCallbackData())->Confirm(); }
+
+#if CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE
+    int mWriteFD;
+#endif
+    WatchableSocket mFD;
+};
+
+} // namespace System
+} // namespace chip
+
+#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
diff --git a/src/system/WatchableEventManager.h b/src/system/WatchableEventManager.h
new file mode 100644
index 00000000000000..bf226453974cd2
--- /dev/null
+++ b/src/system/WatchableEventManager.h
@@ -0,0 +1,72 @@
+/*
+ *
+ *    Copyright (c) 2020-2021 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file declares the abstraction for managing a collection of socket (file descriptor) events.
+ */
+
+#pragma once
+
+// Include configuration headers
+#include <system/SystemConfig.h>
+
+#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
+
+namespace chip {
+
+namespace System {
+
+class Layer;
+
+/**
+ * @class WatchableEventManager
+ *
+ * An instance of this type is contained in System::Layer. Its purpose is to hold socket-event system state
+ * or methods available to every associated instance of WatchableSocket.
+ *
+ * It MUST provide at least three methods:
+ *
+ * - void Init(System::Layer & systemLayer) -- called from System::Layer::Init()
+ * - void Shutdown()                        -- called from System::Layer::Shutdown()
+ * - void Signal()                          -- called to indicate that event monitoring may need to be refreshed or resumed.
+ *
+ * Other contents depend on the contract between socket-event implementation and platform layer implementation.
+ * For POSIX-like platforms, WatchableEventManager provides a set of functions called from the event loop:
+ *
+ * - void EventLoopBegins()  -- Called before the first iterations of the event loop.
+ * - void PrepareEvents()    -- Called at the start of each iteration of the event loop.
+ * - void WaitForEvents()    -- Called on each iteration of the event loop, between PrepareEvents() and HandleEvents().
+ *                              Uniquely, this method gets called with the CHIP stack NOT locked, so it can block.
+ *                              For example, the select()-based implementation calls select() here.
+ * - void HandleEvents()     -- Called at the end of each iteration of the event loop.
+ * - void EventLoopEnds()    -- Called after the last iteration of the event loop.
+ */
+class WatchableEventManager;
+
+} // namespace System
+} // namespace chip
+
+#define INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE 1
+#ifdef CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+#include CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+#else // CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+#include <system/WatchableEventManagerSelect.h>
+#endif // CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+#undef INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+
+#endif // CHIP_SYSTEM_CONFIG_USE_EVENT_MANAGERS
diff --git a/src/system/WatchableEventManagerLibevent.cpp b/src/system/WatchableEventManagerLibevent.cpp
new file mode 100644
index 00000000000000..8517b290a54c27
--- /dev/null
+++ b/src/system/WatchableEventManagerLibevent.cpp
@@ -0,0 +1,196 @@
+/*
+ *
+ *    Copyright (c) 2021 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file implements WatchableEventManager using libevent.
+ */
+
+#include <platform/CHIPDeviceBuildConfig.h>
+#include <support/CodeUtils.h>
+#include <system/SystemLayer.h>
+#include <system/WatchableEventManager.h>
+#include <system/WatchableSocket.h>
+
+#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
+
+namespace chip {
+namespace Mdns {
+void GetMdnsTimeout(timeval & timeout);
+void HandleMdnsTimeout();
+} // namespace Mdns
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
+
+#ifndef CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS
+#define CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS 1 // TODO(#5556): default to off
+#endif
+
+namespace chip {
+namespace System {
+
+namespace {
+
+System::SocketEvents SocketEventsFromLibeventFlags(short eventFlags)
+{
+    return System::SocketEvents()
+        .Set(SocketEventFlags::kRead, eventFlags & EV_READ)
+        .Set(SocketEventFlags::kWrite, eventFlags & EV_WRITE);
+}
+
+void TimeoutCallbackHandler(evutil_socket_t fd, short eventFlags, void * data)
+{
+    event * const ev = reinterpret_cast<event *>(data);
+    evtimer_del(ev);
+}
+
+} // anonymous namespace
+
+void WatchableEventManager::Init(System::Layer & systemLayer)
+{
+#if CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS
+    static bool enabled_event_debug_mode = false;
+    if (!enabled_event_debug_mode)
+    {
+        enabled_event_debug_mode = true;
+        event_enable_debug_mode();
+    }
+#endif // CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS
+
+    mEventBase     = event_base_new();
+    mTimeoutEvent  = evtimer_new(mEventBase, TimeoutCallbackHandler, event_self_cbarg());
+    mActiveSockets = nullptr;
+    mSystemLayer   = &systemLayer;
+}
+
+void WatchableEventManager::PrepareEvents()
+{
+    // TODO(#5556): Integrate timer platform details with WatchableEventManager.
+    timeval nextTimeout = { 0, 0 };
+    PrepareEventsWithTimeout(nextTimeout);
+}
+
+void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout)
+{
+    // TODO(#5556): Integrate timer platform details with WatchableEventManager.
+    mSystemLayer->GetTimeout(nextTimeout);
+    if (nextTimeout.tv_sec || nextTimeout.tv_usec)
+    {
+        evtimer_add(mTimeoutEvent, &nextTimeout);
+    }
+}
+
+void WatchableEventManager::WaitForEvents()
+{
+    VerifyOrDie(mEventBase != nullptr);
+    event_base_loop(mEventBase, EVLOOP_ONCE);
+}
+
+void WatchableEventManager::HandleEvents()
+{
+    mSystemLayer->HandleTimeout();
+
+#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
+    chip::Mdns::HandleMdnsTimeout();
+#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
+
+    while (mActiveSockets != nullptr)
+    {
+        WatchableSocket * const watcher = mActiveSockets;
+        mActiveSockets                  = watcher->mActiveNext;
+        watcher->InvokeCallback();
+    }
+}
+
+void WatchableEventManager::Shutdown()
+{
+    event_base_loopbreak(mEventBase);
+    event_free(mTimeoutEvent);
+    mTimeoutEvent = nullptr;
+    event_base_free(mEventBase);
+    mEventBase = nullptr;
+}
+
+void WatchableEventManager::Signal()
+{
+    /*
+     * Wake up the I/O thread by writing a single byte to the wake pipe.
+     *
+     * If p WakeIOThread() is being called from within an I/O event callback, then writing to the wake pipe can be skipped,
+     * since the I/O thread is already awake.
+     *
+     * Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which
+     * case the select calling thread is going to wake up anyway.
+     */
+#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
+    if (pthread_equal(mSystemLayer->mHandleSelectThread, pthread_self()))
+    {
+        return;
+    }
+#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
+
+    // Send notification to wake up the select call.
+    CHIP_ERROR status = mWakeEvent.Notify();
+    if (status != CHIP_NO_ERROR)
+    {
+        ChipLogError(chipSystemLayer, "System wake event notify failed: %" CHIP_ERROR_FORMAT, ChipError::FormatError(status));
+    }
+}
+
+// static
+void WatchableEventManager::LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data)
+{
+    WatchableSocket * const watcher = reinterpret_cast<WatchableSocket *>(data);
+    VerifyOrDie(watcher != nullptr);
+    VerifyOrDie(watcher->mFD == fd);
+
+    watcher->mPendingIO = SocketEventsFromLibeventFlags(eventFlags);
+
+    // Add to active list.
+    WatchableSocket ** pp = &watcher->mSharedState->mActiveSockets;
+    while (*pp != nullptr)
+    {
+        if (*pp == watcher)
+        {
+            return;
+        }
+        pp = &(*pp)->mActiveNext;
+    }
+    *pp                  = watcher;
+    watcher->mActiveNext = nullptr;
+}
+
+void WatchableEventManager::RemoveFromQueueIfPresent(WatchableSocket * watcher)
+{
+    VerifyOrDie(watcher != nullptr);
+    VerifyOrDie(watcher->mSharedState == this);
+
+    WatchableSocket ** pp = &mActiveSockets;
+    while (*pp != nullptr)
+    {
+        if (*pp == watcher)
+        {
+            *pp = watcher->mActiveNext;
+            return;
+        }
+        pp = &(*pp)->mActiveNext;
+    }
+}
+
+} // namespace System
+} // namespace chip
diff --git a/src/system/WatchableEventManagerLibevent.h b/src/system/WatchableEventManagerLibevent.h
new file mode 100644
index 00000000000000..828b3565a6a273
--- /dev/null
+++ b/src/system/WatchableEventManagerLibevent.h
@@ -0,0 +1,73 @@
+/*
+ *
+ *    Copyright (c) 2021 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file declares an implementation of WatchableEvents using libevent.
+ */
+
+#pragma once
+
+#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+#error "This file should only be included from <system/WatchableEventManager.h>"
+#include <system/WatchableEventManager.h>
+#endif //  !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+
+#include <event2/event.h>
+
+namespace chip {
+
+namespace System {
+
+class WatchableEventManager
+{
+public:
+    WatchableEventManager() : mActiveSockets(nullptr), mSystemLayer(nullptr), mEventBase(nullptr), mTimeoutEvent(nullptr) {}
+    void Init(Layer & systemLayer);
+    void Shutdown();
+    void Signal();
+
+    void EventLoopBegins() {}
+    void PrepareEvents();
+    void WaitForEvents();
+    void HandleEvents();
+    void EventLoopEnds() {}
+
+    // TODO(#5556): Some unit tests supply a timeout at low level, due to originally using select(); these should a proper timer.
+    void PrepareEventsWithTimeout(timeval & nextTimeout);
+
+private:
+    /*
+     * In this implementation, libevent invokes LibeventCallbackHandler from beneath WaitForEvents(),
+     * which means that the CHIP stack is unlocked. LibeventCallbackHandler adds the WatchableSocket
+     * to a queue (implemented as a simple intrusive list to avoid dynamic memory allocation), and
+     * then HandleEvents() invokes the WatchableSocket callbacks.
+     */
+    friend class WatchableSocket;
+    static void LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data);
+    void RemoveFromQueueIfPresent(WatchableSocket * watcher);
+    WatchableSocket * mActiveSockets; ///< List of sockets activated by libevent.
+
+    Layer * mSystemLayer;
+    event_base * mEventBase; ///< libevent shared state.
+    event * mTimeoutEvent;
+
+    WakeEvent mWakeEvent;
+};
+
+} // namespace System
+} // namespace chip
diff --git a/src/system/WatchableEventManagerSelect.cpp b/src/system/WatchableEventManagerSelect.cpp
new file mode 100644
index 00000000000000..f1f975b3bbd18e
--- /dev/null
+++ b/src/system/WatchableEventManagerSelect.cpp
@@ -0,0 +1,233 @@
+/*
+ *
+ *    Copyright (c) 2020-2021 Project CHIP Authors
+ *    Copyright (c) 2014-2017 Nest Labs, Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file implements WatchableEventManager using select().
+ */
+
+#include <platform/LockTracker.h>
+#include <support/CodeUtils.h>
+#include <system/SystemLayer.h>
+#include <system/WatchableEventManager.h>
+#include <system/WatchableSocket.h>
+
+#include <errno.h>
+
+#define DEFAULT_MIN_SLEEP_PERIOD (60 * 60 * 24 * 30) // Month [sec]
+
+#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
+
+namespace chip {
+namespace Mdns {
+void GetMdnsTimeout(timeval & timeout);
+void HandleMdnsTimeout();
+} // namespace Mdns
+} // namespace chip
+
+#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
+
+namespace chip {
+namespace System {
+
+void WatchableEventManager::Init(Layer & systemLayer)
+{
+    mSystemLayer = &systemLayer;
+    mMaxFd       = -1;
+    FD_ZERO(&mRequest.mReadSet);
+    FD_ZERO(&mRequest.mWriteSet);
+    FD_ZERO(&mRequest.mErrorSet);
+
+    // Create an event to allow an arbitrary thread to wake the thread in the select loop.
+    mWakeEvent.Open(*this);
+}
+
+void WatchableEventManager::Shutdown()
+{
+    mWakeEvent.Close();
+    mSystemLayer = nullptr;
+}
+
+void WatchableEventManager::Signal()
+{
+    /*
+     * Wake up the I/O thread by writing a single byte to the wake pipe.
+     *
+     * If p WakeIOThread() is being called from within an I/O event callback, then writing to the wake pipe can be skipped,
+     * since the I/O thread is already awake.
+     *
+     * Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which
+     * case the select calling thread is going to wake up anyway.
+     */
+#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
+    if (pthread_equal(mSystemLayer->mHandleSelectThread, pthread_self()))
+    {
+        return;
+    }
+#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING
+
+    // Send notification to wake up the select call.
+    CHIP_ERROR status = mWakeEvent.Notify();
+    if (status != CHIP_NO_ERROR)
+    {
+        ChipLogError(chipSystemLayer, "System wake event notify failed: %" CHIP_ERROR_FORMAT, ChipError::FormatError(status));
+    }
+}
+
+/**
+ *  Set the read, write or exception bit flags for the specified socket based on its status in
+ *  the corresponding file descriptor sets.
+ *
+ *  @param[in]    socket    The file descriptor for which the bit flags are being set.
+ *
+ *  @param[in]    readfds   A pointer to the set of readable file descriptors.
+ *
+ *  @param[in]    writefds  A pointer to the set of writable file descriptors.
+ *
+ *  @param[in]    exceptfds  A pointer to the set of file descriptors with errors.
+ */
+SocketEvents WatchableEventManager::SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds,
+                                                        const fd_set & exceptfds)
+{
+    SocketEvents res;
+
+    if (socket >= 0)
+    {
+        // POSIX does not define the fd_set parameter of FD_ISSET() as const, even though it isn't modified.
+        if (FD_ISSET(socket, const_cast<fd_set *>(&readfds)))
+            res.Set(SocketEventFlags::kRead);
+        if (FD_ISSET(socket, const_cast<fd_set *>(&writefds)))
+            res.Set(SocketEventFlags::kWrite);
+        if (FD_ISSET(socket, const_cast<fd_set *>(&exceptfds)))
+            res.Set(SocketEventFlags::kExcept);
+    }
+
+    return res;
+}
+
+bool WatchableEventManager::HasAny(int fd)
+{
+    return FD_ISSET(fd, &mRequest.mReadSet) || FD_ISSET(fd, &mRequest.mWriteSet) || FD_ISSET(fd, &mRequest.mErrorSet);
+}
+
+void WatchableEventManager::Set(int fd, fd_set * fds)
+{
+    FD_SET(fd, fds);
+    if (fd > mMaxFd)
+    {
+        mMaxFd = fd;
+    }
+    // Wake the thread calling select so that it starts selecting on the new socket.
+    Signal();
+}
+
+void WatchableEventManager::Clear(int fd, fd_set * fds)
+{
+    FD_CLR(fd, fds);
+    if (fd == mMaxFd)
+    {
+        MaybeLowerMaxFd();
+    }
+    // Wake the thread calling select so that it starts selecting on the new socket.
+    Signal();
+}
+
+void WatchableEventManager::Reset(int fd)
+{
+    FD_CLR(fd, &mRequest.mReadSet);
+    FD_CLR(fd, &mRequest.mWriteSet);
+    FD_CLR(fd, &mRequest.mErrorSet);
+    if (fd == mMaxFd)
+    {
+        MaybeLowerMaxFd();
+    }
+}
+
+void WatchableEventManager::MaybeLowerMaxFd()
+{
+    int fd;
+    for (fd = mMaxFd; fd >= 0; --fd)
+    {
+        if (HasAny(fd))
+        {
+            break;
+        }
+    }
+    mMaxFd = fd;
+}
+
+void WatchableEventManager::PrepareEvents()
+{
+    assertChipStackLockedByCurrentThread();
+
+    // Max out this duration and let CHIP set it appropriately.
+    mNextTimeout.tv_sec  = DEFAULT_MIN_SLEEP_PERIOD;
+    mNextTimeout.tv_usec = 0;
+    PrepareEventsWithTimeout(mNextTimeout);
+}
+
+void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout)
+{
+    // TODO(#5556): Integrate timer platform details with WatchableEventManager.
+    mSystemLayer->GetTimeout(nextTimeout);
+
+#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__
+    chip::Mdns::GetMdnsTimeout(nextTimeout);
+#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
+
+    mSelected = mRequest;
+}
+
+void WatchableEventManager::WaitForEvents()
+{
+    mSelectResult = select(mMaxFd + 1, &mSelected.mReadSet, &mSelected.mWriteSet, &mSelected.mErrorSet, &mNextTimeout);
+}
+
+void WatchableEventManager::HandleEvents()
+{
+    assertChipStackLockedByCurrentThread();
+
+    if (mSelectResult < 0)
+    {
+        ChipLogError(DeviceLayer, "select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno)));
+        return;
+    }
+
+    VerifyOrDie(mSystemLayer != nullptr);
+    mSystemLayer->HandleTimeout();
+
+    for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext)
+    {
+        watchable->SetPendingIO(
+            SocketEventsFromFDs(watchable->GetFD(), mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet));
+    }
+    for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext)
+    {
+        if (watchable->mPendingIO.HasAny())
+        {
+            watchable->InvokeCallback();
+        }
+    }
+
+#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__
+    chip::Mdns::HandleMdnsTimeout();
+#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
+}
+
+} // namespace System
+} // namespace chip
diff --git a/src/system/WatchableEventManagerSelect.h b/src/system/WatchableEventManagerSelect.h
new file mode 100644
index 00000000000000..a7bc285b8536a3
--- /dev/null
+++ b/src/system/WatchableEventManagerSelect.h
@@ -0,0 +1,92 @@
+/*
+ *
+ *    Copyright (c) 2021 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file declares an implementation of WatchableEventManager using select().
+ */
+
+#pragma once
+
+#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+#error "This file should only be included from <system/WatchableEventManager.h>"
+#include <system/WatchableEventManager.h>
+#endif //  !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE
+
+#include <sys/select.h>
+
+#include <support/BitFlags.h>
+#include <support/logging/CHIPLogging.h>
+#include <system/WakeEvent.h>
+
+namespace chip {
+namespace System {
+
+class WatchableSocket;
+
+class WatchableEventManager
+{
+public:
+    void Init(System::Layer & systemLayer);
+    void Shutdown();
+    void Signal();
+
+    void EventLoopBegins() {}
+    void PrepareEvents();
+    void WaitForEvents();
+    void HandleEvents();
+    void EventLoopEnds() {}
+
+    // TODO(#5556): Some unit tests supply a timeout at low level, due to originally using select(); these should a proper timer.
+    void PrepareEventsWithTimeout(timeval & nextTimeout);
+
+    static SocketEvents SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds, const fd_set & exceptfds);
+
+protected:
+    friend class WatchableSocket;
+
+    void Set(int fd, fd_set * fds);
+    void Clear(int fd, fd_set * fds);
+
+    Layer * mSystemLayer               = nullptr;
+    WatchableSocket * mAttachedSockets = nullptr;
+
+    // TODO(#5556): Integrate timer platform details with WatchableEventManager.
+    struct timeval mNextTimeout;
+
+    // Members for select loop
+    struct SelectSets
+    {
+        fd_set mReadSet;
+        fd_set mWriteSet;
+        fd_set mErrorSet;
+    };
+    SelectSets mRequest;
+    SelectSets mSelected;
+    int mMaxFd;
+    int mSelectResult; ///< return value from select()
+
+    WakeEvent mWakeEvent;
+
+private:
+    bool HasAny(int fd);
+    void MaybeLowerMaxFd();
+    void Reset(int fd);
+};
+
+} // namespace System
+} // namespace chip
diff --git a/src/system/SystemSockets.h b/src/system/WatchableSocket.h
similarity index 77%
rename from src/system/SystemSockets.h
rename to src/system/WatchableSocket.h
index c2ee88d1b7f1c2..32ed69aa21a8e6 100644
--- a/src/system/SystemSockets.h
+++ b/src/system/WatchableSocket.h
@@ -17,7 +17,7 @@
 
 /**
  *    @file
- *      This file declares the abstraction of socket (file descriptor) events.
+ *      This file declares the WatchableSocket abstraction of socket (file descriptor) events.
  */
 
 #pragma once
@@ -27,16 +27,16 @@
 
 #if CHIP_SYSTEM_CONFIG_USE_SOCKETS
 
-#include <support/BitFlags.h>
-#include <system/SystemError.h>
-
 #include <unistd.h>
 
+#include <support/BitFlags.h>
+
 namespace chip {
 
 namespace System {
 
 class Layer;
+class WatchableEventManager;
 
 enum class SocketEventFlags : uint8_t
 {
@@ -48,30 +48,6 @@ enum class SocketEventFlags : uint8_t
 
 using SocketEvents = BitFlags<SocketEventFlags>;
 
-/**
- * @class WatchableEventManager
- *
- * An instance of this type is contained in System::Layer. Its purpose is to hold socket-event system state
- * or methods available to every associated instance of WatchableSocket.
- *
- * It MUST provide at least two methods:
- *
- *  void Init(System::Layer & systemLayer) -- called from System::Layer::Init()
- *  void Shutdown()                        -- called from System::Layer::Shutdown()
- *
- * Other contents depend on the contract between socket-event implementation and platform layer implementation.
- * For POSIX-like platforms, WatchableEventManager provides a set of functions called from the event loop:
- *
- *  void EventLoopBegins()  -- Called before the first iterations of the event loop.
- *  void PrepareEvents()    -- Called at the start of each iteration of the event loop.
- *  void WaitForEvents()    -- Called on each iteration of the event loop, between PrepareEvents() and HandleEvents().
- *                             Uniquely, this method gets called with the CHIP stack NOT locked, so it can block.
- *                             For example, the select()-based implementation calls select() here.
- *  void HandleEvents()     -- Called at the end of each iteration of the event loop.
- *  void EventLoopEnds()    -- Called after the last iteration of the event loop.
- */
-class WatchableEventManager;
-
 /**
  * @class WatchableSocket
  *
@@ -306,35 +282,4 @@ class WatchableSocketBasis
 #endif // CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE
 #undef INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE
 
-namespace chip {
-namespace System {
-
-/**
- * @class WakeEvent
- *
- * An instance of this type is contained in System::Layer. Its purpose is to allow other threads
- * to wake the event loop thread via System::Layer::WakeIOThread().
- */
-class WakeEvent
-{
-public:
-    CHIP_ERROR Open(WatchableEventManager & watchState); /**< Initialize the pipeline */
-    CHIP_ERROR Close();                                  /**< Close both ends of the pipeline. */
-
-    int GetNotifFD() const { return mFD.GetFD(); }
-
-    CHIP_ERROR Notify(); /**< Set the event. */
-    void Confirm();      /**< Clear the event. */
-    static void Confirm(WatchableSocket & socket) { reinterpret_cast<WakeEvent *>(socket.GetCallbackData())->Confirm(); }
-
-private:
-#if CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE
-    int mWriteFD;
-#endif
-    WatchableSocket mFD;
-};
-
-} // namespace System
-} // namespace chip
-
 #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
diff --git a/src/system/WatchableSocketLibevent.cpp b/src/system/WatchableSocketLibevent.cpp
index a420c7de069ac5..0913c97857432b 100644
--- a/src/system/WatchableSocketLibevent.cpp
+++ b/src/system/WatchableSocketLibevent.cpp
@@ -17,154 +17,18 @@
 
 /**
  *    @file
- *      This file implements WatchableEvents using libevent.
+ *      This file implements WatchableSocket using libevent.
  */
 
 #include <platform/CHIPDeviceBuildConfig.h>
 #include <support/CodeUtils.h>
 #include <system/SystemLayer.h>
-#include <system/SystemSockets.h>
-
-#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
-
-namespace chip {
-namespace Mdns {
-void GetMdnsTimeout(timeval & timeout);
-void HandleMdnsTimeout();
-} // namespace Mdns
-} // namespace chip
-
-#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
-
-#ifndef CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS
-#define CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS 1 // TODO(#5556): default to off
-#endif
+#include <system/WatchableEventManager.h>
+#include <system/WatchableSocket.h>
 
 namespace chip {
 namespace System {
 
-namespace {
-
-System::SocketEvents SocketEventsFromLibeventFlags(short eventFlags)
-{
-    return System::SocketEvents()
-        .Set(SocketEventFlags::kRead, eventFlags & EV_READ)
-        .Set(SocketEventFlags::kWrite, eventFlags & EV_WRITE);
-}
-
-void TimeoutCallbackHandler(evutil_socket_t fd, short eventFlags, void * data)
-{
-    event * const ev = reinterpret_cast<event *>(data);
-    evtimer_del(ev);
-}
-
-} // anonymous namespace
-
-void WatchableEventManager::Init(System::Layer & systemLayer)
-{
-#if CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS
-    static bool enabled_event_debug_mode = false;
-    if (!enabled_event_debug_mode)
-    {
-        enabled_event_debug_mode = true;
-        event_enable_debug_mode();
-    }
-#endif // CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS
-
-    mEventBase     = event_base_new();
-    mTimeoutEvent  = evtimer_new(mEventBase, TimeoutCallbackHandler, event_self_cbarg());
-    mActiveSockets = nullptr;
-    mSystemLayer   = &systemLayer;
-}
-
-void WatchableEventManager::PrepareEvents()
-{
-    // TODO(#5556): Integrate timer platform details with WatchableEventManager.
-    timeval nextTimeout = { 0, 0 };
-    PrepareEventsWithTimeout(nextTimeout);
-}
-
-void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout)
-{
-    // TODO(#5556): Integrate timer platform details with WatchableEventManager.
-    mSystemLayer->GetTimeout(nextTimeout);
-    if (nextTimeout.tv_sec || nextTimeout.tv_usec)
-    {
-        evtimer_add(mTimeoutEvent, &nextTimeout);
-    }
-}
-
-void WatchableEventManager::WaitForEvents()
-{
-    VerifyOrDie(mEventBase != nullptr);
-    event_base_loop(mEventBase, EVLOOP_ONCE);
-}
-
-void WatchableEventManager::HandleEvents()
-{
-    mSystemLayer->HandleTimeout();
-
-#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
-    chip::Mdns::HandleMdnsTimeout();
-#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
-
-    while (mActiveSockets != nullptr)
-    {
-        WatchableSocket * const watcher = mActiveSockets;
-        mActiveSockets                  = watcher->mActiveNext;
-        watcher->InvokeCallback();
-    }
-}
-
-void WatchableEventManager::Shutdown()
-{
-    event_base_loopbreak(mEventBase);
-    event_free(mTimeoutEvent);
-    mTimeoutEvent = nullptr;
-    event_base_free(mEventBase);
-    mEventBase = nullptr;
-}
-
-// static
-void WatchableEventManager::LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data)
-{
-    WatchableSocket * const watcher = reinterpret_cast<WatchableSocket *>(data);
-    VerifyOrDie(watcher != nullptr);
-    VerifyOrDie(watcher->mFD == fd);
-
-    watcher->mPendingIO = SocketEventsFromLibeventFlags(eventFlags);
-
-    // Add to active list.
-    WatchableSocket ** pp = &watcher->mSharedState->mActiveSockets;
-    while (*pp != nullptr)
-    {
-        if (*pp == watcher)
-        {
-            return;
-        }
-        pp = &(*pp)->mActiveNext;
-    }
-    *pp                  = watcher;
-    watcher->mActiveNext = nullptr;
-}
-
-void WatchableEventManager::RemoveFromQueueIfPresent(WatchableSocket * watcher)
-{
-    VerifyOrDie(watcher != nullptr);
-    VerifyOrDie(watcher->mSharedState == this);
-
-    WatchableSocket ** pp = &mActiveSockets;
-    while (*pp != nullptr)
-    {
-        if (*pp == watcher)
-        {
-            *pp = watcher->mActiveNext;
-            return;
-        }
-        pp = &(*pp)->mActiveNext;
-    }
-}
-
 void WatchableSocket::OnInit()
 {
     mEvent      = nullptr;
@@ -176,6 +40,12 @@ void WatchableSocket::OnAttach()
     evutil_make_socket_nonblocking(mFD);
 }
 
+void WatchableSocket::OnClose()
+{
+    UpdateWatch(0);
+    mSharedState->RemoveFromQueueIfPresent(this);
+}
+
 void WatchableSocket::SetWatch(short eventFlags)
 {
     const short oldFlags = mEvent ? event_get_events(mEvent) : 0;
diff --git a/src/system/WatchableSocketLibevent.h b/src/system/WatchableSocketLibevent.h
index 563e9d794ceeb7..be2448af810190 100644
--- a/src/system/WatchableSocketLibevent.h
+++ b/src/system/WatchableSocketLibevent.h
@@ -23,7 +23,8 @@
 #pragma once
 
 #if !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE
-#error "This file should only be included from <system/SystemSockets.h>"
+#error "This file should only be included from <system/WatchableSocket.h>"
+#include <system/WatchableSocket.h>
 #endif //  !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE
 
 #include <event2/event.h>
@@ -32,49 +33,14 @@ namespace chip {
 
 namespace System {
 
-class WatchableEventManager
-{
-public:
-    WatchableEventManager() : mActiveSockets(nullptr), mSystemLayer(nullptr), mEventBase(nullptr), mTimeoutEvent(nullptr) {}
-    void Init(Layer & systemLayer);
-    void Shutdown();
-
-    void EventLoopBegins() {}
-    void PrepareEvents();
-    void WaitForEvents();
-    void HandleEvents();
-    void EventLoopEnds() {}
-
-    // TODO(#5556): Some unit tests supply a timeout at low level, due to originally using select(); these should a proper timer.
-    void PrepareEventsWithTimeout(timeval & nextTimeout);
-
-private:
-    /*
-     * In this implementation, libevent invokes LibeventCallbackHandler from beneath WaitForEvents(),
-     * which means that the CHIP stack is unlocked. LibeventCallbackHandler adds the WatchableSocket
-     * to a queue (implemented as a simple intrusive list to avoid dynamic memory allocation), and
-     * then HandleEvents() invokes the WatchableSocket callbacks.
-     */
-    friend class WatchableSocket;
-    static void LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data);
-    void RemoveFromQueueIfPresent(WatchableSocket * watcher);
-    WatchableSocket * mActiveSockets; ///< List of sockets activated by libevent.
-
-    Layer * mSystemLayer;
-    event_base * mEventBase; ///< libevent shared state.
-    event * mTimeoutEvent;
-};
+class WatchableEventManager;
 
 class WatchableSocket : public WatchableSocketBasis<WatchableSocket>
 {
 public:
     void OnInit();
     void OnAttach();
-    void OnClose()
-    {
-        UpdateWatch(0);
-        mSharedState->RemoveFromQueueIfPresent(this);
-    }
+    void OnClose();
     void OnRequestCallbackOnPendingRead() { SetWatch(EV_READ); }
     void OnRequestCallbackOnPendingWrite() { SetWatch(EV_WRITE); }
     void OnClearCallbackOnPendingRead() { ClearWatch(EV_READ); }
diff --git a/src/system/WatchableSocketSelect.cpp b/src/system/WatchableSocketSelect.cpp
index 0b28fd8c3d12d3..04f8dbb72020c8 100644
--- a/src/system/WatchableSocketSelect.cpp
+++ b/src/system/WatchableSocketSelect.cpp
@@ -18,193 +18,20 @@
 
 /**
  *    @file
- *      This file implements WatchableEvents using select().
+ *      This file implements WatchableSocket using select().
  */
 
 #include <platform/LockTracker.h>
 #include <support/CodeUtils.h>
 #include <system/SystemLayer.h>
-#include <system/SystemSockets.h>
+#include <system/WatchableEventManager.h>
+#include <system/WatchableSocket.h>
 
 #include <errno.h>
 
-#define DEFAULT_MIN_SLEEP_PERIOD (60 * 60 * 24 * 30) // Month [sec]
-
-#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
-
-namespace chip {
-namespace Mdns {
-void GetMdnsTimeout(timeval & timeout);
-void HandleMdnsTimeout();
-} // namespace Mdns
-} // namespace chip
-
-#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
-
 namespace chip {
 namespace System {
 
-void WatchableEventManager::Init(Layer & systemLayer)
-{
-    mSystemLayer = &systemLayer;
-    mMaxFd       = -1;
-    FD_ZERO(&mRequest.mReadSet);
-    FD_ZERO(&mRequest.mWriteSet);
-    FD_ZERO(&mRequest.mErrorSet);
-}
-
-void WatchableEventManager::Shutdown()
-{
-    mSystemLayer = nullptr;
-}
-
-/**
- *  Set the read, write or exception bit flags for the specified socket based on its status in
- *  the corresponding file descriptor sets.
- *
- *  @param[in]    socket    The file descriptor for which the bit flags are being set.
- *
- *  @param[in]    readfds   A pointer to the set of readable file descriptors.
- *
- *  @param[in]    writefds  A pointer to the set of writable file descriptors.
- *
- *  @param[in]    exceptfds  A pointer to the set of file descriptors with errors.
- */
-SocketEvents WatchableEventManager::SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds,
-                                                        const fd_set & exceptfds)
-{
-    SocketEvents res;
-
-    if (socket >= 0)
-    {
-        // POSIX does not define the fd_set parameter of FD_ISSET() as const, even though it isn't modified.
-        if (FD_ISSET(socket, const_cast<fd_set *>(&readfds)))
-            res.Set(SocketEventFlags::kRead);
-        if (FD_ISSET(socket, const_cast<fd_set *>(&writefds)))
-            res.Set(SocketEventFlags::kWrite);
-        if (FD_ISSET(socket, const_cast<fd_set *>(&exceptfds)))
-            res.Set(SocketEventFlags::kExcept);
-    }
-
-    return res;
-}
-
-bool WatchableEventManager::HasAny(int fd)
-{
-    return FD_ISSET(fd, &mRequest.mReadSet) || FD_ISSET(fd, &mRequest.mWriteSet) || FD_ISSET(fd, &mRequest.mErrorSet);
-}
-
-void WatchableEventManager::WakeSelect()
-{
-#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-    mSystemLayer->WakeIOThread();
-#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD
-}
-
-void WatchableEventManager::Set(int fd, fd_set * fds)
-{
-    FD_SET(fd, fds);
-    if (fd > mMaxFd)
-    {
-        mMaxFd = fd;
-    }
-    // Wake the thread calling select so that it starts selecting on the new socket.
-    WakeSelect();
-}
-
-void WatchableEventManager::Clear(int fd, fd_set * fds)
-{
-    FD_CLR(fd, fds);
-    if (fd == mMaxFd)
-    {
-        MaybeLowerMaxFd();
-    }
-    // Wake the thread calling select so that it starts selecting on the new socket.
-    WakeSelect();
-}
-
-void WatchableEventManager::Reset(int fd)
-{
-    FD_CLR(fd, &mRequest.mReadSet);
-    FD_CLR(fd, &mRequest.mWriteSet);
-    FD_CLR(fd, &mRequest.mErrorSet);
-    if (fd == mMaxFd)
-    {
-        MaybeLowerMaxFd();
-    }
-}
-
-void WatchableEventManager::MaybeLowerMaxFd()
-{
-    int fd;
-    for (fd = mMaxFd; fd >= 0; --fd)
-    {
-        if (HasAny(fd))
-        {
-            break;
-        }
-    }
-    mMaxFd = fd;
-}
-
-void WatchableEventManager::PrepareEvents()
-{
-    assertChipStackLockedByCurrentThread();
-
-    // Max out this duration and let CHIP set it appropriately.
-    mNextTimeout.tv_sec  = DEFAULT_MIN_SLEEP_PERIOD;
-    mNextTimeout.tv_usec = 0;
-    PrepareEventsWithTimeout(mNextTimeout);
-}
-
-void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout)
-{
-    // TODO(#5556): Integrate timer platform details with WatchableEventManager.
-    mSystemLayer->GetTimeout(nextTimeout);
-
-#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__
-    chip::Mdns::GetMdnsTimeout(nextTimeout);
-#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
-
-    mSelected = mRequest;
-}
-
-void WatchableEventManager::WaitForEvents()
-{
-    mSelectResult = select(mMaxFd + 1, &mSelected.mReadSet, &mSelected.mWriteSet, &mSelected.mErrorSet, &mNextTimeout);
-}
-
-void WatchableEventManager::HandleEvents()
-{
-    assertChipStackLockedByCurrentThread();
-
-    if (mSelectResult < 0)
-    {
-        ChipLogError(DeviceLayer, "select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno)));
-        return;
-    }
-
-    VerifyOrDie(mSystemLayer != nullptr);
-    mSystemLayer->HandleTimeout();
-
-    for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext)
-    {
-        watchable->SetPendingIO(
-            SocketEventsFromFDs(watchable->GetFD(), mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet));
-    }
-    for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext)
-    {
-        if (watchable->mPendingIO.HasAny())
-        {
-            watchable->InvokeCallback();
-        }
-    }
-
-#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__
-    chip::Mdns::HandleMdnsTimeout();
-#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__
-}
-
 void WatchableSocket::OnAttach()
 {
     mSharedState->Reset(mFD);
@@ -230,10 +57,30 @@ void WatchableSocket::OnClose()
         pp = &(*pp)->mAttachedNext;
     }
 
-#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD
+#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
     // Wake the thread calling select so that it stops selecting on the socket.
-    mSharedState->WakeSelect();
-#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD
+    mSharedState->Signal();
+#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
+}
+
+void WatchableSocket::OnRequestCallbackOnPendingRead()
+{
+    mSharedState->Set(mFD, &mSharedState->mRequest.mReadSet);
+}
+
+void WatchableSocket::OnRequestCallbackOnPendingWrite()
+{
+    mSharedState->Set(mFD, &mSharedState->mRequest.mWriteSet);
+}
+
+void WatchableSocket::OnClearCallbackOnPendingRead()
+{
+    mSharedState->Clear(mFD, &mSharedState->mRequest.mReadSet);
+}
+
+void WatchableSocket::OnClearCallbackOnPendingWrite()
+{
+    mSharedState->Clear(mFD, &mSharedState->mRequest.mWriteSet);
 }
 
 /**
diff --git a/src/system/WatchableSocketSelect.h b/src/system/WatchableSocketSelect.h
index 1df1e190e5a782..266ad7e69682a7 100644
--- a/src/system/WatchableSocketSelect.h
+++ b/src/system/WatchableSocketSelect.h
@@ -17,71 +17,24 @@
 
 /**
  *    @file
- *      This file declares an implementation of WatchableEvents using select().
+ *      This file declares an implementation of WatchableSocket using select().
  */
 
 #pragma once
 
+#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE
+#error "This file should only be included from <system/WatchableSocket.h>"
+#include <system/WatchableSocket.h>
+#endif //  !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE
+
 #include <sys/select.h>
 
 #include <support/BitFlags.h>
-
-#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE
-#error "This file should only be included from <system/SystemSockets.h>"
-#endif //  !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE
+#include <support/logging/CHIPLogging.h>
 
 namespace chip {
-
 namespace System {
 
-class WatchableEventManager
-{
-public:
-    void Init(System::Layer & systemLayer);
-    void Shutdown();
-
-    void EventLoopBegins() {}
-    void PrepareEvents();
-    void WaitForEvents();
-    void HandleEvents();
-    void EventLoopEnds() {}
-
-    // TODO(#5556): Some unit tests supply a timeout at low level, due to originally using select(); these should a proper timer.
-    void PrepareEventsWithTimeout(timeval & nextTimeout);
-
-    static SocketEvents SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds, const fd_set & exceptfds);
-
-protected:
-    friend class WatchableSocket;
-
-    void Set(int fd, fd_set * fds);
-    void Clear(int fd, fd_set * fds);
-
-    Layer * mSystemLayer               = nullptr;
-    WatchableSocket * mAttachedSockets = nullptr;
-
-    // TODO(#5556): Integrate timer platform details with WatchableEventManager.
-    struct timeval mNextTimeout;
-
-    // Members for select loop
-    struct SelectSets
-    {
-        fd_set mReadSet;
-        fd_set mWriteSet;
-        fd_set mErrorSet;
-    };
-    SelectSets mRequest;
-    SelectSets mSelected;
-    int mMaxFd;
-    int mSelectResult; ///< return value from select()
-
-private:
-    bool HasAny(int fd);
-    void MaybeLowerMaxFd();
-    void Reset(int fd);
-    void WakeSelect();
-};
-
 class WatchableSocket : public WatchableSocketBasis<WatchableSocket>
 {
 public:
@@ -89,10 +42,10 @@ class WatchableSocket : public WatchableSocketBasis<WatchableSocket>
     void OnAttach();
     void OnClose();
 
-    void OnRequestCallbackOnPendingRead() { mSharedState->Set(mFD, &mSharedState->mRequest.mReadSet); }
-    void OnRequestCallbackOnPendingWrite() { mSharedState->Set(mFD, &mSharedState->mRequest.mWriteSet); }
-    void OnClearCallbackOnPendingRead() { mSharedState->Clear(mFD, &mSharedState->mRequest.mReadSet); }
-    void OnClearCallbackOnPendingWrite() { mSharedState->Clear(mFD, &mSharedState->mRequest.mWriteSet); }
+    void OnRequestCallbackOnPendingRead();
+    void OnRequestCallbackOnPendingWrite();
+    void OnClearCallbackOnPendingRead();
+    void OnClearCallbackOnPendingWrite();
 
     void SetPendingIO(SocketEvents events) { mPendingIO = events; }
     void SetFDs(int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds);
diff --git a/src/system/tests/TestSystemWakeEvent.cpp b/src/system/tests/TestSystemWakeEvent.cpp
index 19104e1cd2e93b..4dbfd0c31ea563 100644
--- a/src/system/tests/TestSystemWakeEvent.cpp
+++ b/src/system/tests/TestSystemWakeEvent.cpp
@@ -33,7 +33,7 @@
 #include <support/UnitTestRegistration.h>
 #include <system/SystemError.h>
 #include <system/SystemLayer.h>
-#include <system/SystemSockets.h>
+#include <system/WatchableSocket.h>
 
 #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
 #include <pthread.h>
@@ -42,6 +42,17 @@
 using namespace chip::System;
 
 #if CHIP_SYSTEM_CONFIG_USE_SOCKETS
+
+namespace chip {
+namespace System {
+class WakeEventTest
+{
+public:
+    static int GetReadFD(const WakeEvent & wakeEvent) { return wakeEvent.GetReadFD(); }
+};
+} // namespace System
+} // namespace chip
+
 namespace {
 
 struct TestContext
@@ -65,15 +76,15 @@ struct TestContext
         FD_ZERO(&mReadSet);
         FD_ZERO(&mWriteSet);
         FD_ZERO(&mErrorSet);
-        FD_SET(mWakeEvent.GetNotifFD(), &mReadSet);
-        return select(mWakeEvent.GetNotifFD() + 1, &mReadSet, &mWriteSet, &mErrorSet, &timeout);
+        FD_SET(WakeEventTest::GetReadFD(mWakeEvent), &mReadSet);
+        return select(WakeEventTest::GetReadFD(mWakeEvent) + 1, &mReadSet, &mWriteSet, &mErrorSet, &timeout);
     }
 };
 
 void TestOpen(nlTestSuite * inSuite, void * aContext)
 {
     TestContext & lContext = *static_cast<TestContext *>(aContext);
-    NL_TEST_ASSERT(inSuite, lContext.mWakeEvent.GetNotifFD() >= 0);
+    NL_TEST_ASSERT(inSuite, WakeEventTest::GetReadFD(lContext.mWakeEvent) >= 0);
     NL_TEST_ASSERT(inSuite, lContext.SelectWakeEvent() == 0);
 }
 
@@ -85,11 +96,11 @@ void TestNotify(nlTestSuite * inSuite, void * aContext)
     // Check that select() succeeds after Notify() has been called
     lContext.mWakeEvent.Notify();
     NL_TEST_ASSERT(inSuite, lContext.SelectWakeEvent() == 1);
-    NL_TEST_ASSERT(inSuite, FD_ISSET(lContext.mWakeEvent.GetNotifFD(), &lContext.mReadSet));
+    NL_TEST_ASSERT(inSuite, FD_ISSET(WakeEventTest::GetReadFD(lContext.mWakeEvent), &lContext.mReadSet));
 
     // ...and state of the event is not cleared automatically
     NL_TEST_ASSERT(inSuite, lContext.SelectWakeEvent() == 1);
-    NL_TEST_ASSERT(inSuite, FD_ISSET(lContext.mWakeEvent.GetNotifFD(), &lContext.mReadSet));
+    NL_TEST_ASSERT(inSuite, FD_ISSET(WakeEventTest::GetReadFD(lContext.mWakeEvent), &lContext.mReadSet));
 }
 
 void TestConfirm(nlTestSuite * inSuite, void * aContext)
@@ -99,7 +110,7 @@ void TestConfirm(nlTestSuite * inSuite, void * aContext)
     // Check that select() succeeds after Notify() has been called
     lContext.mWakeEvent.Notify();
     NL_TEST_ASSERT(inSuite, lContext.SelectWakeEvent() == 1);
-    NL_TEST_ASSERT(inSuite, FD_ISSET(lContext.mWakeEvent.GetNotifFD(), &lContext.mReadSet));
+    NL_TEST_ASSERT(inSuite, FD_ISSET(WakeEventTest::GetReadFD(lContext.mWakeEvent), &lContext.mReadSet));
 
     // Check that Confirm() clears state of the event
     lContext.mWakeEvent.Confirm();
@@ -136,7 +147,7 @@ void TestClose(nlTestSuite * inSuite, void * aContext)
     TestContext & lContext = *static_cast<TestContext *>(aContext);
     lContext.mWakeEvent.Close();
 
-    const auto notifFD = lContext.mWakeEvent.GetNotifFD();
+    const auto notifFD = WakeEventTest::GetReadFD(lContext.mWakeEvent);
 
     // Check that Close() has cleaned up itself and reopen is possible
     NL_TEST_ASSERT(inSuite, lContext.mWakeEvent.Open(lContext.mWatchableEvents) == CHIP_NO_ERROR);