Skip to main content

Overview

LibCore is SerenityOS’s core utilities library built on top of AK. It provides essential abstractions for event-driven programming, file I/O, process management, timers, and system integration. LibCore is the foundation for both GUI applications (via LibGUI) and command-line utilities.
LibCore provides cross-platform abstractions with platform-specific implementations for SerenityOS, Linux, and macOS.

Event Loop

EventLoop

Core event loop for asynchronous programming.
#include <LibCore/EventLoop.h>

int main(int argc, char** argv) {
    Core::EventLoop event_loop;
    
    // Schedule deferred work
    event_loop.deferred_invoke([]() {
        dbgln("This runs on next event loop iteration");
    });
    
    // Run the event loop
    return event_loop.exec();
}
  • Deferred invocations: Run callbacks asynchronously
  • Timers: Periodic or one-shot timer events
  • Filesystem notifications: Watch for file changes
  • POSIX signals: Handle signals in event loop
  • Coroutine support: Async/await style programming
  • Nested event loops: Support for modal dialogs
Key Methods:
exec
int()
Pump the event loop until exit is requested. Returns exit code.
pump
size_t(WaitMode)
Process events once. Returns number of events processed.
quit
void(int)
Request event loop exit with given exit code.
deferred_invoke
void(Function<void()>)
Schedule callback to run on next event loop iteration.
spin_until
void(Function<bool()>)
Pump event loop until condition becomes true.
// Spin until condition met
bool ready = false;
event_loop.spin_until([&] { return ready; });

// Quit event loop
event_loop.quit(0);

EventReceiver

Base class for objects that receive events.
#include <LibCore/EventReceiver.h>
#include <LibCore/Event.h>

class MyObject : public Core::EventReceiver {
    C_OBJECT(MyObject)
public:
    virtual void timer_event(Core::TimerEvent& event) override {
        dbgln("Timer fired!");
    }
    
    virtual void custom_event(Core::CustomEvent& event) override {
        dbgln("Custom event: {}", event.custom_type());
    }
    
private:
    MyObject() = default;
};

File I/O

File

Modern file I/O using streams.
#include <LibCore/File.h>

// Open file 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());

// Read some bytes
u8 buffer[1024];
auto bytes = TRY(file->read_some(buffer));

// Open for writing
auto out = TRY(Core::File::open("/tmp/output"sv, 
    Core::File::OpenMode::Write | Core::File::OpenMode::Truncate));
TRY(out->write_until_depleted("Hello, World!"sv.bytes()));
// Read entire file as string
auto file = TRY(Core::File::open("/etc/passwd"sv, Core::File::OpenMode::Read));
auto buffer = TRY(file->read_until_eof());
auto content = String::from_utf8_without_validation(buffer);
Open Modes:
Read
OpenMode
Open for reading
Write
OpenMode
Open for writing
ReadWrite
OpenMode
Open for reading and writing
Append
OpenMode
Append to end of file
Truncate
OpenMode
Truncate file to zero length
MustBeNew
OpenMode
Fail if file already exists

Directory & DirIterator

Directory operations and iteration.
#include <LibCore/Directory.h>
#include <LibCore/DirIterator.h>

// Create directory
TRY(Core::Directory::create("/tmp/mydir"sv, 0755));

// Iterate directory entries
Core::DirIterator iterator("/tmp"sv, Core::DirIterator::SkipDots);
while (iterator.has_next()) {
    auto entry = iterator.next();
    dbgln("{}: {}", entry->name, entry->type);
}

// Check if directory exists
if (Core::Directory::exists("/etc"sv)) {
    // Directory exists
}

MappedFile

Memory-mapped file I/O for efficient access.
#include <LibCore/MappedFile.h>

// Map file into memory
auto mapped = TRY(Core::MappedFile::map("/path/to/large/file"sv));

// Access as bytes
ReadonlyBytes bytes = mapped->bytes();

// File is automatically unmapped on destruction

Timers

Timer

Schedule periodic or one-shot timer events.
#include <LibCore/Timer.h>

// Create repeating timer
auto timer = Core::Timer::create_repeating(1000, []() {
    dbgln("Tick every second");
});
timer->start();

// Create one-shot timer
auto delayed = Core::Timer::create_single_shot(5000, []() {
    dbgln("Fired after 5 seconds");
});
delayed->start();

// Manual timer control
auto timer = Core::Timer::create();
timer->set_interval(500); // 500ms
timer->set_single_shot(false); // Repeating
timer->on_timeout = []() { dbgln("Timeout!"); };
timer->start();

// Stop timer
timer->stop();
interval
int
Timer interval in milliseconds
single_shot
bool
If true, timer fires once then stops. If false, repeats indefinitely.
on_timeout
Function<void()>
Callback invoked when timer fires

Process Management

Process

Spawn and manage child processes.
#include <LibCore/Process.h>

// Spawn process
auto result = TRY(Core::Process::spawn("/bin/ls"sv, 
    Array { "ls"sv, "-la"sv }));
dbgln("Spawned PID: {}", result.pid);

// Run and wait for completion
auto output = TRY(Core::Process::run(
    Array { "/bin/echo"sv, "Hello"sv }));
dbgln("Exit code: {}", output.exit_code);
dbgln("Output: {}", String::from_utf8(output.standard_output));

// Spawn with custom environment
HashMap<String, String> env;
env.set("MY_VAR"_string, "value"_string);
auto result = TRY(Core::Process::spawn("/bin/prog"sv, 
    Array { "prog"sv }, env));
// Spawn without waiting
auto result = TRY(Core::Process::spawn(
    "/usr/bin/myapp"sv,
    Array { "myapp"sv, "--option"sv, "value"sv }
));

dbgln("Process PID: {}", result.pid);
// Process continues running

Network I/O

Socket

TCP/UDP socket abstraction.
#include <LibCore/Socket.h>

// Connect to server
auto socket = TRY(Core::Socket::connect("example.com"sv, 80));

// Send data
TRY(socket->write_until_depleted("GET / HTTP/1.0\r\n\r\n"sv.bytes()));

// Read response
auto response = TRY(socket->read_until_eof());

// Socket automatically closes on destruction

TCPServer & UDPServer

Server socket implementations.
#include <LibCore/TCPServer.h>

auto server = TRY(Core::TCPServer::try_create());
TRY(server->listen({ 127, 0, 0, 1 }, 8080));

server->on_ready_to_accept = [&]() {
    auto client = server->accept();
    if (client) {
        dbgln("Client connected");
        // Handle client...
    }
};

Configuration

ConfigFile

INI-style configuration file handling.
#include <LibCore/ConfigFile.h>

// Open config file
auto config = TRY(Core::ConfigFile::open("/etc/myapp.conf"sv));

// Read values
auto value = config->read_entry("Section"sv, "key"sv, "default"sv);
auto number = config->read_num_entry("Section"sv, "count"sv, 0);
auto flag = config->read_bool_entry("Section"sv, "enabled"sv, false);

// Write values
config->write_entry("Section"sv, "key"sv, "new_value"sv);
config->write_num_entry("Section"sv, "count"sv, 42);
config->write_bool_entry("Section"sv, "enabled"sv, true);

// Save changes
TRY(config->sync());

Environment

Environment variable access.
#include <LibCore/Environment.h>

// Get variable
if (auto path = Core::Environment::get("PATH"sv)) {
    dbgln("PATH: {}", *path);
}

// Set variable
TRY(Core::Environment::set("MY_VAR"sv, "value"sv, true)); // true = overwrite

// Unset variable
TRY(Core::Environment::unset("MY_VAR"sv));

System Utilities

DateTime

Date and time handling.
#include <LibCore/DateTime.h>

// Current time
auto now = Core::DateTime::now();
dbgln("Current time: {}", now.to_string());

// From timestamp
auto dt = Core::DateTime::from_timestamp(1234567890);

// Formatting
auto formatted = now.to_string("%Y-%m-%d %H:%M:%S"sv);

// Components
int year = now.year();
int month = now.month();
int day = now.day();
int hour = now.hour();

ArgsParser

Command-line argument parsing.
#include <LibCore/ArgsParser.h>

int main(int argc, char** argv) {
    Core::ArgsParser parser;
    
    String filename;
    bool verbose = false;
    int count = 1;
    
    parser.add_positional_argument(filename, "Input file", "FILE");
    parser.add_option(verbose, "Verbose output", "verbose", 'v');
    parser.add_option(count, "Repetition count", "count", 'n', "NUM");
    
    parser.parse(argc, argv);
    
    dbgln("File: {}", filename);
    dbgln("Verbose: {}", verbose);
    dbgln("Count: {}", count);
    
    return 0;
}
  • Positional arguments
  • Boolean flags (—flag, -f)
  • Value options (—option=value, -o value)
  • Automatic —help generation
  • Required vs optional arguments
  • Type-safe parsing

ElapsedTimer

High-resolution timing.
#include <LibCore/ElapsedTimer.h>

Core::ElapsedTimer timer;
timer.start();

// Do some work...

auto elapsed_ms = timer.elapsed();
dbgln("Operation took {}ms", elapsed_ms);

System

System information and utilities.
#include <LibCore/System.h>

// Get system info
auto hostname = TRY(Core::System::gethostname());
auto username = TRY(Core::System::getlogin());

// Change directory
TRY(Core::System::chdir("/tmp"sv));

// Sleep
Core::System::sleep(1); // 1 second
Core::System::usleep(1000); // 1000 microseconds

Promises & Async

Promise

Asynchronous operation results.
#include <LibCore/Promise.h>

NonnullRefPtr<Core::Promise<String>> async_read_file(String path) {
    auto promise = Core::Promise<String>::construct();
    
    // Schedule async work
    Core::EventLoop::current().deferred_invoke([promise, path = move(path)]() {
        auto result = Core::File::open(path, Core::File::OpenMode::Read);
        if (result.is_error()) {
            promise->reject(result.error());
        } else {
            auto content = result.value()->read_until_eof();
            if (content.is_error()) {
                promise->reject(content.error());
            } else {
                promise->resolve(String::from_utf8_without_validation(content.value()));
            }
        }
    });
    
    return promise;
}

File Watching

FileWatcher

Monitor file system changes.
#include <LibCore/FileWatcher.h>

auto watcher = TRY(Core::FileWatcher::create());
TRY(watcher->add_watch("/path/to/watch"sv, 
    Core::FileWatcherEvent::Type::Modified));

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

Best Practices

  • Create one EventLoop per thread
  • Use deferred_invoke for async callbacks
  • Don’t block the event loop with long operations
  • Use timers for periodic work
// Always use TRY for operations that return ErrorOr
auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read));

// Or handle errors explicitly
if (auto result = Core::File::open(path, mode); result.is_error()) {
    warnln("Failed to open: {}", result.error());
    return 1;
}
  • Use RAII: Resources automatically cleaned up
  • Files close on destruction
  • Timers stop on destruction
  • Sockets disconnect on destruction
  • AK - Foundation data structures
  • LibGUI - GUI toolkit built on LibCore
  • LibIPC - Inter-process communication

Build docs developers (and LLMs) love