VLink  2.0.0
A high-performance communication middleware
utils.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file utils.h
26  * @brief Portable host-system utility surface used across the VLink runtime.
27  *
28  * @details
29  * @c vlink::Utils gathers free functions that paper over differences between Linux, macOS,
30  * Windows, QNX and Android. All public entry points are @c noexcept and report failure
31  * via empty strings, @c false return values or sentinel values such as @c pid @c == @c -1.
32  *
33  * Helper categories provided by the namespace:
34  *
35  * | Category | Representative entry points |
36  * | ---------------------- | ----------------------------------------------------------------- |
37  * | Process introspection | @c get_app_path, @c get_app_dir, @c get_app_name, @c get_pid |
38  * | Host identity | @c get_host_name, @c get_machine_id, @c get_timezone_diff |
39  * | Filesystem helpers | @c get_tmp_dir, @c wait_for_device |
40  * | Environment management | @c get_env, @c set_env, @c unset_env |
41  * | Network discovery | @c get_all_ipv4_address, @c get_dds_default_address |
42  * | Thread control | @c set_thread_name, @c set_thread_priority, @c set_thread_stick |
43  * | Signal handling | @c register_terminate_signal, @c register_crash_signal |
44  * | Input/terminal | @c start_detect_keyboard, @c get_terminal_size |
45  * | System metrics | @c get_cpu_usage, @c get_memory_usage, @c is_process_running |
46  * | Memory hints | @c try_release_sys_memory |
47  * | Low-level primitives | @c yield_cpu |
48  *
49  * @note
50  * - @c yield_cpu() is inlined to emit the optimal idle hint per ISA: @c PAUSE on x86,
51  * @c YIELD on ARM, an explicit fence on RISC-V, otherwise @c std::this_thread::yield().
52  *
53  * @par Example
54  * @code
55  * vlink::Utils::set_thread_name("worker");
56  * vlink::Utils::set_thread_stick(0b11);
57  *
58  * vlink::Utils::register_terminate_signal([](int sig) {
59  * (void)sig;
60  * shutdown_application();
61  * });
62  * @endcode
63  */
64 
65 #pragma once
66 
67 #include <string>
68 #include <thread>
69 #include <utility>
70 #include <vector>
71 
72 #include "./functional.h"
73 #include "./macros.h"
74 
75 #ifdef _MSC_VER
76 extern "C" void _mm_pause(void);
77 #endif
78 
79 namespace vlink {
80 
81 /**
82  * @namespace vlink::Utils
83  * @brief Portable host-system utility functions.
84  */
85 namespace Utils { // NOLINT(readability-identifier-naming)
86 
87 /**
88  * @brief Returns the absolute path of the executable that is currently running.
89  *
90  * @return Full filesystem path, or an empty string when the OS query fails.
91  */
92 [[nodiscard]] VLINK_EXPORT std::string get_app_path() noexcept;
93 
94 /**
95  * @brief Returns the directory portion of the executable's absolute path.
96  *
97  * @return Directory without a trailing separator, or an empty string on failure.
98  */
99 [[nodiscard]] VLINK_EXPORT std::string get_app_dir() noexcept;
100 
101 /**
102  * @brief Returns the file-name portion of the executable's absolute path.
103  *
104  * @return Executable name, or an empty string on failure.
105  */
106 [[nodiscard]] VLINK_EXPORT std::string get_app_name() noexcept;
107 
108 /**
109  * @brief Returns the local machine's host name as reported by the operating system.
110  *
111  * @return Host name string, or an empty string on failure.
112  */
113 [[nodiscard]] VLINK_EXPORT std::string get_host_name() noexcept;
114 
115 /**
116  * @brief Returns the process identifier of the calling process.
117  *
118  * @return PID, or @c -1 on failure.
119  */
120 [[nodiscard]] VLINK_EXPORT int32_t get_pid() noexcept;
121 
122 /**
123  * @brief Returns the process identifier of the calling process as a decimal string.
124  *
125  * @return PID string, or an empty string on failure.
126  */
127 [[nodiscard]] VLINK_EXPORT std::string get_pid_str() noexcept;
128 
129 /**
130  * @brief Returns a path suitable for short-lived temporary files.
131  *
132  * @details
133  * Honours the @c VLINK_TMP_DIR environment override. Otherwise falls back to
134  * @c std::filesystem::temp_directory_path(), or @c /var/log on QNX.
135  *
136  * @return Temporary directory path.
137  */
138 [[nodiscard]] VLINK_EXPORT std::string get_tmp_dir() noexcept;
139 
140 /**
141  * @brief Reads the current value of an environment variable.
142  *
143  * @param key Variable name.
144  * @param default_value Value returned when @p key is not set. Default: empty string.
145  * @return Variable value, or @p default_value when unset.
146  */
147 [[nodiscard]] VLINK_EXPORT std::string get_env(const std::string& key, const std::string& default_value = "") noexcept;
148 
149 /**
150  * @brief Sets or updates an environment variable.
151  *
152  * @param key Variable name.
153  * @param value Variable value.
154  * @param force When @c true (default), overwrites an existing variable.
155  * @return @c true on success.
156  */
157 VLINK_EXPORT bool set_env(const std::string& key, const std::string& value, bool force = true) noexcept;
158 
159 /**
160  * @brief Removes an environment variable.
161  *
162  * @param key Variable name.
163  * @return @c true on success.
164  */
165 VLINK_EXPORT bool unset_env(const std::string& key) noexcept;
166 
167 /**
168  * @brief Returns whether an interface name is treated as virtual/tunnel-only by address discovery.
169  *
170  * @details Used by @c get_all_ipv4_address(true) and @c get_all_ipv6_address(true) to skip
171  * container, VPN, bridge and tunnel adapters when choosing transport discovery addresses.
172  *
173  * @param name Interface name; @c nullptr is treated as ignored.
174  * @return @c true when the name should be filtered out.
175  */
176 [[nodiscard]] VLINK_EXPORT bool is_ignored_iface_name(const char* name) noexcept;
177 
178 /**
179  * @brief Returns every IPv4 address bound to a local network interface.
180  *
181  * @param filter_available When @c true, only includes interfaces in the UP state. Default: @c false.
182  * @return Vector of dotted-decimal strings.
183  */
184 [[nodiscard]] VLINK_EXPORT std::vector<std::string> get_all_ipv4_address(bool filter_available = false) noexcept;
185 
186 /**
187  * @brief Returns every IPv6 address bound to a local network interface.
188  *
189  * @param filter_available When @c true, only includes interfaces in the UP state. Default: @c false.
190  * @return Vector of IPv6 strings.
191  */
192 [[nodiscard]] VLINK_EXPORT std::vector<std::string> get_all_ipv6_address(bool filter_available = false) noexcept;
193 
194 /**
195  * @brief Returns the interface name that owns a given IPv4 address.
196  *
197  * @param ipv4 Address to look up.
198  * @return Interface name, or an empty string when no match exists.
199  */
200 [[nodiscard]] VLINK_EXPORT std::string get_interface_name_by_ipv4(const std::string& ipv4) noexcept;
201 
202 /**
203  * @brief Returns the interface name that owns a given IPv6 address.
204  *
205  * @param ipv6 Address to look up.
206  * @return Interface name, or an empty string when no match exists.
207  */
208 [[nodiscard]] VLINK_EXPORT std::string get_interface_name_by_ipv6(const std::string& ipv6) noexcept;
209 
210 /**
211  * @brief Selects IPv4 addresses suitable as DDS participant unicast locators.
212  *
213  * @details
214  * When @c 127.0.0.1 is present it is placed first in the result; the remaining
215  * non-loopback unicast addresses are then appended until @c max_count is reached.
216  * Loopback is therefore included (as the leading entry) rather than filtered out.
217  *
218  * @param filter_available When @c true, only includes UP interfaces. Default: @c false.
219  * @param max_count Upper bound on the number of returned addresses. Default: @c 5.
220  * @return Vector of selected IPv4 strings.
221  */
222 [[nodiscard]] VLINK_EXPORT std::vector<std::string> get_dds_default_address(bool filter_available = false,
223  int max_count = 5) noexcept;
224 
225 /**
226  * @brief Provides a singleton mutual-exclusion check for a program name.
227  *
228  * @details
229  * Uses a Win32 mutex on Windows and a POSIX lock file under @c VLINK_LOCK_DIR (or the
230  * platform default) elsewhere. Returns @c false when another instance already holds
231  * the lock.
232  *
233  * @param program_name Program tag used to build the lock identity. Empty defaults to the executable name.
234  * @return @c true when this process holds the singleton lock.
235  */
236 [[nodiscard]] VLINK_EXPORT bool check_singleton(const std::string& program_name = "") noexcept;
237 
238 /**
239  * @brief Polls a filesystem path until it exists or a timeout expires.
240  *
241  * @details
242  * Useful for waiting for device nodes (e.g. @c /dev/video0) at startup.
243  *
244  * @param path Filesystem path to poll.
245  * @param timeout_ms Maximum total wait in milliseconds.
246  * @param poll_ms Polling interval in milliseconds. Default: @c 50.
247  * @return @c true when the path appears within the timeout.
248  */
249 VLINK_EXPORT bool wait_for_device(const std::string& path, int timeout_ms, int poll_ms = 50) noexcept;
250 
251 /**
252  * @brief Emits the most efficient CPU pause/yield hint for the host ISA.
253  *
254  * @details
255  * Maps to @c PAUSE on x86, @c YIELD on ARMv7/AArch64, an explicit fence on RISC-V, and
256  * @c std::this_thread::yield() as a portable fallback. Always inlined.
257  */
258 VLINK_EXPORT void yield_cpu() noexcept;
259 
260 /**
261  * @brief Sets the Windows console output code page to UTF-8.
262  *
263  * @details
264  * Calls @c SetConsoleOutputCP(CP_UTF8); a no-op on non-Windows targets.
265  */
267 
268 /**
269  * @brief Sets the OS-visible name of a thread so debug tools display it.
270  *
271  * @param name Thread name; Linux truncates beyond 15 characters.
272  * @param thread Thread to rename, or @c nullptr for the calling thread. Default: @c nullptr.
273  * @return @c true on success.
274  */
275 VLINK_EXPORT bool set_thread_name(const std::string& name, std::thread* thread = nullptr) noexcept;
276 
277 /**
278  * @brief Updates the scheduling policy and priority of a thread.
279  *
280  * @details
281  * On Linux wraps @c pthread_setschedparam. Real-time policies require @c CAP_SYS_NICE
282  * or an adequate @c RLIMIT_RTPRIO. POSIX policy constants (@c SCHED_FIFO, @c SCHED_RR,
283  * @c SCHED_OTHER) come from @c <sched.h>, which callers must include. On Windows the
284  * value is mapped to a thread priority class or silently ignored.
285  *
286  * @param priority_level Policy-dependent priority value.
287  * @param policy Scheduling policy; @c -1 leaves the existing policy untouched.
288  * @param thread Thread to configure, or @c nullptr for the calling thread. Default: @c nullptr.
289  * @return @c true on success.
290  */
291 VLINK_EXPORT bool set_thread_priority(int priority_level, int policy = -1, std::thread* thread = nullptr) noexcept;
292 
293 /**
294  * @brief Pins a thread to a set of CPU cores expressed as a bitmask.
295  *
296  * @details
297  * Bit @c i of @p core_mask corresponds to core @c i, so @c 0b0101 pins to cores @c 0 and
298  * @c 2. Wraps @c pthread_setaffinity_np on Linux.
299  *
300  * @param core_mask Bitmask of CPU cores.
301  * @param thread Thread to pin, or @c nullptr for the calling thread. Default: @c nullptr.
302  * @return @c true on success.
303  */
304 VLINK_EXPORT bool set_thread_stick(uint32_t core_mask, std::thread* thread = nullptr) noexcept;
305 
306 /**
307  * @brief Returns the native OS thread identifier of the calling thread.
308  *
309  * @details
310  * Maps to:
311  * - Linux / Android / QNX: @c syscall(SYS_gettid) — kernel TID matching @c top / @c perf.
312  * - macOS / iOS: @c pthread_threadid_np() — 64-bit Mach thread id.
313  * - Windows: @c GetCurrentThreadId() — Win32 thread id.
314  *
315  * @return Native thread identifier.
316  */
317 [[nodiscard]] VLINK_EXPORT uint64_t get_native_thread_id() noexcept;
318 
319 /**
320  * @brief Installs a callback for graceful termination signals.
321  *
322  * @details
323  * Hooks @c SIGINT, @c SIGTERM and @c SIGHUP on POSIX, or @c SIGINT / @c SIGTERM on Windows.
324  *
325  * @param callback Callback receiving the signal number.
326  * @param is_async When @c true, runs the callback on a dedicated thread instead of the
327  * signal context. Default: @c false.
328  * @param pass_through When @c true, re-raises the signal after the callback returns so the
329  * default OS behaviour also fires. Default: @c false.
330  */
331 VLINK_EXPORT void register_terminate_signal(MoveFunction<void(int)>&& callback, bool is_async = false,
332  bool pass_through = false) noexcept;
333 
334 /**
335  * @brief Installs a callback for crash signals such as @c SIGSEGV, @c SIGABRT, @c SIGFPE, @c SIGBUS.
336  *
337  * @details
338  * Useful for emitting crash diagnostics. The callback should be async-signal-safe and
339  * short.
340  *
341  * @param callback Callback receiving the signal number.
342  */
343 VLINK_EXPORT void register_crash_signal(MoveFunction<void(int)>&& callback) noexcept;
344 
345 /**
346  * @brief Starts a background poller that detects keyboard input on stdin.
347  *
348  * @details
349  * Calls @p callback with a key name string (such as @c "enter" or @c "q") whenever a
350  * key is detected. Stop with @c stop_detect_keyboard().
351  *
352  * @param callback Callback receiving the key name. Default: @c nullptr (ignore events).
353  * @param poll_ms Polling interval in milliseconds. Default: @c 20.
354  */
355 VLINK_EXPORT void start_detect_keyboard(MoveFunction<void(const std::string& key)>&& callback = nullptr,
356  int poll_ms = 20) noexcept;
357 
358 /**
359  * @brief Stops the keyboard poller started by @c start_detect_keyboard().
360  */
362 
363 /**
364  * @brief Returns the current terminal dimensions in columns and rows.
365  *
366  * @return Pair @c {columns, @c rows}; @c {-1, @c -1} when stdout is not a TTY.
367  */
368 [[nodiscard]] VLINK_EXPORT std::pair<int, int> get_terminal_size() noexcept;
369 
370 /**
371  * @brief Returns the system-wide CPU usage as a percentage averaged over all logical CPUs.
372  *
373  * @details
374  * Implemented as @c (1 @c - @c idle_delta @c / @c total_delta) @c * @c 100 from
375  * @c /proc/stat on Linux/Android or @c GetSystemTimes on Windows. Because the previous
376  * sample is zero-initialised, the first call after process start reports the average usage
377  * since boot rather than the usage over an interval.
378  *
379  * @return Percentage in @c [0.0, @c 100.0], or @c -1.0 on error / unsupported platform.
380  */
381 [[nodiscard]] VLINK_EXPORT double get_cpu_usage() noexcept;
382 
383 /**
384  * @brief Returns the system-wide memory usage as a percentage of total physical RAM.
385  *
386  * @return Percentage in @c [0.0, @c 100.0], or @c -1.0 on error / unsupported platform.
387  */
388 [[nodiscard]] VLINK_EXPORT double get_memory_usage() noexcept;
389 
390 /**
391  * @brief Reports whether at least one process with the given executable name is alive.
392  *
393  * @param process_name Executable name without a path component.
394  * @return @c true when a matching process exists.
395  */
396 [[nodiscard]] VLINK_EXPORT bool is_process_running(const std::string& process_name) noexcept;
397 
398 /**
399  * @brief Returns the local timezone offset from UTC in minutes.
400  *
401  * @details
402  * UTC+8 returns @c 480; UTC-5 returns @c -300.
403  *
404  * @return Offset in minutes.
405  */
406 [[nodiscard]] VLINK_EXPORT int32_t get_timezone_diff() noexcept;
407 
408 /**
409  * @brief Returns a stable identifier for the host machine.
410  *
411  * @details
412  * Reads @c /etc/machine-id on Linux and uses platform-specific equivalents elsewhere.
413  *
414  * @return Machine ID string, or an empty string on failure.
415  */
416 [[nodiscard]] VLINK_EXPORT std::string get_machine_id() noexcept;
417 
418 /**
419  * @brief Hints to the OS that any unreferenced cached memory pages can be released.
420  *
421  * @details
422  * On Linux invokes @c malloc_trim(0); on other platforms it is a no-op.
423  */
425 
426 ////////////////////////////////////////////////////////////////
427 /// Details
428 ////////////////////////////////////////////////////////////////
429 
430 #if defined(__GNUC__) && !defined(_WIN32) && !defined(__CYGWIN__)
431 inline __attribute__((artificial)) void yield_cpu() noexcept {
432 #else
433 inline void yield_cpu() noexcept {
434 #endif
435 #if __has_builtin(__yield)
436  __yield();
437 #elif defined(_MSC_VER) && defined(_YIELD_PROCESSOR)
438  _YIELD_PROCESSOR();
439 #elif __has_builtin(__builtin_ia32_pause)
440 __builtin_ia32_pause();
441 #elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)
442 #if defined(_MSC_VER)
443 _mm_pause();
444 #elif defined(__GNUC__)
445 __builtin_ia32_pause();
446 #else
447 __asm__("pause");
448 #endif
449 #elif __has_builtin(__builtin_arm_yield)
450 __builtin_arm_yield();
451 #elif defined(__arm__) || defined(__aarch64__)
452 #if defined(__GNUC__)
453 __asm__("yield");
454 #else
455 std::this_thread::yield();
456 #endif
457 #elif defined(__riscv)
458 __asm__(".word 0x0100000f");
459 #elif defined(_MSC_VER) && defined(_YIELD_PROCESSOR)
460 _YIELD_PROCESSOR();
461 #else
462 std::this_thread::yield();
463 #endif
464 }
465 
466 } // namespace Utils
467 
468 } // namespace vlink
Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction.
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VLINK_EXPORT
Definition: macros.h:81