Wait-free resizable hashmap, lock-free array & set
30 modules · lock-free core · BSD-3-Clause
The C standard library
you wish existed.
Containers, Unicode, regex, HTTP parsing, sockets, time and lock-free concurrency, written in plain C and tested on Linux, macOS and Windows.
#include <mulle-core/mulle-core.h> // everything
cc main.c -lmulle-core // one link
Why mulle-core
libc gives you strings, stdio and not much more. mulle-core covers the things you would otherwise write yourself or collect from a dozen different projects.
Used in production by mulle-objc.
mulle-core is the foundation of mulle-objc, an Objective-C that is statically linkable, unloadable and real-time capable, and runs wherever C does.
- Lock-free concurrency. A wait-free resizable hashmap, a lock-free linked list and FIFOs, and a working solution to the ABA problem. Classic mutexes and atomics are in mulle-thread for when you need them.
- Fits your build. Use CMake
FetchContent, a git submodule or a plain clone. A pinned snapshot builds with stock CMake and the compiler you already have. You don't need mulle-sde or any code generation. - Cross-platform. Ubuntu with GCC and Clang, Windows with MSVC (Win32/Win64) and GCC, macOS with Clang and GCC, all tested in CI.
- Documented for AI agents. Every module comes with an AI-oriented reference that has keyword front-matter and the C signatures inline. It's plain text, so it works just as well for people with grep.
- BSD-3-Clause. Every module uses the same license. Use it in commercial products, fork it, link it statically or dynamically.
What's inside
30 modules from the mulle-c, mulle-concurrent and mulle-core GitHub organizations, combined into one distribution.
⚡ Lock-free concurrency
Timestamp-based ABA problem solver
Wait- and lock-free linked list
Fixed-size producer/consumer FIFOs
Multi-producer/multi-consumer FIFOs
Threads, mutexes, TLS, atomics
🗄️ Containers & memory
Flexible memory allocation scheme
Arrays, hashtables, queue
Red/black tree
Memory management for tree nodes
Debugging support for containers
Debugging support for red/black trees
🔤 Text, unicode & parsing
Unicode ctype library
UTF-8/16/32 analysis & manipulation
Unicode regex library
URL slug creation
URL character validation (scheme, host, path, …)
Streaming HTTP request/response parser
🔢 Formatting, I/O & time
Extensible sprintf (stdarg + vararg)
mulle-sprintf married to stdio
Double-to-string conversion
Varargs accessed in struct layout
Growable char array & stream, stack + heap
Memory-mapped file access
Cross-platform dynamic loading
timespec/timeval arithmetic
🧩 System & networking
Cross-platform TCP sockets (POSIX + Winsock)
Run programs without fork, system or shell
🧱 Foundation
Cross-platform compiler glue
Collection of hash functions
Quick start
Three steps. CMake downloads the mulle-core sources into your build tree for you.
-
1Fetch it in CMake
include( FetchContent) set( MULLE__CORE_TAG "master" CACHE STRING "branch or tag to fetch mulle-core from") FetchContent_Declare( MULLE__CORE GIT_REPOSITORY https://github.com/mulle-core/mulle-core.git GIT_TAG "${MULLE__CORE_TAG}") FetchContent_MakeAvailable( MULLE__CORE) add_executable( hello-core src/main.c) target_link_libraries( hello-core PRIVATE mulle-core) -
2Write code
#include <mulle-core/mulle-core.h> #include <stdio.h> int main( void) { void *item; // array and buffer are freed automatically when the block exits mulle_pointerarray_do( array) { mulle_pointerarray_add( array, "one"); mulle_pointerarray_add( array, "two"); mulle_buffer_do( buffer) { mulle_pointerarray_for( array, item) { mulle_buffer_add_string( buffer, item); mulle_buffer_add_string( buffer, "\n"); } mulle_buffer_make_string( buffer); fputs( mulle_buffer_get_string( buffer), stdout); } } return( 0); } -
3Build and run
cmake -S . -B build-debug -DCMAKE_BUILD_TYPE=Debug cmake --build build-debug --parallel ./build-debug/hello-core
The first configure clones mulle-core into
build-debug/_deps. To pin a release, pass
-DMULLE__CORE_TAG=0.9.0. Develop with
Debug and ship with Release: a
Debug build keeps assert() active, so mistakes
abort right where they happen. If you'd rather keep the sources in your
own tree, use a submodule or a plain clone and point
add_subdirectory at it. The README lists
all installation methods.
Lock-free concurrency
The resizable hashmap is wait-free: every operation finishes in a bounded number of steps, whatever the other threads are doing. There are no locks, so nothing to spin on and nothing to deadlock.
#include <mulle-core/mulle-core.h>
#include <assert.h>
#include <string.h>
int main( void)
{
struct mulle_concurrent_hashmap map;
char *key = "mulle-core";
void *value;
intptr_t hash;
mulle_aba_init( NULL); // one-time setup
mulle_aba_register(); // per thread
mulle_concurrent_hashmap_init( &map, 0, NULL);
// wait-free from any thread:
// the hash is the key, so it must be a legal one: 0 is NO_HASH
hash = (intptr_t) mulle_hash( key, strlen( key));
if( ! hash)
hash = 1;
mulle_concurrent_hashmap_register( &map, hash, key);
value = mulle_concurrent_hashmap_lookup( &map, hash);
assert( value == key);
mulle_concurrent_hashmap_done( &map);
mulle_aba_unregister();
mulle_aba_done();
return( 0);
}
- Wait-free hashmap. Register, insert, lookup and remove are all wait-free.
- Lock-free linked list & FIFOs. Queues for single and multiple producers and consumers.
- ABA solved. mulle-aba uses per-thread timestamps to make reuse-after-free detectable, on every supported platform.
- Resizing without blocking. The map migrates to a larger table atomically while callers keep working.
- Just C. No runtime and no garbage collector, and it works with any allocator.
Demos
Seven terminal programs, each built so that a constituent does the heavy lifting instead of making a token appearance. They show, with numbers, what each part of mulle-core is good at. Every demo is self-contained and builds mulle-core from source.
mredis — a Redis-shaped server
Speaks RESP2 over TCP with a small command set
(PING, SET, GET, DEL,
EXISTS, QUIT). Each connection gets its
own thread, and all threads share one lock-free hash table. Redis
is single-threaded by design; mredis takes the opposite approach,
which mulle-concurrent makes practical, with
mulle-socket handling the networking. It reaches
10.2M requests/s with 64 connections and
4.1× the throughput of redis-server at eight
threads, because it scales with the number of cores while Redis
stays on one.
multigrep
Parallel text search over memory-mapped files: one producer, N workers and a drain loop, with no locks on the hot path.
logtop
A live terminal dashboard for a log stream that shows counts, a histogram and throughput.
windowrb
Keeps many short-lived ordered sets over a stream, so the cost of creating and tearing down an rbtree shows up in the numbers instead of disappearing as a rounding error.
urltoolkit
Validates, cross-checks and rewrites URLs with two independent parsers, and sorts out the cases where they disagree.
blackboard
Seven processes publish through one shared 256 KiB region. The mmap holds a heap, locks and FIFOs, and a note can be freed by a different process than the one that allocated it.
hashspace
In the concurrent maps the hash is the key, so it matters which hash values are legal. hashspace runs both maps and shows what each one accepts.
CI-tested across platforms
| OS | Compiler | Flags |
|---|---|---|
| Ubuntu | GCC | |
| Ubuntu | GCC | -O3 |
| Ubuntu | GCC | Out-of-source |
| Ubuntu | Clang | Debug |
| Windows | MSVC Win32 | |
| Windows | MSVC Win64 | |
| Windows | GCC | |
| macOS | Clang | |
| macOS | GCC |
Links & Resources
Where to find the source, the documentation, the examples and the people behind mulle-core.
Source, issues and releases
De Re mulle-core, from an empty directory to working code
mredis and six more example programs
All constituent repositories
An Objective-C that runs wherever C does
Questions, help and release news
The modules that make up mulle-core