counters(3)
β¨ counters(3) β Counter Functions β¨
okay bestie, welcome to the counters module from Erlang's own manual (page counters(3), filed under Erlang Module Definition, and yes this is the whole thing, no skips, no cap) π
π DESCRIPTION π
So basically this module hands you a bunch of functions for doing operations on shared mutable counter variables β yes, mutable, we're being chaotic today π«Ά. The way it does that is that it doesn't use any software level locking at all, which is exactly why it's so efficient when a million processes all want a piece of the same counter. It's giving main character energy.
The counters live inside arrays, and here's the semantics of those arrays, read 'em and weep:
- Counters are 64 bit signed integers.
- Counters wrap around at overflow and underflow operations. (yeah they just loop back around, iconic behavior honestly)
- Counters are initialized to zero.
- Write operations guarantee atomicity. No intermediate results can be seen from a single write operation. periodt.
- You can create two types of counter arrays with the options
atomicsorwrite_concurrency. Theatomicscounters have good allround performance with nice consistent semantics, whilewrite_concurrencycounters offer even better concurrent write performance β at the expense of some potential read inconsistencies. Go look atnew/2for the details π - Indexes into counter arrays are one-based. A counter array of size N contains N counters with index from 1 to N. (yes bestie, 1, not 0, we said what we said)
π¦ DATA TYPES π¦
-opaque counters_ref()β this is the thing that identifies a counter array, and it's what you get handed back fromnew/2. Treat it like a little ticket π
π FUNCTIONS π
πΈ add(Ref, Ix, Incr) -> ok
when
Ref :: counters_ref(),
Ix :: pos_integer(),
Incr :: integer().
Adds Incr to the counter sitting at index Ix. That's it, that's the whole job β¨
π get(Ref, Ix) -> integer()
when Ref :: counters_ref(), Ix :: pos_integer().
Reads the counter value. Just reads it, no notes π
π info(Ref) -> Info
when
Ref :: counters_ref(),
Info :: #{size := Size, memory := Memory},
Size :: non_neg_integer(),
Memory :: non_neg_integer().
Returns info about a counter array, wrapped up all cute in a map π
The map has the following keys (at least β so like, minimum these, could be more someday):
sizeβ the number of counters in the array.memoryβ the approximate memory consumption for the array, in bytes. Approximate, bestie, don't come for me with a calculator π₯
π§ new(Size, Opts) -> counters_ref()
when
Size :: pos_integer(),
Opts :: [Opt],
Opt :: atomics | write_concurrency.
Creates a brand new counter array of Size counters, and every single counter in it starts at zero. fresh start, glow up, all of that π
The argument Opts is a list of these possible options:
atomicsβ (this is the Default, so if you don't pick anything, you get this one) Counters will be sequentially consistent. So if write operation A is done sequentially before write operation B, then a concurrent reader may see the result of none of them, only A, or both A and B. It cannot see the result of only B. Very demure, very mindful, very consistent πΈwrite_concurrencyβ this one is an optimization to achieve very efficient concurrentaddandsuboperations, and it pays for that with potential read inconsistency and memory consumption per counter. So like... tradeoffs, queen π
Read operations may see sequentially inconsistent results with regard to concurrent write operations. Even if write operation A is done sequentially before write operation B, a concurrent reader may see any combination of A and B, including only B. A read operation is only guaranteed to see all writes done sequentially before the read. No writes are ever lost, though β they will eventually all be seen. (not lost, just fashionably late π)
The typical use case for write_concurrency is when concurrent calls to add and sub toward the same counters are very frequent, while calls to get and put are much less frequent. The lack of absolute read consistency must also be acceptable. So know what you're signing up for, fr fr.
Counters are not tied to the current process, and they get automatically garbage collected when they're no longer referenced. they clean up after themselves, we love a self-sufficient icon π«Ά
π put(Ref, Ix, Value) -> ok
when
Ref :: counters_ref(),
Ix :: pos_integer(),
Value :: integer().
Writes Value to the counter at index Ix.
NOTE: Despite its name, thewrite_concurrencyoptimization does not improveput. A call toputis a relatively heavy operation compared to the very lightweight and scalableaddandsub. The cost for aputwithwrite_concurrencyis like agetplus aputwithoutwrite_concurrency. so yeah, the name is a lil misleading, don't @ me, I'm just the messenger π
π¦ sub(Ref, Ix, Decr) -> ok
when
Ref :: counters_ref(),
Ix :: pos_integer(),
Decr :: integer().
Subtracts Decr from the counter at index Ix. math but make it concurrent β
and that's the whole page, bestie!! no crumbs left, we kept every single option, every type spec, every warning, all of it π
signing off with the receipts: this page is brought to you by Ericsson AB, running erts 17.0.6, and it's the counters(3) page. slay, go increment something responsibly ππ
π¦ where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms