Utility Methods API for C SDK
This page covers the small utilities that have no other home in the API reference: fetching PubNub server time, reading the SDK version, checking which features are compiled in, and pointing the client at custom DNS servers. It also notes where proxy and TCP keepalive behavior are documented, and what has no public equivalent in this SDK at all.
Server time
Method(s)
#include <pubnub/features/time.h>
pubnub_future_t pubnub_time(pubnub_context_t* ctx);
| Parameter | Direction | Description |
|---|---|---|
ctx | in, borrowed | Must be an already-initialized context (pubnub_create or pubnub_init already succeeded). |
Header: #include <pubnub/features/time.h> (or the umbrella <pubnub/pubnub.h>)
Types: pubnub_future_t, pubnub_timetoken_t
Prerequisite: an initialized context.
Feature flag: PUBNUB_ENABLE_TIME. This flag is ON only in the full build profile. It is OFF in minimal and embedded, so pubnub_time() does not exist in the compiled API on those profiles. See Feature flags for the full per-profile table.
Ownership / lifetime: the timetoken view returned by the result accessor aliases the response buffer and stays valid until pubnub_future_release is called on the same future.
Thread / callback context: the same three consumption styles as every other feature call, with no operation-specific re-entrancy limit.
Blocking: pubnub_time() itself does not block. It submits an asynchronous request and returns a pubnub_future_t immediately. The calling thread only blocks if you choose the blocking consumption style (pubnub_await).
Sample code
Cooperative polling, adapted from the SDK's own examples/time/cooperative.c:
#include <pubnub/pubnub.h>
#include <stdio.h>
int main(void)
{
pubnub_config_t cfg = pubnub_config_defaults();
cfg.subscribe_key = "demo";
cfg.user_id = "example-time-cooperative";
pubnub_context_t* ctx = pubnub_create(&cfg);
if (NULL == ctx) {
printf("pubnub_create failed\n");
return 1;
}
show all 35 linesOther examples
Callback style
Adapted from the SDK's examples/time/async.c. The loop uses the same pubnub_process pattern as the SDK's other callback-style examples in this documentation, without the internal example helper the source file uses for its sleep interval:
#include <pubnub/pubnub.h>
#include <stdio.h>
static volatile int s_done = 0;
static void on_time_complete(pubnub_future_t future, pubnub_res_t status, void* user_data)
{
(void)user_data;
if (PUBNUB_OK == status) {
const pubnub_timetoken_t tt = pubnub_time_result_timetoken(future);
printf("Server time: %.*s\n", (int)tt.len, tt.ptr);
} else {
const pubnub_string_view_t err = pubnub_response_error_message(future);
show all 51 linesNo compiled example in the SDK uses pubnub_await with pubnub_time() specifically. The blocking style works the same as every other feature call: submit with pubnub_time(ctx), then call pubnub_await(fut) in place of the polling loop above.
Returns
pubnub_timetoken_t pubnub_time_result_timetoken(pubnub_future_t future);
Returns the server timetoken as a pubnub_timetoken_t view on success. If the future is not yet ready, carries an error, or the response did not parse, it returns a zero-initialized view ({.ptr = NULL, .len = 0}). Always check pubnub_future_status(future) == PUBNUB_OK before reading the result. Do not infer success from a non-NULL .ptr alone. Call this before pubnub_future_release, since the returned view aliases the future's own response buffer.
Error responses
| Value | Meaning |
|---|---|
PUBNUB_ERR_INVALID_ARGUMENT | ctx is NULL, or the request could not be queued. |
PUBNUB_ERR_SERVER | The server responded with a non-2xx HTTP status. |
PUBNUB_ERR_SERIALIZATION | The response body was empty or did not parse as a timetoken. |
For the full pubnub_res_t catalog, see Status Events.
SDK version
pubnub_sdk_version(void) returns the SDK's version as a static string literal, for example "1.0.0". The value comes from the CMake project version configured at build time (PUBNUB_SDK_VERSION in the generated config.h), not from a runtime lookup. It's a local, compile-time constant, not a PubNub API call.
Feature detection
pubnub_has_feature(ctx, feature), declared in pubnub/capabilities.h, reports whether a given feature is compiled into and active on a context. It returns non-zero if active, 0 if the feature is disabled at compile time or ctx is NULL. feature is a pubnub_feature_t value: PUBNUB_FEATURE_PUBLISH, _SUBSCRIBE, _PRESENCE, _HISTORY, _MESSAGE_ACTIONS, _SIGNAL, _PAM, _APP_CONTEXT, _FILES, _CHANNEL_GROUPS, _CRYPTO, _PUSH_NOTIFICATIONS, or _TIME. Use it to check at runtime whether the build you're linked against can make a given kind of PubNub call, instead of relying only on the PUBNUB_ENABLE_* flag you built with.
int has_presence = pubnub_has_feature(ctx, PUBNUB_FEATURE_PRESENCE);
Futures and async lifecycle
Every feature function that performs network I/O returns a pubnub_future_t by value, declared in #include <pubnub/future.h>. The functions on this page are the shared vocabulary behind the three consumption styles used throughout this documentation (cooperative polling, blocking pubnub_await, callback-driven pubnub_async, see Environment Setup): they are what every one of those styles is actually built from.
bool pubnub_future_is_ready(pubnub_future_t future);
pubnub_res_t pubnub_future_status(pubnub_future_t future);
void pubnub_future_release(pubnub_future_t future);
pubnub_res_t pubnub_await(pubnub_future_t future);
pubnub_res_t pubnub_async(pubnub_future_t future, pubnub_async_cb_t callback, void* user_data);
pubnub_res_t pubnub_future_cancel(pubnub_future_t future);
| Function | Returns | Description |
|---|---|---|
pubnub_future_is_ready(future) | bool | true once the future has reached a terminal state (complete, failed, cancelled, or invalid). |
pubnub_future_status(future) | pubnub_res_t | PUBNUB_IN_PROGRESS while active, otherwise the final result code. Safe to call from multiple threads once the future is terminal. |
pubnub_future_release(future) | void | Returns the request-pool slot to the pool. Must be called exactly once for every future, including a future in an error state and PUBNUB_FUTURE_INVALID. Safe to call from inside a completion callback, and safe to call on a still in-flight future (reclamation is deferred until the operation completes). |
pubnub_await(future) | pubnub_res_t | Blocks until future completes. On a threaded build this polls pubnub_future_is_ready while yielding; on a cooperative build (no background thread) it drives pubnub_process internally, so you do not need your own polling loop. Returns the final status code. |
pubnub_async(future, callback, user_data) | pubnub_res_t | Registers callback to fire exactly once when future reaches a terminal state (firing immediately, inline, if it already has). Returns PUBNUB_ERR_INVALID_ARGUMENT when callback is NULL or future carries a NULL context. pubnub_async_cb_t is void (*)(pubnub_future_t future, pubnub_res_t status, void* user_data). |
pubnub_future_cancel(future) | pubnub_res_t | Cancels a pending or in-flight request. The completion callback (if any) still fires, with PUBNUB_ERR_CANCELLED for a request that was pending. Does not release the slot — call pubnub_future_release afterward as usual. |
The required lifecycle order is always: issue → wait/poll (or await, or register a callback) → read result → release.
PUBNUB_FUTURE_INVALID: a future that never touched the network
Some feature functions validate their arguments synchronously and, on failure, return the PUBNUB_FUTURE_INVALID sentinel instead of a future tied to a real request. No request was ever sent. pubnub_future_is_ready() is already true for it, and pubnub_future_status() reports PUBNUB_ERR_INVALID_ARGUMENT. Because both accessors already behave correctly on it, you do not need to compare a future against this sentinel directly. You do still need to call pubnub_future_release() on it exactly once, like any other future.
pubnub_future_t fut = pubnub_time(NULL /* invalid ctx */);
if (PUBNUB_ERR_INVALID_ARGUMENT == pubnub_future_status(fut)) {
/* fut may be PUBNUB_FUTURE_INVALID, or a real request that failed
validation for another reason -- either way, this check catches it. */
}
pubnub_future_release(fut);
Cooperative polling: pubnub_process return values
pubnub_process(ctx), declared in #include <pubnub/client.h>, pumps the context's event loop for one non-blocking tick: dispatching pending requests, driving transport I/O, and observing completions. Every cooperative-polling example on this site calls it in a loop until the future it is waiting on becomes ready.
| Return value | Meaning |
|---|---|
PUBNUB_OK | The context is quiescent; no work remains right now. This is a normal, expected return, not a completion signal for any specific future. |
PUBNUB_IN_PROGRESS | Work remains (a request is still in flight or pending). Also a normal, expected return. |
PUBNUB_ERR_NOT_INITIALIZED | ctx is not an initialized context. |
PUBNUB_IN_PROGRESS is not an error: it is what most calls to pubnub_process() return while a request is outstanding. Check pubnub_future_is_ready(fut) (or, in a blocking style, let pubnub_await do this for you) to know when to stop calling pubnub_process(), not the return value of pubnub_process() itself.
Context size
size_t pubnub_context_size(void);
pubnub_context_size(void), declared in #include <pubnub/client.h>, returns the number of bytes required for a pubnub_context_t. Call it to size a buffer for pubnub_init(), the caller-provided-memory lifecycle, when the opaque struct's size is not known at compile time:
size_t sz = pubnub_context_size();
pubnub_context_t* ctx = (pubnub_context_t*)malloc(sz);
pubnub_res_t rc = pubnub_init(ctx, &cfg);
The returned size can vary between build configurations (which features are compiled in, which platform provider is linked). Always call pubnub_context_size() at runtime rather than hardcoding a byte count. On a statically or globally declared buffer, pair this with the PUBNUB_ALIGNAS macro from pubnub/pubnub_compat.h and the PUBNUB_CONTEXT_SIZE compile-time upper bound; see No-heap operation and static context storage for the full pattern, including why the alignment macro matters on strictly-aligned targets.
String views
typedef struct pubnub_string_view {
const char* ptr; /* NOT NUL-terminated */
size_t len;
} pubnub_string_view_t;
pubnub_string_view_t, declared in #include <pubnub/types.h>, is the return type of nearly every result accessor in this SDK: message bodies, timetokens, channel names, and every other piece of string data a future's result carries. pubnub_timetoken_t is a type alias for the same struct.
The field ptr is explicitly not NUL-terminated. Reading it with printf("%s", sv.ptr), strlen(sv.ptr), or strcmp(sv.ptr, "...") is undefined behavior: it will read past the intended range in the common case where the underlying buffer happens not to have a '\0' immediately after the last character. Always carry len alongside ptr:
printf("%.*s\n", (int)sv.len, sv.ptr); /* correct */
memcmp(sv.ptr, "expected", sv.len); /* correct, when lengths are also compared */
A view returned by a result accessor aliases memory owned by the future that produced it, and stays valid only until pubnub_future_release() is called on that future (or, for a view inside a subscribe listener callback, only for the duration of that callback invocation). Copy the bytes into your own buffer first if you need them afterward.
Context getters
const char* pubnub_get_user_id(const pubnub_context_t* ctx);
const char* pubnub_get_auth_token(const pubnub_context_t* ctx);
const char* pubnub_get_origin(const pubnub_context_t* ctx);
pubnub_serialization_provider_t* pubnub_serialization(pubnub_context_t* ctx);
All four are declared in #include <pubnub/client.h> and let you inspect a context's current state at runtime.
| Function | Returns NULL when | Lifetime of the returned pointer |
|---|---|---|
pubnub_get_user_id(ctx) | ctx is uninitialized. | Owned by the context. Valid until the next pubnub_set_user_id() call or context destruction, whichever comes first. |
pubnub_get_auth_token(ctx) | ctx is uninitialized, or no token is set. | Owned by the context. Valid until the next pubnub_set_auth_token() call or context destruction. |
pubnub_get_origin(ctx) | ctx is NULL or uninitialized. | Owned by the context. Valid until the next pubnub_set_origin() call or context destruction. |
pubnub_serialization(ctx) | ctx is NULL or uninitialized. | Borrowed vtable pointer, valid until context destruction. |
None of these pointers should be freed by the caller. pubnub_serialization(ctx) returns the context's resolved serialization provider vtable; its optional members may be NULL, so check the specific function pointer you intend to call (for example value_as_string) before invoking it. See Providers — Serialization for the full vtable and which members are optional.
Proxy and TCP keepalive
pubnub_proxy_config_t and pubnub_tcp_keepalive_config_t are pubnub_config_t fields, not separate operations. Their field reference, defaults, and per-transport support notes, including which proxy types and authentication schemes the default curl transport actually honors, live in Configuration and TCP keepalive. This page does not repeat those tables.
One fact is easy to miss because it is stated separately for each struct there: both are copied into the active transport provider once, at pubnub_create()/pubnub_init() time, and neither can be changed for a context that already exists. To change either, build a new pubnub_config_t and create or initialize a new context.
DNS servers
pubnub_set_dns_servers(ctx, primary, secondary), declared in pubnub/client.h, points the context at custom DNS servers instead of system-discovered ones:
pubnub_res_t pubnub_set_dns_servers(pubnub_context_t* ctx,
const char* primary,
const char* secondary);
primary and secondary are each an IPv4 or IPv6 address string (for example "8.8.8.8"), or NULL. Passing NULL for primary clears both servers and reverts to system discovery. This is the current replacement for the legacy C-Core SDK's pubnub_dns_set_primary_server_ipv4() and its siblings, all of which have no direct counterpart here beyond this single setter.
It's also the runtime setter for the dns_primary/dns_secondary config fields described in Configuration, so you can set both at context creation and change them later with the same call. It's thread-safe, including on multi-core RTOS targets. It returns PUBNUB_ERR_NOT_SUPPORTED if the active transport can't honor custom DNS servers, or PUBNUB_ERR_INVALID_ARGUMENT if primary or secondary is non-NULL but not a valid address string.
DNS resolution itself is handled internally, and only by the socket transport. The curl transport delegates resolution to libcurl and the operating system. Beyond pubnub_set_dns_servers(), the only way to influence the internal resolver is through compile-time tunables with no runtime setter or public struct: PUBNUB_CFG_MAX_DNS_RESULTS, PUBNUB_CFG_DNS_CACHE_SIZE, PUBNUB_CFG_MAX_DNS_SERVERS, PUBNUB_CFG_MAX_HOSTNAME_LEN, PUBNUB_CFG_DNS_PLATFORM_STATE_SIZE, and PUBNUB_CFG_DNS_MAX_TTL_SEC. See Environment Setup for how compile-time tunables are set.
No UUID-generation API
No UUID-generation function exists anywhere in this SDK's public headers. If you are migrating from the legacy C-Core SDK, its UUID v1/v3/v4/v5 generators have no counterpart here, and none is planned on this page. Generate a user_id with whatever UUID library your platform already provides.