Instruction file imported from rdkcentral/Thunder (
.github/instructions/core.instructions.md). Copyright stays with the author.
Thunder core/ Library
Source/core/ is a self-contained, dependency-free platform-primitive library. It has zero dependency on Source/com/, Source/plugins/, Source/Thunder/, or any ThunderInterfaces. Every addition must remain usable without the plugin framework.
Dependency Rule
- Never
#includefromSource/plugins/,Source/com/, orSource/Thunder/insideSource/core/. - Allowed includes: other
core/headers, OS system headers,Module.h,Portability.h. - Use
#ifdef __LINUX__/#ifdef __WINDOWS__/#ifdef __POSIX__/#ifdef __UNIX__for OS-specific code — always guarded, never assumed.
Prefer core/ Abstractions over Standard Library
When writing or reviewing code in Source/core/, generated plugins, or any Thunder component, prefer Thunder's own abstractions over raw C++ standard library equivalents:
| Instead of… | Use… |
|---|---|
std::mutex / std::lock_guard |
Core::CriticalSection / Core::SafeSyncType<> |
std::thread / pthread_create |
Core::IWorkerPool::Submit() + Core::IDispatch for pooled async work; Core::Thread (subclass, override Worker()) when an exclusive dedicated thread is required |
std::shared_ptr / std::unique_ptr |
Core::ProxyType<T> for ref-counted COM objects |
std::this_thread::sleep_for() |
Core::TimerType<T> |
std::condition_variable |
Core::Event (signalling) |
fopen / fclose |
Core::File |
assert() from <cassert> |
ASSERT() |
This ensures portability, integrates with Thunder's lifecycle (WorkerPool, ResourceMonitor), and avoids hidden allocations or exceptions.
I/O & Resource Readiness — IResource / ResourceMonitor
- All file descriptor readiness (sockets, pipes, doors, eventfds) must go through
ResourceMonitor. - Implement
Core::IResource: provideDescriptor(),Events()(returnsPOLLIN/POLLOUT/POLLERRbits), andHandle(events). - Register with
Core::ResourceMonitor::Instance().Register(*this)and unregister withUnregister(*this). - Never poll, busy-wait, or call
select()/epoll_wait()directly —ResourceMonitorowns the event loop.
Threading — WorkerPool / IWorkerPool / Thread
- Never create raw
std::threadorpthread_createincore/code intended for plugins. - Pooled async work: dispatch via
Core::IWorkerPool::Instance().Submit(job)wherejobis aCore::ProxyType<Core::IDispatch>. ImplementCore::IDispatch(singleDispatch()method). - Dedicated exclusive thread: subclass
Core::Threadand overrideWorker()— use this only when a long-running loop requires its own thread (e.g. a blocking I/O reader).Core::Threadintegrates with Thunder's lifecycle and signal handling. Core::TimerType<T>schedules timed callbacks — never usesleep(),usleep(), orstd::this_thread::sleep_for().
Synchronization — Sync.h
- Use
Core::CriticalSectionfor mutual exclusion (wrapspthread_mutex_trecursively on Linux). - Use
Core::BinarySemaphore/Core::CountingSemaphorefor signalling. - Use
Core::Eventfor one-shot or auto-reset wait/signal. Core::SafeSyncType<LOCK>provides an RAII lock guard — prefer it over manualLock()/Unlock().- Never hold a
CriticalSectionacross a blocking call or any call that acquires another lock — document lock order when nesting is unavoidable.
JSON — JSON.h / JsonObject.h
- All structured data uses
Core::JSON::Containersubclasses — never hand-parse JSON strings. - Scalar types:
Core::JSON::DecUInt32,Core::JSON::String,Core::JSON::Boolean,Core::JSON::EnumType<E>,Core::JSON::ArrayType<T>. - Register fields in the constructor with
Add(_T("key"), &Member). - Deserialize with
.FromString(str)/.ToString(out).
Smart Pointers — Proxy.h
Core::ProxyType<T>: ref-counted shared ownership. Obtain viaCore::ProxyType<T>::Create(args...).Core::ProxyObject<T>: the heap-allocated ref-counted wrapper — do not use directly; accessed throughProxyType.- Raw
IUnknown*pointers returned byQueryInterface()carry a ref — caller must callRelease(). - Never
deleteaCore::IUnknownsubclass directly — alwaysRelease().
Error Handling
- No exceptions (
-fno-exceptions). Neverthrow. Signal errors viauint32_t/Core::hresultreturn codes. - Use
Core::ERROR_NONE(== 0) for success. ASSERT(condition)for developer invariants — compiled out in release. Never useassert()from<cassert>.TRACE_L1(fmt, ...)for lightweight diagnostic prints (compiled out inMinSizeRel).
Platform Portability
- Use
stringfromPortability.h(notstd::stringdirectly) where the code must be wide-char-safe on Windows. - Use
_T("literal")for string literals passed to Thunder APIs. - Integer widths: prefer
uint8_t,uint16_t,uint32_t,uint64_t— neverintfor protocol fields. - File paths: use
Core::FileSystemhelpers, never hardcode separators.
Platform Preprocessor Guards
- Use
#ifdef __LINUX__for Linux-specific code (includes Android). - Use
#ifdef __POSIX__for POSIX-common code. - Use
#ifdef __WINDOWS__for Windows-specific code. - Use
#ifdef __UNIX__for general Unix (POSIX + others). - Always provide an
#elseor#errorfor unsupported platforms in new platform-abstraction code.
Network Info (NetworkInfo.h / NetworkInfo.cpp)
AdapterIterator provides a cross-platform abstraction over network interface enumeration.
Linux Implementation
- Uses
netlinksockets (AF_NETLINK,NETLINK_ROUTE) for interface enumeration. - Reads
/proc/net/if_inet6for IPv6 addresses. - Sends
RTM_GETADDR/RTM_GETLINKrequests to query interface state.
DHCPClient
- Uses raw sockets (
AF_PACKET) for DHCP discovery.
NodeId (NodeId.h / NodeId.cpp)
Core::NodeIdwraps socket addresses (IPv4, IPv6, Unix domain, Netlink).getaddrinfo()is used for hostname resolution — handleEAI_NONAMEandEAI_AGAINas non-fatal (especially for numeric-only addresses).- Unix domain socket paths: max length is
sizeof(sockaddr_un::sun_path) - 1(108 bytes on Linux).
IPC Message System (IPCConnector.h)
The IPC framework is built on a type-erased message hierarchy:
Core Types
| Type | Location | Role |
|---|---|---|
IIPC |
IPCConnector.h:251 |
Abstract interface: Label(), IParameters(), IResponse() |
IReferenceCounted |
IPCConnector.h |
Ref-counting mixin: AddRef(), Release() |
IPCMessageType<ID, P, R> |
IPCConnector.h:266 |
Template: inherits BOTH IIPC and IReferenceCounted |
IIPCServer |
IPCConnector.h:259 |
Handler: Procedure(IPCChannel&, ProxyType<IIPC>&) |
IPCMessageType<ID, PARAMETERS, RESPONSE>
IDis a compile-timeuint32_tdiscriminator — returned byLabel()and::Id().PARAMETERSandRESPONSEare serializable message body types.- Inner classes
RawSerializedType<PACKAGE, REALIDENTIFIER>implementIMessagefor each direction. - Reference counting is forwarded from the inner parameter/response objects to the parent
IPCMessageType.
IPCChannel Message Routing
IPCChannel::Register(label, handler)associates a label ID with anIIPCServerhandler.IPCChannel::Unregister(label)removes the handler.- Incoming frames are deserialized, matched by label, and dispatched to the registered handler.
- The handler receives
Core::ProxyType<Core::IIPC>&— a type-erased smart pointer to the message. - Critical: never convert
ProxyType<IIPC>to a specificProxyType<IPCMessageType<...>>viadynamic_cast— useLabel()to identify the message type andstatic_castto convert (seecom.instructions.md).dynamic_caston template specialisations is not reliable across dylib boundaries.
ResourceMonitor Details
- Singleton:
Core::ResourceMonitor::Instance(). - Uses
poll()on POSIX (orepollon Linux when available). - Registered resources must unregister before destruction — failure to unregister causes use-after-free.
- Unregister timing:
Unregister()may be called from theHandle()callback itself (re-entrant safe), but the resource must not be destroyed untilUnregister()returns.
New File Checklist
- Include guard for new headers: prefer
#pragma once(consistent withconstraints.md); existing files with classic#ifndefguards may keep them. - License header (Apache 2.0, Metrological copyright) at top.
EXTERNALmacro on any class/function exported from the shared library.- Export the new header from
core/core.hif it is part of the public API.
Dependency Inversion Rule
Source/core/ is a provider of abstractions — it must never depend on upper layers. Concretely:
core/defines and implementsWorkerPool,ResourceMonitor,IWorkerPool,IResource,IDispatch— these are fully self-contained inSource/core/with no dependency oncom/,plugins/, orThunder/.- Upper layers (
com/,plugins/,Thunder/) consume these abstractions; they never provide implementations back intocore/. - If new
core/behaviour would require a dependency on a COM or plugin type, define an interface incore/and inject the concrete implementation fromThunder/orcom/at runtime — never pull upper-layer headers intocore/.
Singleton Pattern in core/
Core::Singleton<T> provides a process-lifetime singleton with explicit disposal:
// Access:
T& instance = Core::Singleton<T>::Instance();
// Disposal (must be called at process exit to ensure proper shutdown order):
Core::Singleton::Dispose();
- Use singletons only for process-lifetime services:
WorkerPool,ResourceMonitor,Administrator. - Do not use
Core::Singleton<T>for objects that have a meaningful lifetime shorter than the process. - Prefer passing instances explicitly over using singletons in new code where possible.
Core::Singleton::Dispose()must be called inmain()after all other cleanup — it tears down all registered singletons in reverse construction order.
FileSystem, File, Directory Utilities
- Use
Core::Filefor file operations — neverfopen/fclosedirectly. - Use
Core::Directoryfor directory traversal. - Use
Core::FileSystem::PathName()/Core::FileSystem::FileName()for path manipulation — neverstrchr/strrchron path strings. Core::FileObserverregisters for file change notifications viaIResource— use it for hot-reload patterns.
DataElement and DataElementFile
Core::DataElementis a reference-counted view over a raw byte buffer — used for zero-copy IPC payload passing.Core::DataElementFilememory-maps a file as aDataElement.- Never copy large binary payloads — pass
Core::DataElementorCore::ProxyType<Core::DataElement>instead.
Cross-Reference
- For IPC message type safety (
Label()+static_castpattern): seecom.instructions.md. - For
NodeIdandNetworkInfousage from the plugin layer: seecom.instructions.md(Communicator socket configuration).