Skip to main content
LibCore is Ladybird’s foundational library for event-driven programming and operating system abstractions. It provides the event loop, timers, file I/O, networking primitives, and process management.

Event loop system

The event loop is the heart of LibCore, enabling asynchronous I/O and event handling. See the event loop architecture for more details.

Creating and running an event loop

#include <LibCore/EventLoop.h>

int main(int argc, char** argv)
{
    Core::EventLoop event_loop;
    
    // Queue work
    event_loop.deferred_invoke([] {
        dbgln("This runs on the next event loop iteration");
    });
    
    // Run until quit
    return event_loop.exec();
}
There is at most one running event loop per thread. Event loops can be nested for modal dialogs and other temporary UI states.

Event loop capabilities

Deferred invocation

Execute callbacks on next event loop iteration

Timers

Schedule callbacks to run after delays or repeatedly

I/O notifications

React when file descriptors become readable/writable

Signal handling

Handle POSIX signals in event-driven manner

How it works

The event loop uses select(2) to efficiently wait for events:
  1. exec() repeatedly calls pump() to process events
  2. pump() uses select() to sleep until an event occurs
  3. When awakened, callbacks for expired timers and I/O are invoked
  4. The loop returns to sleep until the next event
Do not store event loops in global variables due to initialization order issues. Create them in main() and pass them to classes that need them.

Timers

Core::Timer

Schedule callbacks to run after a delay:
auto timer = Core::Timer::create();

// One-shot timer (runs once)
timer->set_single_shot(true);
timer->set_interval(1000); // milliseconds
timer->on_timeout = [] {
    dbgln("Timer fired!");
};
timer->start();

// Repeating timer
auto repeating = Core::Timer::create();
repeating->set_interval(100);
repeating->on_timeout = [] {
    dbgln("Tick");
};
repeating->start();

// Stop timer
repeating->stop();
Timers are not super accurate due to event loop scheduling. Don’t rely on them for precise timing in real-time scenarios like audio processing.

I/O and notifications

Core::Notifier

React when file descriptors become readable or writable:
auto notifier = Core::Notifier::create(
    socket_fd,
    Core::Notifier::Type::Read
);

notifier->on_activation = [socket_fd] {
    // Socket has data to read
    auto bytes = read_from_socket(socket_fd);
    process(bytes);
};
Notifier types:
  • Read - File descriptor has data to read
  • Write - File descriptor is ready for writing
  • Exception - Exception condition occurred

File system operations

Core::File

Cross-platform file I/O:
// Open for reading
auto file = TRY(Core::File::open(
    "/path/to/file"sv,
    Core::File::OpenMode::Read
));

// Read entire file
auto contents = TRY(file->read_until_eof());

// Open for writing
auto output = TRY(Core::File::open(
    "/path/to/output"sv,
    Core::File::OpenMode::Write
));

TRY(output->write_until_depleted(data));

Core::Directory

Directory operations:
// List directory contents
auto dir = TRY(Core::Directory::create(
    "/path/to/dir"sv,
    Core::Directory::CreateDirectories::Yes
));

TRY(dir->for_each_entry([](auto const& entry) {
    dbgln("Found: {}", entry.name);
    return IterationDecision::Continue;
}));

Core::DirIterator

Iterate over directory entries:
Core::DirIterator iterator { "/path/to/dir"sv };

while (iterator.has_next()) {
    auto entry = iterator.next();
    if (entry->type == Core::DirectoryEntry::Type::Directory) {
        dbgln("Directory: {}", entry->name);
    }
}

File watching

Monitor file system changes:
auto watcher = TRY(Core::FileWatcher::create());

TRY(watcher->add_watch(
    "/path/to/watch"sv,
    Core::FileWatcher::Event::Type::MetadataModified
));

watcher->on_change = [](auto const& event) {
    dbgln("File changed: {}", event.path);
};

Networking

Core::Socket

Base class for network sockets with async I/O:
auto socket = TRY(Core::Socket::connect(
    { "example.com", 80 }
));

socket->on_ready_to_read = [&] {
    auto buffer = TRY(socket->read_some(4096));
    process_data(buffer);
};

TRY(socket->write_until_depleted("GET / HTTP/1.1\r\n"sv));

Core::TCPServer

TCP server with event-driven connection handling:
auto server = TRY(Core::TCPServer::try_create());
TRY(server->listen({ 0, 0, 0, 0 }, 8080));

