C++ 바인딩 최종 구조¶
이 장이 정의하는 것 — C++ 라이브러리의
Contracts/Runtime형태와 필수 의미 범위.
이 문서는 C++ 라이브러리의 공개 계약과 소유 구조를 정의한다.
모든 메서드를 빠짐없이 열거하지 않는다. 구체적인 공개 계약은
bindings/cpp/include/zlink/Contracts/에 있다.
Contracts/, 설치되는 헤더 투영, 테스트, 샘플, perf runner와 runtime
동작이 모두 core/include/zlink.h의 안정적인 코어 기능을 C++ 관용 타입으로 매핑한다.
이 README는 ../README.md의 공통 정책을 C++ binding에 적용하는 방법을 정의한다.
이 바인딩은 공통 바인딩 아키텍처 지도를 C++ 네이밍으로 따른다. Contracts/는 설치되는
공개 헤더를 소유하고, Runtime/은 src/ 아래 비공개 구현을 소유한다. 폴더 이름은 저장소
구성을 위한 것이지 사용자가 의존해야 하는 네임스페이스 분절이 아니다.
| 절 | 다루는 내용 |
|---|---|
| 공개 계약 소스 | Contracts/Runtime 소스 위치, 언어 기준, 비동기 표면 정책 링크 |
| 저장소 레이아웃 | 정렬된 디렉터리 트리와 파일 세분화 정책 |
| .NET 계약 카테고리 투영 | .NET 레이아웃을 C++ 네이밍으로 투영하는 방법 |
| 공개 계약 한눈에 보기 | 영역 → 공개 객체 → 소유 헤더 표, 구체 파사드 예시 |
| 코어 기능 소유 규칙 | 새 기능을 추가할 때 따르는 절차 |
| 라이브러리 형태 | RAII, snake_case, Pimpl, 빌더 필수화 규칙 |
| 계약/런타임 배치 규칙 | 공개 선언과 런타임 헬퍼의 경계 |
| 빌드 및 패키징 정책 | zlink_cpp 타깃 빌드·링크·설치 규칙 |
| 계약 폴더 레이아웃 | Contracts/ 하위 카테고리별 소유 범위 |
| 표준 인터페이스 규칙 | recv 시그니처, 빌더, handler 이름 규칙 |
| 64-bit byte HWM과 monitoring 계약 | byte_count_t 표현과 monitor snapshot field |
| Receive flow state | receive-flow 상태 타입, setter와 monitor 표면 |
| 기능 범위 | 완성된 공개 헤더가 다루는 그룹 |
| 수명과 ownership | 리소스 클래스 해제·move·수신 저장소 규칙 |
| 에러와 result 정책 | 실패 표현과 result 도메인 |
| 성능 정책 | hot path·링크 대상 제약 |
| 완성 구조 요구사항 | 완성 선언 전 확인 항목 |
| Actor와 Spot 라우트 결과 | Framework 공개 계약 링크 |
공개 계약 소스¶
- 공개 계약:
bindings/cpp/include/zlink/Contracts/. - 런타임 구현:
bindings/cpp/src/Runtime/. - 공개 진입점 투영:
bindings/cpp/include/zlink.hpp. - 설치되는 투영:
bindings/cpp/include/zlink.hpp및bindings/cpp/include/zlink/Contracts/.... - 컴파일된 라이브러리: C++ 바인딩은 코어 네이티브
zlink라이브러리와 별도로zlink_cpp같은 C++ 라이브러리 타깃을 빌드하고 설치한다. - 언어 기준: C++20.
- 네임스페이스: raw binding 공개 타입은
zlink아래에 둔다. - 내부 구현: 네이티브 브리지 헬퍼, 콜백 트램펄린, 요청 진행 헬퍼, 비공개
detail헬퍼, 비공개 구현 헤더,.cpp파일은bindings/cpp/src/Runtime/아래에 둔다. -
문서의 역할: 이 README는 형태, 경계, 필수 의미 범위를 정의한다. 정확한 멤버 목록은
Contracts/가 소유하며, 설치되는 헤더는 이를 의도적으로 투영한다. -
C++는 더 이상 header-only 바인딩으로 모델링하지 않는다.
- 두 번째
bindings/cpp/src/zlink/Contracts/트리를 만들지 않는다. - 공개 계약은 설치되는 헤더에 남고, 구현은
.cpp파일과 비공개 런타임 헤더 뒤로 옮긴다. 계약/런타임 분리는 그대로다. Contracts/는 사용자 표면을 선언하고,src/Runtime/은 그 표면을 위한 구현 지원을 담는다.- Java나 .NET의 인터페이스 중심 레이아웃을 C++에 그대로 복사하지 않는다.
-
C++는 설치되는 헤더, RAII 클래스, 구체 값, 불투명 구현 상태를 자연스러운 경계로 사용한다.
-
C++20은 bindings 라이브러리의 최소 지원 범위다.
- bindings 라이브러리는 직접
co_await하는 move-onlyasync_result_t<T>를 제공하지만, coroutine executor, framework handler executor, framework dispatcher를 소유하지 않는다. - framework coroutine은 같은 awaitable을 직접 기다리며 optional promise hook으로 serial turn과 ambient context의 continuation handoff만 제공한다.
- 언어별 비동기 실행 표면 기준은 바인딩 비동기 실행 표면 정책을 따른다.
저장소 레이아웃¶
완성된 C++ 바인딩은 다음 경로를 일관되게 사용한다. 아래 파일 이름은 목표 소유 지도이며, 공개 개념이나 런타임 책임이 독립적으로 변할 이유가 있을 때만 카테고리를 더 쪼갠다.
bindings/cpp/
+-- CMakeLists.txt
+-- include/
| +-- zlink.hpp
| +-- zlink/
| +-- Contracts/
| | +-- Core/
| | | +-- capability.hpp
| | | +-- context.hpp
| | | +-- context_options.hpp
| | | +-- routing_id.hpp
| | | +-- utilities.hpp
| | +-- Messaging/
| | | +-- message.hpp
| | | +-- received.hpp
| | | +-- topic_message.hpp
| | | +-- subscription_event.hpp
| | | +-- operation_contracts.hpp
| | | +-- request_result.hpp
| | +-- Sockets/
| | | +-- socket_contracts.hpp
| | | +-- message_socket_contracts.hpp
| | | +-- routed_socket_contracts.hpp
| | | +-- pubsub_socket_contracts.hpp
| | | +-- stream_socket.hpp
| | | +-- socket_options.hpp
| | | +-- results.hpp
| | +-- Eventing/
| | | +-- monitor.hpp
| | | +-- poller.hpp
| | | +-- poll_event.hpp
| | | +-- timers.hpp
| | | +-- events.hpp
| | | +-- status.hpp
| | +-- Errors/
| | +-- errors.hpp
| | +-- results.hpp
+-- src/
| +-- Runtime/
| +-- zlink_cpp.cpp
| +-- Core/
| | +-- capability.cpp
| | +-- context.cpp
| | +-- utilities.cpp
| | +-- operation_detail.hpp
| | +-- runtime_helpers.hpp
| | +-- types_impl.hpp
| +-- Messaging/
| | +-- message.cpp
| +-- Errors/
| | +-- error.cpp
| +-- Eventing/
| | +-- monitor.cpp
| | +-- poller.cpp
| | +-- timers.cpp
| +-- Sockets/
| | +-- base_socket.cpp
| | +-- pair.cpp
| | +-- dealer.cpp
| | +-- pubsub.cpp
| | +-- router.cpp
| | +-- stream.cpp
| | +-- detail.hpp
| +-- Options/
| | +-- socket_options.cpp
| +-- Native/
| +-- socket_handle.hpp
| +-- native_message_parts.hpp
| +-- native_parts.hpp
| +-- native_options.hpp
| +-- native_send_result.hpp
+-- native/
+-- samples/
+-- tests/
+-- perf/
CMakeLists.txt는 컴파일된 C++ 바인딩 타깃(예: zlink_cpp)을 정의하고 코어 네이티브
zlink 라이브러리에 링크한다. 샘플, 테스트, perf 바이너리, 애플리케이션은 비공개 런타임
소스를 직접 컴파일하지 않고 이 타깃에 링크한다.
Contracts/는 bindings/cpp/include/zlink/ 아래에 설치되는 공개 계약 표면이다.
Runtime/은 bindings/cpp/src/Runtime/ 아래의 비공개 구현 지원이다. zlink
네임스페이스와 zlink.hpp는 그 계약을 C++로 투영한 결과다. Contracts나 Runtime을
네임스페이스 분절로 노출하지 않는다.
런타임 헬퍼 헤더는 공개 계약 API가 아니다. 공개 샘플, perf, 테스트는 <zlink.hpp>를
include하고 C++ 바인딩 라이브러리에 링크한다. 런타임 헬퍼 경로는 include하지 않는다.
include/zlink/message.hpp,
include/zlink/sockets/dealer.hpp 같은 래퍼 헤더는 완성된 레이아웃의 일부가 아니다.
완성된 트리는 이들을 포워딩 헤더로 대체하지도 않는다.
monitor, poller, timer 계약은 공통 Eventing/ 카테고리 아래에 둔다. Contracts/Monitoring/
은 완성된 공개 계약의 일부가 아니며, 완성된 트리는 Monitoring/ 포워딩 헤더를 유지하지
않는다.
파일 단위 세분화는 ../README.md의 공통 정책을 따른다. 독립적인 공개 개념 하나, 또는
긴밀한 operation/모델 묶음 하나당 파일 하나를 둔다. 아주 작은 marker, delegate, enum,
pass-through 헬퍼 파일은 공개 형태가 더 잘 읽힐 때 인접한 계약 파일에 합친다.
.NET 계약 카테고리 투영¶
C++ 바인딩은 .NET 공개 계약 카테고리 레이아웃을 분류 표준으로 사용한다. 이는 카테고리와
책임의 투영이지 C# 형태를 복사한 것이 아니다. C++은 C++20 명명 규칙, 헤더, RAII facade,
이동 의미론, 구체 값 타입을 유지한다.
.NET의 파일 목록을 이 문서에 그대로 옮기지 않는다. .NET의 단일 기준은
.NET 바인딩 청사진, 특히 Contract Folder Layout과 Runtime Folder
Layout 섹션이다. 이 C++ README는 그 카테고리의 C++ 투영만 정의한다.
카테고리 소유 규칙은 엄격하다. 런타임 구현이 다른 위치에 두기 쉽다는 이유로 공개 C++ 타입이
다른 카테고리로 이동하지 않는다. 런타임 헬퍼 코드는 src/Runtime/ 아래에서 더 세분화될 수
있지만, 공개 계약 소유자는 해당 카테고리에 그대로 남는다.
- 이 투영은 C# 인터페이스 스타일을 엄격하게 따르지 않는다.
.NET의 socket role 인터페이스는 공개 계약에서 socket role을 식별한다. C++가 기본적으로isocket_t,istream_socket_t,ISocket,IStreamSocket을 노출하도록 요구하지 않는다.- 사용자가 진짜 substitutable한 동작을 필요로 하지 않는 한 구체 RAII facade를 사용한다.
- substitutable한 role이 필요하면 인터페이스를 좁게 유지하고, send/receive/poll/dispatch 핫패스에서 회피 가능한 virtual dispatch가 없도록 한다.
공개 계약 한눈에 보기¶
완성된 C++ 바인딩은 인터페이스 전용 계층을 추가하지 않고도 공개 계약을 보이도록 만든다.
사용자는 <zlink.hpp>에서 출발해 다음 지도로 소유 계약 헤더를 찾는다.
.NET 기준의 원본 세부사항은 .NET 바인딩 청사진에 두며,
이 문서에는 C++ 투영만 둔다.
| 영역 | 공개 객체와 역할 | 소유 계약 헤더 |
|---|---|---|
| Core | context_t, context 옵션, routing id, version/역할 헬퍼 |
Contracts/Core/ |
| Messaging | message_t, received_t, topic_message_t, subscription_event_t, multipart 헬퍼 |
Contracts/Messaging/ |
| Sockets | pair_socket_t, dealer_socket_t, router_socket_t, pub_socket_t, sub_socket_t, xpub_socket_t, xsub_socket_t, stream_socket_t, send/recv/request/reply 빌더 |
Contracts/Sockets/ |
| Eventing | socket_monitor_t, monitor 이벤트, poller, poll 이벤트, timer, readiness 헬퍼 |
Contracts/Eventing/ |
| Errors | 공개 예외와 result 도메인 타입 | Contracts/Errors/ |
poller_t는 void add(socket_monitor_t &monitor_, poll_event_flag_t events_, std::uintptr_t slot_),
void modify(socket_monitor_t &monitor_, poll_event_flag_t events_), bool remove(socket_monitor_t &monitor_)로 socket monitor를
source로 받는다(공통 spec "Poller의 monitor source"). monitor mask의 결과는 공통 spec "Poller의 monitor source"를 따른다.
ready 뒤 socket_monitor_t::recv(DONTWAIT)로 drain한다.
위 지도는 공개 API 색인이다. 계약 표면 개요의 C++ 등가물이며, IContext 같은 추상 인터페이스를 의미하지 않는다. 공개 리소스 객체는 호출자가 진정한 대체
동작을 필요로 하지 않는 한 구체 RAII 파사드로 유지한다. 좁은 인터페이스는 codec, callback,
handler, poll target처럼 사용자가 자연스럽게 교체하는 역할에만 허용한다.
공개 계약은 두 단계로 읽는다.
<zlink.hpp>에서 시작해 C++ 바인딩이 포함하는 공개 계약 카테고리를 본다.- 해당
Contracts/...헤더를 열어 구체 공개 타입과 그 공개 멤버 목록을 살핀다.
공개 Spot과 Actor facade 형태는 Framework API가 규정한다.
zlink.hpp는 이 파사드들의 공개 목차 역할을 한다.
#include "zlink/Contracts/Core/capability.hpp"
#include "zlink/Contracts/Core/context.hpp"
#include "zlink/Contracts/Core/context_options.hpp"
#include "zlink/Contracts/Core/routing_id.hpp"
#include "zlink/Contracts/Messaging/message.hpp"
#include "zlink/Contracts/Messaging/received.hpp"
#include "zlink/Contracts/Messaging/topic_message.hpp"
#include "zlink/Contracts/Messaging/subscription_event.hpp"
#include "zlink/Contracts/Messaging/operation_contracts.hpp"
#include "zlink/Contracts/Sockets/message_socket_contracts.hpp"
#include "zlink/Contracts/Sockets/routed_socket_contracts.hpp"
#include "zlink/Contracts/Sockets/pubsub_socket_contracts.hpp"
#include "zlink/Contracts/Eventing/poll_event.hpp"
#include "zlink/Contracts/Eventing/poller.hpp"
#include "zlink/Contracts/Errors/errors.hpp"
런타임 세부는 raw socket facade 뒤에 두고, 공개 헤더에 native handle, callback trampoline, whole-message array, request pump, marshalling helper를 노출하지 않는다.
코어 기능 소유 규칙¶
C++가 노출하는 모든 안정 코어 기능은 다음 소유 규칙을 따른다.
- 올바른
bindings/cpp/include/zlink/Contracts/카테고리에 공개 타입 또는 메서드를 추가한다. bindings/cpp/include/zlink.hpp와 의도적으로 설치하는 투영 헤더를 갱신한다.- C++ 도메인 소유자를 결정한다: context, message, socket, monitor, timer, error, option 중 하나.
- raw C 핸들 접근, whole-message 배열 marshalling, callback userdata, 트램펄린 상태, 네이티브 marshalling
헬퍼는
src/Runtime/헤더와.cpp파일에 둔다. - 새로운 기능이 사용자 워크플로 또는 측정에 영향을 줄 때는 공개 헤더 테스트와 최소 하나의 샘플/perf 갱신을 추가한다.
- 새로운 공개 API가 얕은 C 래퍼에 머무르지 않는지 확인한다. ownership, 검증, 형태를 개선하지 않고 단순히 위임만 한다면 내부로 유지한다.
- 사용하지 않게 된 공개 이름은 남기지 않는다. 별칭 deprecated, 포워딩 오버로드, 대체 공개 헤더는 이후 문서가 이 C++ 정책을 명시적으로 바꾸지 않는 한 두지 않는다.
Spot GetOrCreate의 공개 계약은
Framework API가 소유한다.
라이브러리 형태¶
C++ 바인딩은 코어 C 계약 위에 얹힌 작은 네이티브 C++ 라이브러리처럼 느껴진다.
- 공개 리소스 객체는 문서화된 수명에 따라 네이티브 핸들을 소유하거나 빌려 사용하는 RAII 클래스다.
- 소멸자는 호출자가 네이티브 close 순서를 몰라도 리소스를 해제한다. 리소스 소멸자와 그
외 단순하지 않은 메서드는
.cpp파일에서 out-of-line으로 정의한다. - 작은 값 타입 연산은 네이티브 ownership, callback 상태, request 상태, marshalling 세부를 노출하지 않을 때 inline으로 남겨도 된다.
- 공개 메서드는
snake_case를 사용한다. - message, routing id, received metadata, topic message, result, error, enum, option 같은 공개 값 타입은 구체로 유지한다.
- 공개 리소스 헤더는 네이티브 핸들 레이아웃, callback 상태, request 상태, ABI 민감 저장소가 계약에 새어 나가는 것을 막기 위해 Pimpl 등 불투명 구현 상태를 사용한다.
- 템플릿, 오버로드, move 의미는 호출자 ownership을 단순화하거나 복사를 피할 때만 사용한다. 명확한 도메인 타입의 대체로 템플릿 기계장치를 노출하지 않는다.
- 가상 인터페이스는 호출자가 대체 동작을 필요로 할 때만 사용한다. 기본적으로 모든 핸들을 추상 인터페이스로 감싸지 않는다.
- Core raw multipart send, publish, request, reply operation에는 빌더를 필수로 한다. 이렇게 해야 네이티브 request 상태가 숨고 ownership이 분명해진다.
계약/런타임 배치 규칙¶
- 공개 선언과 사용자에게 보이는 동작은
Contracts/에 둔다. - 공개 free function, static 헬퍼, extension 스타일 헬퍼, 빌더 편의 헬퍼는 사용자가 직접
호출할 수 있을 때
Contracts/에 둔다. - 런타임 핸들 소유자, socket 커널, request 펌프, callback 트램펄린, part 루프 헬퍼는
src/Runtime/에 둔다. - FFI 선언, raw C 핸들, 네이티브 struct mirror, marshalling 헬퍼, 플랫폼 로딩 코드는
src/Runtime/Native/에 둔다. zlink.hpp는Contracts/를 투영한다.Runtime/헬퍼 경로를 공개 include 스타일로 만들지 않는다.- 계약 헤더는 비공개 런타임 헤더를 include하지 않는다. 공개 클래스가 구현 상태를
필요로 하면 불완전한
impl타입이나 다른 불투명 비공개 멤버만 노출하고 동작은.cpp에서 정의한다. - 런타임 구체 클래스는 사용자 진입점이 아니다. 공개 동작이라면
Contracts/가 선언하고Runtime/이 구현한다.
빌드 및 패키징 정책¶
C++가 header-only를 벗어나면 바인딩은 컴파일된 산출물을 하나 더 가진다. 따라서 완성된 바인딩은 다음 빌드 규칙을 유지한다.
- C++ 바인딩은
zlink_cpp라이브러리 타깃을 빌드한다. zlink_cpp는 코어 네이티브zlink라이브러리에 링크되며 코어 라이브러리와 버전 호환성 규칙을 가진다.- Linux, macOS, Windows 패키지는 지원되는 아키텍처와 런타임 툴체인마다 C++ 라이브러리를 빌드한다.
- CMake install/export 메타데이터는 애플리케이션이 공개 헤더와 컴파일된 C++ 바인딩 타깃을 함께 소비할 수 있게 한다.
- 샘플, 테스트, perf 러너는 애플리케이션이 사용하는 동일한 설치 스타일 C++ 타깃에 링크한다. 비공개 런타임 소스 경로에 의존하지 않는다.
- 런타임 검색 경로, DLL 조회 규칙, 패키징된 네이티브 산출물은 테스트한다. 이제 애플리케이션은 코어 네이티브 라이브러리와 C++ 바인딩 라이브러리를 함께 로드하기 때문이다.
- 공개 헤더는 ABI 민감 구현 저장소 노출을 피한다. 공개 메서드 시그니처는 C++ 관용을 유지해도 되지만, 네이티브 핸들 레이아웃, callback 상태, request 상태, marshalling 버퍼는 설치되는 헤더 밖에 둔다.
계약 폴더 레이아웃¶
Contracts/는 공개 C++ 선언의 소스 소유 지도다. zlink.hpp는 이 카테고리들을 zlink
네임스페이스로 투영한다.
Core/: context, context 옵션, routing id, utility 리소스, 그리고 version 또는 역할 헬퍼 같은 공개 free function.Messaging/: message, received metadata, topic message, subscription event, stream packet 값, 빌더 payload 헬퍼. Codec 배포 범위는 공통 raw payload 정책을 따른다.Sockets/: socket 동작, socket family, 타입 지정 옵션, request/reply, publish/subscribe 표면.Eventing/: monitor, monitor snapshot/event, poller, poll event, timer, 공개 poll 헬퍼.Errors/: 예외 또는 타입 지정 error-result 도메인.- enum, flag, result 타입은 의미를 정의하는 카테고리 안에 둔다. 문법별로 묶기 위한
Enums/폴더를 만들지 않는다.
표준 인터페이스 규칙¶
- data-plane
recv, routed recv, subscribe, subscription-event 수신은 호출자가 제공하는 출력 저장소(예:received_t&,topic_message_t&,subscription_event_t&)를 사용한다. message_t::from(...)은 호출자가 넘긴 바이트를 독립적으로 복사한다. 호출자가 소유한 버퍼를 복사 없이 메시지로 넘겨야 할 때는 고급 API인external_message_t::from(span, free_fn, hint)오버로드를 사용한다. 이 오버로드는 버퍼를 메시지에 맡기고, 메시지가 버퍼를 해제할 때free_fn(data, hint)를 한 번 호출한다.- Raw send, routed send, publish, request, reply는 move-only fluent builder를 반환한다.
- 빌더 시작 메서드는 대상 identity, topic, routing ID와
reply_token_t만 받는다. payload, flag, timeout, async submit 선택은 빌더 단계에서 한다. - operation 시작 메서드와 같은 이름의 단일 payload 단축 오버로드를 추가하지 않는다.
send(message),send(routing_id, message),publish(topic, message), 는 공개 계약 멤버가 아니다. 호출자는send(...).message(message).submit()을 사용한다. - multipart payload는
message(...)를 반복 호출해 쌓는다.messages(...)편의 메서드는 동일한 빌더 계약에 위임하고Contracts/에 선언될 때만 허용한다. - Dealer socket은
request_frame(...)이나reply(request_token, parts)같은 프로토콜 envelope 헬퍼를 노출하지 않는다. Dealer는request()로 request를 시작할 수 있지만 API 수준 peer routing id가 없으므로 임의 token에 reply할 수 없다. send_no_wait,publish_with_flags,request_async같은 operation 시작 오버로드 계열을 추가하지 않는다. operation 이름은 하나로 유지하고 변형은 빌더가 흡수한다. 종단 빌더 메서드의 언어별 이름은 바인딩 비동기 실행 표면 정책을 따른다.- Send의 blocking
submit()은 CoreNONEadmission을 사용하고async()는 CoreDONTWAITcompletion을 기다린다. SocketSNDTIMEO는 blocking admission wait의 상한이다. Binding은 payload 재전송 queue를 만들지 않는다. - C++ binding은 outbound 경로에 자체 lock이나 gate를 두지 않는다. Builder가 모은 모든 part를 native 배열로 만든 뒤 Core whole-message API를 한 번 호출한다. Core는 배열 전체를 하나의 record로 원자적으로 제출하고 모든 슬롯을 소비하지만, binding의 별도 native view가 공개 C++ message를 보존한다. 여러 thread의 독립된 제출은 Core가 처리하며 binding은 직렬화하거나 대기하거나 재시도하지 않는다. close와 in-flight 제출의 경합도 Core lifecycle gate가 담당한다.
- Request는 blocking
submit()과async()를 제공하고 builder의 reply timeout을 유지한다. Target은 operation 생성 때 capture하며 physical connection identity를 public target으로 사용하지 않는다. - request timeout은 Core 소유다(
ZLINK_REQUEST_TIMED_OUT). builder의timeout(...)은 그 Core-owned reply deadline을 지정한다. 제출 실패는submit_error_t로 던지고, 수용 후에는 Core reply lifecycle이 완료를 소유한다. Awaitable drop은 waiter만 detach하고 late completion은 runtime drain이 정리한다. publish_operation_t(PUB/XPUB)의 terminal은 synchronoussubmit() -> bool하나다. PUB/XPUB는 lossy publish semantics를 가지므로 publish builder에는async()가 없다.ZLINK_PUB_OPT_NODROP의 backpressure는 동기 submit에서만 표면화한다.- Raw ROUTER/
received_treply의 terminal은reply_submit_operation_t::submit() -> void인 동기 one-shot이다. Terminal reply와 error reply를 native 호출 한 번으로 제출한다. DEALER peer에는 Application HWM(queue의 byte 보관량을 제한하는 기준)·PAUSED와SNDTIMEO를 적용하여BACKPRESSURED가 될 수 있고, ROUTER peer에는 HWM 없는 Completion connection을 사용한다.NOT_CONNECTED,TERMINATED,INVALID_ARGUMENT와 그 밖의 submit 실패는 즉시submit_error_t로 전달한다.
64-bit byte HWM과 monitoring 계약¶
- Socket HWM과 context Core HWM memory limit·budget은
byte_count_t로 표현한다. - 이 값 타입은
uint64_tbyte만 보관하며bytes(...)생성 함수와bytes()조회 함수로 단위를 드러낸다. - 이전
message_count_t는 alias나 adapter로 유지하지 않는다. 0은 HWM에서 무제한을 뜻하며, 수동 기본값은4,096,000 bytes다.
auto options = socket.options ();
options.send_hwm (zlink::byte_count_t::bytes (send_limit)); // Send pipe의 byte HWM을 정한다.
options.recv_hwm (zlink::byte_count_t::bytes (0)); // 0은 무제한 receive HWM이다.
auto context_options = context.options ();
context_options.core_hwm_memory_limit_bytes (
zlink::byte_count_t::bytes (memory_limit));
context_options.core_hwm_budget_bytes (
zlink::byte_count_t::bytes (core_budget));
context_options.core_hwm_profile (zlink::auto_hwm_profile::balanced);
const auto snapshot = context.core_hwm_budget_snapshot ();
context.reset_core_hwm_budget_metrics ();
core_hwm_memory_limit_bytes(...)와 core_hwm_budget_bytes(...)의 0은 각각
명시 입력과 수동 Core budget이 없다는 뜻이다. Binding은 profile 비율, connection 수 또는
queue별 HWM을 계산하지 않고 exact uint64_t 값을 Core context option으로 전달한다.
Core가 감지한 finite hard limit보다 명시 입력이
크면 EINVAL을 그대로 전달하고 clamp하지 않는다.
core_hwm_budget_snapshot_t는 Core ABI v1 필드와 flag를 단위 변환 없이 투영하며
core_hwm_budget_snapshot()이 ABI version과 struct size 초기화를 소유한다. 사용자가
send_hwm(...) 또는 recv_hwm(...)을 호출한 방향은 기존처럼 수동 override다.
Snapshot은 configured/resolved memory limit, configured/effective budget,
planned/applied/manual-reserved HWM, Core queue/application/current/peak/provisional accounted
byte, completion current/peak/pending과 total messaging byte, monitor/instance aggregate,
application/completion queue count, outstanding_application_lease_count, retired_queue_count,
deferred_origin_credit_bytes, oversize·blocked·aggregate flag, budget_generation과
measurement_epoch을 빠짐없이 노출한다. application_accounted_bytes와 위 세
owner-lifecycle 필드는 ABI 예약 필드이며 항상 0이다. Metrics reset은
current·pending·queue count를 유지하고 budgeted/completion peak를 각 current로
재기준화하며 epoch counter를 0으로 만든 뒤 measurement_epoch을 증가시킨다.
계산·수동 override·admission은 Core HWM 계약을 따른다.
0 bytes는 무제한이며 message 한 건을 허용한다는 뜻이 아니다.
socket.monitor_open(events, monitor_hwm_bytes)와
socket_monitor_t::open(socket, events, monitor_hwm_bytes)는 byte_count_t를 받는다.
0은 Core monitor 기본값을 선택하고, 양수는 정확한 monitor queue byte HWM으로
변환 없이 전달한다. Message-count overload나 alias는 없다.
recv(received_t&), subscribe(topic_message_t&)의
출력 객체는 C++ copy·close()·소멸자로 part와 routing/topic/request metadata의 수명을 관리한다.
수신 회계와 결과 수명의 경계는 공통 수신 ownership 계약을 따른다.
Legacy auto_hwm_msg_unit_bytes, slot·size-cap·connection-bucket planner property는 alias 없이
제거한다. Monitor snapshot은 Core monitoring ABI v4의 byte pending field를 투영하고,
pending message count와 snd_pending_bytes·rcv_pending_bytes를 별도 값으로 유지하며,
context-wide budget·accounting·queue count는 core_hwm_budget_snapshot_t에서 조회한다.
Receive flow state¶
zlink::receive_flow_state_t는 int 기반 enum class이며 running = 0, paused = 1이다.
Setter는 void socket_t::set_receive_flow_state(receive_flow_state_t)이며 실패한
native result를 담은 config_error_t를 던진다.
상태·결과·monitor 투영은 공통 receive-flow 계약을 따른다.
기능 범위¶
완성된 C++ 바인딩의 공개 헤더는 다음 그룹을 다룬다.
- Core: context, version/역할 헬퍼, context 옵션, shutdown, 자동 HWM 재계산,
atomic_counter_t,stopwatch_t,thread_t. - Messaging: message ownership, 빌더 multipart 입력, received metadata, topic message, subscription event, routing id, callback 타입.
- Socket family: pair, dealer, router, pub, sub, xpub, xsub, stream, 공통 옵션, 타입 지정 socket 옵션, bind/connect/disconnect, TLS, callback, request/reply 표면.
- Eventing: socket monitor, monitor event, monitor snapshot, poller, one-shot
poll(...), poll event, timer, readiness flag. - Errors: 코어 result 도메인을 보존하는 타입 지정 예외 또는 error-result 표면.
C++ 표면은 raw 네이티브 핸들, whole-message 배열 marshalling, callback userdata, 내부 inproc endpoint, request 펌프 객체를 공개 개념으로 노출하지 않는다.
수명과 ownership¶
C++ 호출자는 C 핸들 정리를 추론하지 않아도 된다.
- 리소스 클래스는 소멸자에서 네이티브 핸들을 해제하고, close가 실패할 수 있을 때는 명시적
close또는 동등한 생명주기 메서드를 지원한다. - mutable 핸들을 공유 소유하는 대신 move-only 리소스 클래스를 선호한다.
- 메시지 값은 효율적인 move를 지원하고, 명시적 payload 공유·이전·복제는 공통 계약의
Copy/Move/Clone(각각 Czlink_msg_copy/zlink_msg_move및 deep copy)을 따른다. C++ 시그니처는message_t::copy()(공유, 새 값 반환) ·message_t::move(message_t&)(소유권 이전, 호출자 empty) ·message_t::clone()(독립 버퍼 깊은 복사)이며,move는 C API를 직접 감싼다(C++ move 시맨틱과 별개). 정의는 Message ownership 공통 계약 §"명시적 Copy / Move / Clone". - data-plane 수신과 subscribe 경로는 호출자가 제공하는 저장소를 사용한다.
- 수신 결과의 수명 API는 64-bit byte HWM과 monitoring 계약의 C++ 출력 객체 설명을 따른다.
- callback은 네이티브 callback 수명과 사용자 callable 수명을 내부에서 일관되게 유지한다.
에러와 result 정책¶
바인딩은 예외 또는 타입 지정 result 객체 중 무엇을 사용해도 되지만, 공개 형태는 코어 의미를 보존한다.
- data 없음과 일시적 backpressure는 강한 실패와 구분해 유지한다.
- request, submit, recv, bind, connect, config, handler, close 실패는 result 도메인 의미를 유지한다.
pollout은 send 복구 readiness 신호이며 일반적인 writable 비트가 아니다.- ROUTER/PUB 기본값은 코어 헤더를 따른다.
성능 정책¶
- multipart 값은 Core whole-message receive가 채운 배열에서 직접 만든다.
- hot path에서 불필요한 힙 할당, 회피 가능한 복사, reflection 같은 동적 dispatch, 숨겨진 대기, sleep, busy wait, 광범위한 lock, join을 피한다.
- perf와 샘플은 설치되는 공개 헤더만 include한다.
- perf와 샘플은 공개 C++ 바인딩 타깃에 링크한다. 비공개 런타임 object 파일이나 헬퍼 소스 디렉터리에 링크하지 않는다.
- C++ perf 의미는
bindings/c/perf와 일치한다. 같은 패턴 의미, 같은 transport 의미, 같은 클라이언트 수 정책을 따르며 비공개 fast path는 두지 않는다.
완성 구조 요구사항¶
완성된 C++ 바인딩은 다음 요구사항을 만족한다.
- 설치되는 헤더가 안정적인 사용자 대상 코어 기능을 모두 노출한다.
- C++ 바인딩은 공개 헤더 외에 컴파일된 C++ 라이브러리 타깃을 빌드하고 설치한다.
Contracts/Eventing/이 유일한 공개 eventing 카테고리다.Contracts/Monitoring/은 사라졌고,zlink.hpp는 Eventing 헤더를 include한다.- 옛 래퍼 include 경로는 사라졌다. 애플리케이션, 샘플, perf, 테스트는
<zlink.hpp>또는 의도적인Contracts/...헤더만 include한다. - 공개 헤더와 컴파일된 C++ 바인딩 타깃만으로 애플리케이션, perf, 샘플, framework 어댑터가 필요한 것을 모두 갖춘다.
- 사용자는 비공개 헬퍼 헤더와 비공개 런타임 소스 경로가 필요 없다.
- 추상화가 실제 복잡도를 줄이지 않는 한 값 타입은 구체로 남는다.
- 공개 API는 네이티브 part 루프, raw 핸들, callback userdata를 숨긴다.
- handler 등록은
set_..._handler이름을 사용하고, 공개on_...별칭은 두지 않는다. - 공개 헬퍼/free function과 빌더 편의 메서드는 런타임 헬퍼가 아니라
Contracts/에 선언한다. - perf 테스트는 C perf와 동일한 측정 의미를 사용한다.
Actor와 Spot 라우트 결과¶
공개 Spot/Actor 형태는 Framework API.
Pull completion 공개 계약¶
C++ package 정보는 배포 metadata를, Core ABI 버전은 Core release metadata를 따른다.
C++는 blocking submit()과 결과 객체(send_submission_t/request_submission_t: result와 admitted, request는 reply)를 돌려주는 async()를 제공한다.
완료 대기 객체의 수명 종료는 async_result_t drop으로 표현한다.
Native completion ID·user_context·raw drain은 public API에 노출하지 않는다.
제출 결과는 공통 결과 투영을, 완료 합류·수명과
poll_event_flag_t::pollcompletion의 진행 조건은 비동기 실행 모델을 따른다.
ROUTER REQUEST receive만 reply_token_t를 만든다. Token은 ROUTER wrapper가 만든 shared owner
tag와 opaque value를 함께 보유한다. Equality·hash와 reply owner 검증은 두 값을 사용한다.
Public numeric constructor, raw accessor, ordering, serialization과 close를 제공하지 않는다.
stream_packet_t는 move-only reusable output이며 recv 진입 때 이전 payload를 먼저 비운다.
같은 output의 concurrent recv는 invalid-state다. Header/body reference는 다음 recv 진입이나
close() 전까지만 유효하다. Receive mode setter는 첫 bind/connect 전에 raw·packet만 받고
unspecified를 거부한다.
Public interface¶
struct send_submission_t {
zlink_submit_result_t result; // OK | BACKPRESSURED, 제출 시점 스냅샷
async_result_t<void> admitted; // OK면 완료 상태
};
struct request_submission_t {
zlink_submit_result_t result;
async_result_t<void> admitted;
async_result_t<std::vector<message_t>> reply; // admitted 성공 뒤 완료
};
class send_submit_operation_t {
public:
send_submit_operation_t&& message(message_t&) &&;
send_submit_operation_t&& message(message_t&&) &&;
void submit() &&;
send_submission_t async() &&;
};
class request_submit_operation_t {
public:
request_submit_operation_t&& message(message_t&) &&;
request_submit_operation_t&& message(message_t&&) &&;
request_submit_operation_t&& timeout(std::chrono::milliseconds) &&;
std::vector<message_t> submit() &&;
request_submission_t async() &&;
};
class reply_token_t final {
public:
reply_token_t() = delete;
reply_token_t(const reply_token_t&) = default;
reply_token_t& operator=(const reply_token_t&) = default;
friend bool operator==(const reply_token_t&, const reply_token_t&) noexcept;
private:
reply_token_t(std::shared_ptr<const void> owner, uint64_t value) noexcept;
std::shared_ptr<const void> owner_;
uint64_t value_;
friend struct detail::received_access_t;
friend struct reply_token_hash_t;
};
struct reply_token_hash_t {
std::size_t operator()(const reply_token_t&) const noexcept;
};
class reply_submit_operation_t {
public:
reply_submit_operation_t&& message(message_t&) &&;
void submit() &&;
};
enum class stream_recv_mode_t : int {
unspecified = 0,
raw = 1,
packet = 2
};
class stream_packet_t final {
public:
stream_packet_t() = default;
~stream_packet_t();
stream_packet_t(stream_packet_t&&) noexcept = default;
stream_packet_t& operator=(stream_packet_t&&) noexcept = default;
stream_packet_t(const stream_packet_t&) = delete;
stream_packet_t& operator=(const stream_packet_t&) = delete;
bool empty() const noexcept;
const std::optional<routing_id_t>& routing_id() const noexcept;
message_t& header();
message_t& body();
void close() noexcept;
private:
std::optional<routing_id_t> routing_id_;
std::optional<message_t> header_;
std::optional<message_t> body_;
friend class stream_socket_t;
};
bool stream_socket_t::recv_packet(
stream_packet_t& out, recv_flags_t flags = recv_flags_t::none);
stream_recv_mode_t stream_socket_options_t::recv_mode() const;
void stream_socket_options_t::recv_mode(stream_recv_mode_t mode);
Operation 시작 signature는 PAIR send_operation_t send(), DEALER
send_operation_t send()·request_operation_t request(), ROUTER
send_operation_t send(const routing_id_t&)·
request_operation_t request(const routing_id_t&)·
reply_operation_t reply(const routing_id_t&, reply_token_t), STREAM
send_operation_t send(const routing_id_t&)다. Send factory는 target을 builder에 capture한다.
received_t::reply_token()은 const std::optional<reply_token_t>&를 반환한다.
received_t::send()은 source target을 capture한 send_operation_t, received_t::reply()는 source
RID와 token을 capture한 reply_operation_t를 반환한다.
Public C++ surface에는 send/request/reply .flags(...), send .timeout(...), request callback
overload/type, async_result_t::cancel(), STREAM packet handler, monitor on_event·ignore_event,
timer on_fire, pair/generation member와 exact-pair method가 없다. PAIR·STREAM send의
submit()도 bool을 반환하지 않는다. routed_send_operation_t와
routed_send_submit_operation_t도 public type이 아니다.
Monitor는 optional<monitor_event_t> recv(recv_flags_t = none)·status()·close()를,
timer는 start(duration, uint64_t = 0)·stop()·optional<uint64_t> recv()·close()를
제공한다. Pending native option 이름은 ZLINK_OPT_PENDING_MAX_MSGS와
ZLINK_OPT_PENDING_MAX_BYTES다.
Monitor event의 connection_id는 진단과 correlation에만 사용하며 send·reply target이나
reconnect fence로 사용하지 않는다.
Pending native option은 public high-level option façade를 추가하지 않는다.
구현 및 contract test 검증 요구¶
Public C++ interface, 반환값·exception과 poller event만으로 다음을 확인한다. 각 항목은 contract test 하나로 이어진다.
Operation과 완료
- 모든 socket의 send factory가
send_operation_t를 반환하고 blockingsubmit()과async()가 각각NONE과DONTWAIT완료 경계를 관찰한다. - Request는 timeout·blocking result·
async()만 제공한다. - 완료·cancellation·poller의 공통 관측은 실행 모델 검증 요구를 따른다.
- Raw reply를 DEALER peer로 제출해 HWM·PAUSED 대기가 만료하면
submit_error_t의BACKPRESSURED가 관찰되고, ROUTER peer로 제출하면 Completion connection의 HWM-free 결과가 유지된다.
ReplyToken과 STREAM
- 같은 ROUTER owner와 opaque value의 token은 같고 다른 owner의 token은 다르며, 다른 owner token의 reply는 native 호출 전에 실패한다.
- ROUTER wrapper move는 owner tag identity를 보존하고 close·recreate 뒤 stale token은 새 wrapper에 사용할 수 없다.
recv_packet()은 성공 때 output을 채우고NO_DATA·오류 때 empty로 유지하며,close()뒤 같은 output을 다시 사용할 수 있다.
Pull eventing
- Monitor DONTWAIT no-data는
nullopt, timer no-data도nullopt이며 pull lifecycle로 event와 fire count를 한 번씩 관찰한다.