# How Windows GUI Message Pump Integrates with Fiber Scheduling in libfiber

> Explore how libfiber integrates the Windows GUI message pump with fiber scheduling via a hidden window and WSAAsyncSelect. Learn how Windows messages become fiber readiness events.

- Repository: [iQIYI/libfiber](https://github.com/iqiyi/libfiber)
- Tags: internals
- Published: 2026-03-04

---

**libfiber seamlessly weaves the Windows GUI message pump into its fiber scheduling mechanism by using a hidden window that receives socket notifications via `WSAAsyncSelect`, translating Windows messages into fiber readiness events through the `EVENT` abstraction layer.**

The iqiyi/libfiber library abstracts I/O multiplexing behind a portable **event-engine** interface (`EVENT`). On Windows, the framework optionally builds its scheduling core atop the native GUI message pump rather than traditional polling mechanisms, enabling fibers to suspend and resume within message-driven Windows applications without blocking the main thread.

## The Event Engine Abstraction on Windows

libfiber decouples its scheduler from platform-specific I/O mechanisms through the `EVENT` interface. On Windows, when the `HAS_WMSG` preprocessor macro is defined, the factory function `event_create()` instantiates the Windows message-pump implementation via `event_wmsg_create()` in [`c/src/event/event_wmsg.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_wmsg.c) instead of using `select`-based or I/O completion port engines.

This abstraction allows the scheduler to call generic methods like `event->add_read()`, `event->add_write()`, and `event->event_wait()` without knowledge of the underlying Windows messaging infrastructure.

## Hidden Window Creation and Socket Mapping

The Windows-specific engine initializes by creating a hidden window to receive asynchronous socket notifications. In `event_wmsg_create()`, the function registers a window class via `InitApplication` and creates a message-only window using `CreateSockWindow` that listens for the custom `WM_SOCKET_NOTIFY` message.

To associate socket descriptors with their corresponding fiber I/O contexts, the engine maintains a hash table (`ev->tbl`) that maps `SOCKET` handles to `FILE_EVENT` structures. The helper functions `wmsg_fdmap_set()`, `wmsg_fdmap_get()`, and `wmsg_fdmap_del()` manage these mappings in [`c/src/event/event_wmsg.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_wmsg.c), enabling O(1) lookup when Windows messages arrive.

## Asynchronous Socket Event Registration

When a fiber yields waiting for I/O, the scheduler invokes `event->add_read()` or `event->add_write()`, which dispatch to `wmsg_add_read()` and `wmsg_add_write()` in the Win-msg engine. These functions call `WSAAsyncSelect()` to register the socket with the hidden window, requesting Windows message notifications for specific network events.

For read operations, `wmsg_add_read()` sets interest in `FD_READ` and `FD_CLOSE` flags (and `FD_ACCEPT` for listening sockets). For write operations, `wmsg_add_write()` sets `FD_WRITE` and `FD_CLOSE` flags (and `FD_CONNECT` for outgoing connections). This registration ensures that when network data arrives, Windows posts a `WM_SOCKET_NOTIFY` message to the hidden window rather than requiring the scheduler to poll socket states.

## Message Pump Loop Implementation

The engine implements the blocking `event_wait` callback through `wmsg_wait()`, which serves as the heart of the fiber scheduler's event loop on Windows. This function creates a timer to honor the timeout parameter, then enters a blocking `GetMessage()` call to retrieve the next Windows message from the queue.

After dispatching the message via `DispatchMessage()`, the loop drains any remaining queued messages using `PeekMessage()` to ensure timely processing of multiple socket events. The core sequence—`SetTimer`, `GetMessage`, `DispatchMessage`, `PeekMessage`—transforms the standard Windows message pump into a fiber-aware event source that wakes only when sockets are ready or timers expire.

## Dispatching Notifications to Resumable Fibers

The window procedure `WndProc` in [`c/src/event/event_wmsg.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_wmsg.c) handles `WM_SOCKET_NOTIFY` messages by extracting the socket handle from `wParam` and the event code using `WSAGETSELECTEVENT(lParam)`. It looks up the corresponding `FILE_EVENT` structure in the hash table, then invokes the appropriate callback—`onRead()`, `onWrite()`, `onAccept()`, or `onClose()`—based on the notification type.

These callbacks execute the fiber's registered I/O handlers (`fe->r_proc` for reads, `fe->w_proc` for writes), which mark the waiting fiber as ready to run and trigger a context switch back into the scheduler. This mechanism bridges the Windows message-driven architecture with libfiber's cooperative multitasking model.

## Scheduler Integration Flow

The integration completes within the main scheduler loop defined in `acl_fiber_schedule_with()`. The scheduler obtains an `EVENT` object through `event_create()`, which selects the `event_wmsg_create()` implementation on Windows builds configured with `HAS_WMSG`.

When a fiber calls `acl_fiber_add_read()` or `acl_fiber_add_write()` and then yields, the underlying engine registers the socket for asynchronous notification. The subsequent call to `event->event_wait()` (implemented as `wmsg_wait()`) blocks the scheduler thread on `GetMessage()` until the Windows message pump delivers a socket-ready notification. At that point, the scheduler resumes the corresponding fiber, creating a seamless cooperative multitasking environment that respects Windows' message-based architecture.

## Practical Code Example

The following example demonstrates registering write interest and yielding until the socket becomes writable, leveraging the Windows message pump integration internally:

```c
/* compile on Windows with libfiber sources */
#include "fiber/libfiber.h"

static void echo_client(ACL_FIBER *fiber, void *ctx)
{
    SOCKET fd = (SOCKET)ctx;
    const char *msg = "hello\r\n";

    /* Register interest in write readiness – triggers WSAAsyncSelect internally */
    acl_fiber_add_write(fd);
    acl_fiber_yield();                 /* Suspends until WM_SOCKET_NOTIFY arrives */

    acl_fiber_send(fd, msg, strlen(msg), 0);
    acl_fiber_close(fd);
}

int main(void)
{
    socket_init();

    acl_fiber_create(
        [](ACL_FIBER *f, void *){
            SOCKET s = socket_connect("127.0.0.1", 9001);
            echo_client(f, (void *)s);
        },
        NULL, 128000);

    /* Blocks in wmsg_wait() using the Windows message pump */
    acl_fiber_schedule();
    return 0;
}

```

In this example, `acl_fiber_add_write()` invokes the Win-msg engine's registration logic, `acl_fiber_yield()` suspends the fiber, and `acl_fiber_schedule()` enters the message pump loop that resumes the fiber when the socket becomes writable.

## Summary

- libfiber uses a **hidden window** and the `WM_SOCKET_NOTIFY` message to receive socket events on Windows, implemented in [`c/src/event/event_wmsg.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_wmsg.c).
- The **hash table** in `event_wmsg_create()` maps socket descriptors to `FILE_EVENT` structures for O(1) lookup during message dispatch.
- **WSAAsyncSelect** bridges sockets to the GUI message pump, with `wmsg_add_read()` and `wmsg_add_write()` managing event registration for `FD_READ`, `FD_WRITE`, and `FD_CLOSE` conditions.
- The **message pump loop** in `wmsg_wait()` replaces traditional polling, using `GetMessage()` and `DispatchMessage()` to block until I/O events occur.
- The scheduler factory selects the Windows message-pump engine when `HAS_WMSG` is defined, integrating seamlessly with `acl_fiber_schedule()` to resume fibers via `fe->r_proc` and `fe->w_proc` callbacks.

## Frequently Asked Questions

### How does libfiber avoid blocking the GUI thread while waiting for socket I/O?

The library delegates blocking to the Windows message pump itself. By using `WSAAsyncSelect()` to request `WM_SOCKET_NOTIFY` messages and then calling `GetMessage()` in `wmsg_wait()`, the scheduler yields control to Windows' message dispatcher. This allows the thread to process GUI messages while simultaneously waiting for network events, eliminating the need for a separate polling thread.

### What is the purpose of the hidden window created by event_wmsg_create()?

The hidden window serves as the recipient for asynchronous socket notifications generated by `WSAAsyncSelect()`. Since Windows requires a window handle (`HWND`) to target messages, `event_wmsg_create()` creates a message-only window that receives `WM_SOCKET_NOTIFY` events. The `WndProc` window procedure then maps these messages back to the appropriate fiber I/O contexts using the internal hash table.

### Can the Win-msg engine handle both GUI messages and fiber scheduling simultaneously?

Yes, because `wmsg_wait()` uses the standard `GetMessage()` and `DispatchMessage()` loop, it naturally processes all Windows messages in the thread's queue. This design allows a single thread to run both a traditional Windows GUI message pump and the libfiber scheduler without conflict, making it ideal for applications that embed fiber-based networking code within graphical interfaces.

### How does the scheduler know which fiber to resume when a socket becomes ready?

When a `WM_SOCKET_NOTIFY` message arrives, the `WndProc` extracts the socket handle from `wParam` and looks up the associated `FILE_EVENT` structure in the hash table via `wmsg_fdmap_get()`. This structure contains function pointers to the fiber's I/O callbacks (`fe->r_proc` for reads, `fe->w_proc` for writes). Invoking these callbacks marks the specific suspended fiber as runnable, allowing the scheduler to resume it on the next iteration.