server->on_ready_to_accept = [&] {
    auto client = TRY(server->accept());
    
    client->on_ready_to_read = [client = move(client)] {
        auto data = TRY(client->read_some(4096));
        // Handle client data
    };
};

Core::UDPServer

UDP socket for connectionless communication:
auto udp = TRY(Core::UDPServer::try_create());
TRY(udp->bind({ 0, 0, 0, 0 }, 9000));

udp->on_ready_to_receive = [&] {
    auto [data, sender] = TRY(udp->receive());
    dbgln("Received from {}: {}", sender, data);
};

Process management

Core::Process

Spawn and manage child processes:
// Simple spawn
auto pid = TRY(Core::Process::spawn("/bin/ls"sv, 
    Vector<StringView> { "-la"sv }));

// Wait for completion
auto status = TRY(Core::Process::wait(pid));

// Spawn with I/O
auto result = TRY(Core::Process::run(
    "/bin/echo"sv,
    Vector<StringView> { "Hello"sv },
    Core::Process::KeepAsChild::No
));

dbgln("Output: {}", result.output);

Environment and system info

Environment variables

// Get environment variable
auto path = Core::Environment::get("PATH"sv);

// Set environment variable
Core::Environment::set("MY_VAR"sv, "value"sv);

// Check existence
if (Core::Environment::has("HOME"sv)) {
    // ...
}

Standard paths

// Get standard directories
auto home = Core::StandardPaths::home_directory();
auto config = Core::StandardPaths::config_directory();
auto cache = Core::StandardPaths::cache_directory();
auto temp = Core::StandardPaths::tempfile_directory();

Platform abstraction

LibCore provides cross-platform abstractions for:
  • File I/O: Works on Linux, macOS, Windows (WSL), and Unix systems
  • Networking: Portable socket API across platforms
  • Process management: Unified process spawning and control
  • Event loops: Platform-specific implementations (EventLoopImplementationUnix, EventLoopImplementationWindows)
// Platform detection available in headers
#if defined(AK_OS_MACOS)
    // macOS-specific code
#elif defined(AK_OS_WINDOWS)
    // Windows-specific code
#elif defined(AK_OS_LINUX)
    // Linux-specific code
#endif

Resource loading

Core::Resource

Load embedded or file-based resources:
auto resource = TRY(Core::Resource::load_from_file(
    "/resources/image.png"sv
));

auto data = resource->data();

Promises and async operations

Core::Promise

Type-safe async value resolution:
auto promise = Core::Promise<String>::create();

promise->on_resolution = [](String const& value) {
    dbgln("Promise resolved: {}", value);
};

// Later, resolve the promise
promise->resolve("Result"_string);

Core::ThreadedPromise

Promise that resolves on a background thread:
auto promise = Core::ThreadedPromise<ByteBuffer>::create();

promise->on_resolved = [](ByteBuffer buffer) {
    // Runs on main thread
    process(buffer);
};

// Work happens on background thread
promise->resolve_async([] {
    return load_large_file();
});

Configuration files

Core::ConfigFile

INI-style configuration file handling:
auto config = TRY(Core::ConfigFile::open(
    "/path/to/config.ini"sv
));

// Read values
auto value = config->read_entry("Section"sv, "Key"sv);
auto number = config->read_num_entry("Section"sv, "Port"sv, 8080);

// Write values
config->write_entry("Section"sv, "Key"sv, "Value"sv);
TRY(config->sync());

Argument parsing

Core::ArgsParser

User-friendly command-line argument parsing:
int main(int argc, char** argv)
{
    Core::ArgsParser args;
    args.set_general_help("My application");
    
    bool verbose = false;
    StringView output_path;
    Vector<StringView> inputs;
    
    args.add_option(verbose, "Verbose output", "verbose", 'v');
    args.add_option(output_path, "Output file", "output", 'o', "PATH");
    args.add_positional_argument(inputs, "Input files", "inputs");
    
    args.parse(argc, argv);
    
    // Use parsed arguments
}

Source location

  • Repository: ~/workspace/source/Libraries/LibCore/
  • Key headers: EventLoop.h, Timer.h, File.h, Socket.h, Process.h
  • Documentation: ~/workspace/source/Documentation/EventLoop.md
  • Platform code: LibCore/Platform/
LibCore’s event loop is designed for I/O-bound operations and UI events. For CPU-intensive work, use LibThreading to run tasks on background threads.

Build docs developers (and LLMs) love