한국어 | English
Core Spec Index | Previous: Monitoring | Next: Runtime Boundary
Utilities¶
What this chapter defines — The public contracts of utility APIs that do not belong to an individual category, including atomic counters, timers, stopwatches, capability detection, proxies, and thread helpers.
1. Utilities Overview¶
zlink Core provides common runtime features that are not part of the messaging contract as utility APIs. These include atomic counters for atomically handling a shared integer, nanosecond-resolution timers, stopwatches as high-resolution clocks, capability detection for checking which features a library build includes, proxies for forwarding messages between two raw sockets, and sleep and thread helpers.
This document defines the public contracts of these utilities. Its intended readers are developers who map each utility's lifecycle, thread safety, and callback ownership to the C API and each language binding. It answers the question, "When using common runtime features, how should the lifetime of each handle and callback, the scope of concurrent calls, and return values be interpreted?"
The following documents own the related contracts.
| Related contract | Defining document |
|---|---|
zlink_poller_add_timer, which registers a timer with a poller |
Poll and Poller |
| Raw socket creation and send/receive contracts (the targets forwarded by a proxy) | Socket Common |
| Context lifetime and termination | Context |
2. Atomic Counter¶
An atomic counter provides atomic increment, decrement, and read operations on
a single integer shared by multiple threads. Create a counter with
zlink_atomic_counter_new and destroy it with zlink_atomic_counter_destroy.
zlink_atomic_counter_new¶
Creates a new atomic counter initialized to zero.
Allocates and returns an opaque handle to an atomic counter whose initial value is zero.
Returns: A counter handle on success. If memory allocation fails, the
process aborts instead of returning NULL.
Thread safety: May be called from any thread.
See also: zlink_atomic_counter_set, zlink_atomic_counter_destroy
zlink_atomic_counter_set¶
Sets the counter to an explicit value.
Replaces the current counter value with value_.
Thread safety: Not thread-safe. It must not be called concurrently with another operation on the same counter. It is typically used only during initial setup.
See also: zlink_atomic_counter_value
zlink_atomic_counter_inc¶
Increments the counter by one.
Atomically increments the counter and returns its previous value (the value immediately before the increment).
Returns: The counter value before the increment.
Thread safety: May be called from any thread.
See also: zlink_atomic_counter_dec
zlink_atomic_counter_dec¶
Decrements the counter by one.
Atomically decrements the counter and returns 1 if it remains greater than
zero after the decrement, or 0 if it reaches zero.
Returns: 1 if the counter is still nonzero after the decrement, or 0
if it has reached zero.
Thread safety: May be called from any thread.
See also: zlink_atomic_counter_inc
zlink_atomic_counter_value¶
Returns the current counter value.
Atomically reads the current value of the counter.
Returns: The current counter value.
Thread safety: May be called from any thread.
See also: zlink_atomic_counter_set
zlink_atomic_counter_destroy¶
Destroys the counter and releases its memory.
Releases the counter handle. After destruction, the pointer at *counter_p_
is set to NULL.
Thread safety: It must not be called while another thread is operating on the same counter.
See also: zlink_atomic_counter_new
3. Timer¶
A timer provides a nanosecond-resolution periodic or one-shot generic timer.
Create a standalone timer with zlink_timer_new. A timer fire event is
received with zlink_timer_recv or integrated into a poller with
zlink_poller_add_timer—Poll and Poller owns the poller
integration contract.
%%{init: {'sequence': {'actorFontSize': '18px', 'messageFontSize': '18px', 'noteFontSize': '18px', 'boxMargin': 8, 'width': 140}, 'themeVariables': {'fontSize': '18px'}}}%%
sequenceDiagram
participant App as Application
participant T as Timer
App->>T: zlink_timer_new()
App->>T: zlink_timer_start(interval_ns, repeat_count)
Note over T: First fire after interval_ns nanoseconds,<br/>then repeats at the same interval
App->>T: zlink_timer_recv()
T-->>App: fire count (increments from 1 within this start)
Note over T: If repeat_count is positive, automatically stops after that many fires
App->>T: zlink_timer_stop() / zlink_timer_destroy()
zlink_timer_new¶
Creates a standalone timer.
Allocates and returns an opaque timer handle. Destroy it with
zlink_timer_destroy when it is no longer needed.
Returns: A timer handle on success, or NULL on failure. On failure, errno
is set.
Thread safety: May be called from any thread.
See also: zlink_timer_destroy
zlink_timer_destroy¶
Destroys a timer and releases its resources.
Stops a running timer and releases its handle. After destruction,
*timer_p_ is set to NULL.
Returns: ZLINK_CLOSE_OK on success, or a zlink_close_result_t value
on failure. zlink_errno() preserves the internal errno for diagnostics.
Thread safety: It must not be called while another thread is using the same timer.
See also: zlink_timer_new
zlink_timer_start¶
Starts a timer.
ZLINK_EXPORT zlink_config_result_t zlink_timer_start (void *timer_,
uint64_t interval_ns_,
uint64_t repeat_count_);
Starts the timer so that its first event occurs after interval_ns_
nanoseconds. interval_ns_ is the interval between events in nanoseconds and
must not be 0. If repeat_count_ is 0, the timer repeats until
explicitly stopped. If it is positive, the timer generates that many events
and then stops automatically. Each successful start resets the fire count, so
the first fire is 1, followed by 2, 3, and so on. Calling it again on a
running timer also succeeds — the new start replaces the existing schedule with
the new interval and repeat count, and discards any fire not yet read and any
poller readiness from the previous start.
Returns: ZLINK_CONFIG_OK on success, or a zlink_config_result_t value
on failure. zlink_errno() preserves the internal errno for diagnostics.
Errors: If interval_ns_ == 0, the result is
ZLINK_CONFIG_INVALID_ARGUMENT and the internal errno is EINVAL.
Thread safety: It must not be called concurrently with another operation on the same timer.
See also: zlink_timer_stop
zlink_timer_stop¶
Stops a running timer.
Stops the timer. It generates no new fire events until it is started again.
Returns: ZLINK_CONFIG_OK on success, or a zlink_config_result_t value
on failure. zlink_errno() preserves the internal errno for diagnostics.
Thread safety: It must not be called concurrently with another operation on the same timer.
See also: zlink_timer_start
zlink_timer_recv¶
Synchronously receives a timer fire.
In receive mode, waits for the next timer fire. On success,
*fire_count_out_ is set to the fire count that starts from 1 within the most
recent start execution.
Returns: ZLINK_RECV_OK on success, or a zlink_recv_result_t value on
failure. zlink_errno() preserves the internal errno for diagnostics.
Errors: If timer_ is invalid or fire_count_out_ == NULL, the result is
ZLINK_RECV_INVALID_HANDLE (internal EFAULT). If the timer has already
stopped and there is no fire left to read, the result is ZLINK_RECV_NO_DATA
(internal EAGAIN).
Thread safety: It must not be called concurrently with another operation on the same timer.
See also: zlink_timer_start
4. Stopwatch¶
A stopwatch provides high-resolution timing functions for benchmarking and profiling. Start the stopwatch, read intermediate measurements, and stop it to obtain the total elapsed time in microseconds.
zlink_stopwatch_start¶
Starts a high-resolution stopwatch.
Captures the current time and returns an opaque handle used to measure elapsed
time. The handle must eventually be released with zlink_stopwatch_stop.
Returns: An opaque stopwatch handle on success. If memory allocation fails,
the process aborts instead of returning NULL.
Thread safety: May be called from any thread. The returned handle must be used by only one thread at a time.
See also: zlink_stopwatch_intermediate, zlink_stopwatch_stop
zlink_stopwatch_intermediate¶
Returns elapsed microseconds without stopping the stopwatch.
Reads the elapsed time since zlink_stopwatch_start was called without
releasing the handle. It may be called multiple times for successive
measurements.
Returns: Elapsed time in microseconds.
Thread safety: It must not be called concurrently with
zlink_stopwatch_stop on the same handle.
See also: zlink_stopwatch_start, zlink_stopwatch_stop
zlink_stopwatch_stop¶
Stops the stopwatch and returns the total elapsed microseconds.
Returns the total elapsed time since zlink_stopwatch_start was called and
releases the stopwatch handle. The handle must not be used after this call.
Returns: Elapsed time in microseconds.
Thread safety: It must not be called concurrently with another operation on the same handle.
See also: zlink_stopwatch_start, zlink_stopwatch_intermediate
5. Capability Detection¶
Use zlink_has at runtime to determine which features were included when the
library was built.
zlink_has¶
Checks whether the current library build provides a capability.
capability_ is a non-NULL, NUL-terminated string, and the function does not
retain it. "tcp" is always true. "ipc", "tls", "ws", and
"wss" are true only when the library was built with the corresponding
feature. Any other string is false.
Thread safety: Does not mutate global state and may be called from any thread.
6. Proxy¶
A proxy is a blocking helper that forwards multipart messages bidirectionally between two raw sockets. Socket Common owns the creation and send/receive contracts of raw sockets.
zlink_proxy¶
Forwards multipart messages bidirectionally between two raw sockets.
frontend_ and backend_ are required raw socket handles. capture_ may
be NULL; when non-NULL, it is a raw socket handle that receives a copy of
every forwarded message. The function blocks the calling thread until the
running proxy loop ends.
All three handles are borrowed. The function neither closes nor owns them. The proxy receives message frames and forwards them to the opposite socket without returning frame pointers to the application.
The proxy forwards only raw multipart messages on the application lane. Even if a received message
has an internal ZMP request-reply kind and sequence, the proxy removes them before sending the
message to the opposite socket and to capture_. Both destinations therefore receive the same
application part count, order, and bytes, while a frame sent back to the wire has the ordinary data
kind.
The proxy does not bridge request correlation or reply-target state. This API does not transparently complete a request across a proxy, and the proxy neither creates request-reply metadata nor forwards completion records.
Returns: The proxy loop has no normal termination condition and no public
stop API, so there is no path on which this function returns ZLINK_CONFIG_OK.
When a poll, receive, or send fails — including socket or context termination —
the function returns that errno as a zlink_config_result_t error and the
calling thread is released. If a required handle is NULL or is not a raw
socket, the result is ZLINK_CONFIG_INVALID_HANDLE before the loop starts.
7. Sleep and Thread¶
These are a portable sleep function that wraps platform-specific APIs and an OS thread helper.
zlink_thread_fn¶
This is the entry-point signature of a thread started with
zlink_thread_start.
zlink_sleep¶
Sleeps for the specified number of seconds.
A convenience wrapper that applies the platform sleep (Sleep() or POSIX
sleep()) once to the calling thread. On POSIX, if sleep() is interrupted by
a signal the remaining time is not slept again, so the call may return earlier
than seconds_ seconds.
Thread safety: May be called from any thread.
See also: zlink_stopwatch_start
zlink_thread_start¶
Starts a new thread that runs the specified function.
Creates and starts a new operating-system thread that executes func_ with
arg_ as its sole argument. The returned handle must be passed to
zlink_thread_join to wait for completion and release resources.
Returns: An opaque thread handle on success. If allocation of the handle or
creation of the operating-system thread fails, the process aborts instead of
returning NULL.
Thread safety: May be called from any thread.
See also: zlink_thread_join
zlink_thread_join¶
Waits for a thread to finish and releases its handle.
Blocks the calling thread until the thread identified by thread_ terminates,
then releases the handle. The handle must not be used after this call.
Thread safety: Must be called exactly once per handle. It must not be called from the thread being joined.
See also: zlink_thread_start
8. Implementation and Contract Test Verification Requirements¶
Verify the following using only the public surface (utility functions, return values, and errno). Each item maps to one unit test.
Atomic counter
- A counter created with
zlink_atomic_counter_newhas an initial value of zero—zlink_atomic_counter_valuereturns0immediately after creation. If a memory allocation failure is injected, the function aborts the process instead of returningNULL. - After
zlink_atomic_counter_set,zlink_atomic_counter_valuereturns the set value. zlink_atomic_counter_increturns the value immediately before the increment.zlink_atomic_counter_decreturns1if the counter remains greater than zero after the decrement, or0if it reaches zero.- It is safe for multiple threads to call inc, dec, and value concurrently on the same counter—updates are not lost because increments and decrements are atomic.
- After
zlink_atomic_counter_destroy,*counter_p_isNULL.
Timer
zlink_timer_newreturns a non-NULL handle on success, orNULLwith errno set on failure.zlink_timer_start(timer, 0, repeat_count)fails withZLINK_CONFIG_INVALID_ARGUMENTand internalEINVAL.- When
zlink_timer_startreceives a positiverepeat_count_, the timer fires that many times and then stops automatically. When it receives0, the timer repeats until explicitly stopped. zlink_timer_recvwaits for the next fire and, on success, writes to*fire_count_out_the fire count that starts from 1 within the current start execution. After a stop followed by another start, the first value is1again. If the timer has already stopped and no fire remains to be read, the result isZLINK_RECV_NO_DATA(internalEAGAIN).- After
zlink_timer_stop, no new fire event occurs until the timer is started again. - After
zlink_timer_destroy,*timer_p_isNULL.
Stopwatch
- If a memory allocation failure is injected into
zlink_stopwatch_start, the function aborts the process instead of returningNULL. zlink_stopwatch_intermediatereturns the elapsed microseconds since start without releasing the handle and may be called multiple times with the same handle.zlink_stopwatch_stopreturns the total elapsed microseconds since start and releases the handle.
Capability detection
zlink_has("tcp")is alwaystrue."ipc","tls","ws", and"wss"aretrueonly when the library was built with the corresponding feature, and any other string isfalse.
Proxy
- If a required handle passed to
zlink_proxyisNULLor is not a raw socket, the result isZLINK_CONFIG_INVALID_HANDLE. - When a non-NULL
capture_is supplied, a copy of every forwarded message arrives at the capture socket. - The proxy blocks the calling thread until the loop ends and returns
ZLINK_CONFIG_OKwhen it ends normally. - The supplied handles are borrowed—the caller still owns them after the proxy ends, and the function does not close them.
- When a raw fixture with request, reply, or error-reply kind is sent through the proxy, the opposite socket and a non-NULL capture socket receive the same application multipart as an ordinary message; sending it back to the raw wire produces the data kind.
- Even though DEALER-ROUTER uses a single connection, the proxy does not bridge request correlation or reply-target state, so a reply on its far side does not automatically complete the original request.
Sleep and thread
zlink_sleep(n)suspends the calling thread for at leastnseconds.zlink_thread_startstarts a thread that executesfunc_witharg_as its sole argument and returns a handle on success. If a handle allocation or operating-system thread creation failure is injected, the function aborts the process instead of returningNULL.zlink_thread_joinwaits until the target thread terminates and then releases the handle, and it is called exactly once per handle.
Common return convention
- Each function that returns a result type (
zlink_close_result_t,zlink_config_result_t, orzlink_recv_result_t) returns the corresponding OK value on success and a result value on failure, whilezlink_errno()preserves the internal errno for diagnostics.
Core Spec Index | Previous: Monitoring | Next: Runtime Boundary