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:
exec() repeatedly calls pump() to process events
pump() uses select() to sleep until an event occurs
When awakened, callbacks for expired timers and I/O are invoked
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 ();
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.