This is an automated email from the ASF dual-hosted git repository.
chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git
The following commit(s) were added to refs/heads/master by this push:
new 82ff8239 Add IPC_V2 data path for UBRing IPC (#3573)
82ff8239 is described below
commit 82ff823902379eb68b0875ab179995ea6a7627c8
Author: Chuang Zhang <[email protected]>
AuthorDate: Tue Oct 13 19:43:17 2026 +0800
Add IPC_V2 data path for UBRing IPC (#3573)
* Add IPC_V2 data path for UBRing IPC
* Move IPC_V2 ring implementation out of header
---------
Co-authored-by: BGQ99 <[email protected]>
---
src/brpc/ubshm/shm/shm_mgr.cpp | 4 +
src/brpc/ubshm/shm/shm_mgr.h | 4 +-
src/brpc/ubshm/ub_endpoint.cpp | 63 ++-
src/brpc/ubshm/ub_endpoint.h | 13 +-
src/brpc/ubshm/ub_ring.cpp | 376 ++++++++++++-
src/brpc/ubshm/ub_ring.h | 38 +-
.../ubshm/{shm/shm_mgr.h => ubr_data_format.h} | 38 +-
src/brpc/ubshm/ubr_msg_v2.cpp | 276 ++++++++++
src/brpc/ubshm/ubr_msg_v2.h | 176 +++++++
src/brpc/ubshm/ubr_trx.h | 5 +-
test/brpc_ubring_unittest.cpp | 583 +++++++++++++++++++++
11 files changed, 1496 insertions(+), 80 deletions(-)
diff --git a/src/brpc/ubshm/shm/shm_mgr.cpp b/src/brpc/ubshm/shm/shm_mgr.cpp
index ea01113e..d4096bc3 100644
--- a/src/brpc/ubshm/shm/shm_mgr.cpp
+++ b/src/brpc/ubshm/shm/shm_mgr.cpp
@@ -87,6 +87,10 @@ void SetShmType(SHM_TYPE type) {
g_shm_type = type;
}
+SHM_TYPE GetShmType() {
+ return g_shm_type;
+}
+
RETURN_CODE ShmLocalMalloc(SHM *shm) {
if (UNLIKELY(!CheckInputShmParam(shm))) {
LOG(ERROR) << "Input param shm is invalid.";
diff --git a/src/brpc/ubshm/shm/shm_mgr.h b/src/brpc/ubshm/shm/shm_mgr.h
index 597f5e4b..b1e55f03 100644
--- a/src/brpc/ubshm/shm/shm_mgr.h
+++ b/src/brpc/ubshm/shm/shm_mgr.h
@@ -26,6 +26,8 @@ namespace brpc {
namespace ubring {
void SetShmType(SHM_TYPE type);
+SHM_TYPE GetShmType();
+
RETURN_CODE ShmMgrInit(void);
void ShmMgrFini(void);
@@ -48,4 +50,4 @@ RETURN_CODE ShmFree(SHM *shm);
}
}
-#endif //BRPC_SHM_MGR_H
\ No newline at end of file
+#endif //BRPC_SHM_MGR_H
diff --git a/src/brpc/ubshm/ub_endpoint.cpp b/src/brpc/ubshm/ub_endpoint.cpp
index 44685eb3..ec7e0842 100644
--- a/src/brpc/ubshm/ub_endpoint.cpp
+++ b/src/brpc/ubshm/ub_endpoint.cpp
@@ -33,6 +33,7 @@
#include "brpc/ubshm/ub_helper.h"
#include "brpc/ubshm/ub_endpoint.h"
#include "brpc/ubshm/shm/shm_def.h"
+#include "brpc/ubshm/shm/shm_mgr.h"
#include "brpc/ubshm/common/common.h"
#include "brpc/ubshm_transport.h"
#include "brpc/ubshm/ubr_trx.h"
@@ -86,6 +87,27 @@ void HelloFormatExtension::Deserialize(const void* data) {
format_id = butil::NetToHost16(net_format_id);
}
+UbrDataFormat PreferredDataFormatForShmType(SHM_TYPE shm_type) {
+ switch (shm_type) {
+ case SHM_TYPE_IPC:
+ return UBR_DATA_FORMAT_IPC_V2;
+ case SHM_TYPE_UBS:
+ return UBR_DATA_FORMAT_LEGACY_64;
+ default:
+ return UBR_DATA_FORMAT_NONE;
+ }
+}
+
+UbrDataFormat SelectDataFormat(UbrDataFormat local_format,
+ uint16_t remote_format_id) {
+ if ((local_format != UBR_DATA_FORMAT_LEGACY_64 &&
+ local_format != UBR_DATA_FORMAT_IPC_V2) ||
+ remote_format_id != static_cast<uint16_t>(local_format)) {
+ return UBR_DATA_FORMAT_NONE;
+ }
+ return local_format;
+}
+
void HelloMessage::Serialize(void* data) const {
char* current_pos = static_cast<char*>(data);
const uint16_t net_msg_len = butil::HostToNet16(msg_len);
@@ -426,6 +448,8 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
return nullptr;
}
+ const UbrDataFormat local_format =
+ PreferredDataFormatForShmType(GetShmType());
UbrDataFormat selected_format = UBR_DATA_FORMAT_NONE;
if (!HelloNegotiationValid(remote_msg)) {
LOG(WARNING) << "Fail to negotiate with server, fallback to tcp:"
@@ -433,7 +457,8 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
ub_transport->_ub_state = UBShmTransport::UB_OFF;
} else {
HelloFormatExtension local_extension = {
- HelloFormatExtension::WIRE_SIZE, UBR_DATA_FORMAT_LEGACY_64};
+ HelloFormatExtension::WIRE_SIZE,
+ static_cast<uint16_t>(local_format)};
local_extension.Serialize(data);
ep->_state = C_FORMAT_SEND;
if (ep->WriteToFd(data, HelloFormatExtension::WIRE_SIZE) < 0) {
@@ -460,16 +485,19 @@ void* UBShmEndpoint::ProcessHandshakeAtClient(void* arg) {
}
HelloFormatExtension remote_extension;
remote_extension.Deserialize(data);
- if (remote_extension.extension_len != HelloFormatExtension::WIRE_SIZE
||
- remote_extension.format_id == UBR_DATA_FORMAT_NONE ||
- remote_extension.format_id != local_extension.format_id) {
+ if (remote_extension.extension_len == HelloFormatExtension::WIRE_SIZE)
{
+ selected_format =
+ SelectDataFormat(local_format, remote_extension.format_id);
+ }
+ if (selected_format == UBR_DATA_FORMAT_NONE) {
LOG(WARNING) << "Fail to negotiate data format with server, "
<< "fallback to tcp:" << s->description();
ub_transport->_ub_state = UBShmTransport::UB_OFF;
} else {
- selected_format = UBR_DATA_FORMAT_LEGACY_64;
ep->_state = C_MAP_REMOTE_SHM;
- if (ep->_ub_ring->UbrMapRemoteShm(&local_trx_shm, shm_name) < 0) {
+ if (ep->_ub_ring->UbrMapRemoteShm(
+ &local_trx_shm, shm_name, remote_msg.len,
+ selected_format) < 0) {
LOG(WARNING) << "Fail to map the remote shm, fallback to tcp:"
<< s->description();
ub_transport->_ub_state = UBShmTransport::UB_OFF;
@@ -563,6 +591,8 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
ep->_state = FAILED;
return nullptr;
}
+ const UbrDataFormat local_format =
+ PreferredDataFormatForShmType(GetShmType());
if (!HelloNegotiationValid(remote_msg)) {
LOG(WARNING) << "Fail to negotiate with client, fallback to tcp:"
<< s->description();
@@ -588,7 +618,8 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
LOG(WARNING) << "Copy client shared memory name failed, ret=" <<
result;
ub_transport->_ub_state = UBShmTransport::UB_OFF;
}
- if (result >= 0 && ep->AllocateServerResources(&remote_trx_shm,
&local_trx_shm) < 0) {
+ if (result >= 0 && ep->AllocateServerResources(
+ &remote_trx_shm, &local_trx_shm, local_format) < 0) {
LOG(WARNING) << "Fail to allocate ub resources, fallback to tcp:"
<< s->description();
ub_transport->_ub_state = UBShmTransport::UB_OFF;
@@ -636,10 +667,13 @@ void* UBShmEndpoint::ProcessHandshakeAtServer(void* arg) {
remote_extension.Deserialize(data);
HelloFormatExtension local_extension = {
HelloFormatExtension::WIRE_SIZE, UBR_DATA_FORMAT_NONE};
- if (remote_extension.extension_len == HelloFormatExtension::WIRE_SIZE
&&
- remote_extension.format_id == UBR_DATA_FORMAT_LEGACY_64) {
- local_extension.format_id = UBR_DATA_FORMAT_LEGACY_64;
- selected_format = UBR_DATA_FORMAT_LEGACY_64;
+ if (remote_extension.extension_len == HelloFormatExtension::WIRE_SIZE)
{
+ selected_format =
+ SelectDataFormat(local_format, remote_extension.format_id);
+ }
+ if (selected_format != UBR_DATA_FORMAT_NONE) {
+ local_extension.format_id =
+ static_cast<uint16_t>(selected_format);
} else {
ub_transport->_ub_state = UBShmTransport::UB_OFF;
}
@@ -783,7 +817,9 @@ int UBShmEndpoint::AllocateClientResources(ubring::SHM*
local_trx_shm, const cha
return 0;
}
-int UBShmEndpoint::AllocateServerResources(ubring::SHM* remote_trx_shm,
ubring::SHM* local_trx_shm) {
+int UBShmEndpoint::AllocateServerResources(ubring::SHM* remote_trx_shm,
+ ubring::SHM* local_trx_shm,
+ UbrDataFormat format) {
if (BAIDU_UNLIKELY(g_skip_ub_init)) {
// For UT
return 0;
@@ -800,7 +836,8 @@ int UBShmEndpoint::AllocateServerResources(ubring::SHM*
remote_trx_shm, ubring::
PLOG(WARNING) << "Fail to create socket for UBRing poller";
return -1;
}
- int ret = _ub_ring->UbrAllocateServerShm(remote_trx_shm, local_trx_shm);
+ int ret = _ub_ring->UbrAllocateServerShm(
+ remote_trx_shm, local_trx_shm, format);
if (ret != 0) {
return ret;
}
diff --git a/src/brpc/ubshm/ub_endpoint.h b/src/brpc/ubshm/ub_endpoint.h
index 2d7b1f55..526966c9 100644
--- a/src/brpc/ubshm/ub_endpoint.h
+++ b/src/brpc/ubshm/ub_endpoint.h
@@ -30,6 +30,7 @@
#include "butil/macros.h"
#include "butil/containers/mpsc_queue.h"
#include "brpc/socket.h"
+#include "brpc/ubshm/ubr_data_format.h"
#include "brpc/ubshm/ub_helper.h"
#include "brpc/ubshm/ub_ring.h"
#include "brpc/ubshm/shm/shm_def.h"
@@ -43,11 +44,6 @@ DECLARE_int32(ub_poller_num);
DECLARE_bool(ub_edisp_unsched);
DECLARE_bool(ub_disable_bthread);
-enum UbrDataFormat {
- UBR_DATA_FORMAT_NONE = 0,
- UBR_DATA_FORMAT_LEGACY_64 = 1,
-};
-
struct HelloFormatExtension {
// The V3 format extension is a fixed-size frame. A different wire size
// requires negotiation through a new hello version.
@@ -60,6 +56,10 @@ struct HelloFormatExtension {
void Deserialize(const void* data);
};
+UbrDataFormat PreferredDataFormatForShmType(SHM_TYPE shm_type);
+UbrDataFormat SelectDataFormat(UbrDataFormat local_format,
+ uint16_t remote_format_id);
+
struct HelloMessage {
void Serialize(void* data) const;
void Deserialize(void* data);
@@ -176,7 +176,8 @@ private:
// Return 0 if success, -1 if failed and errno set
int AllocateClientResources(SHM* local_trx_shm, const char* shm_name);
- int AllocateServerResources(SHM* remote_trx_shm, SHM* local_trx_shm);
+ int AllocateServerResources(SHM* remote_trx_shm, SHM* local_trx_shm,
+ UbrDataFormat format);
// Release resources
void DeallocateResources();
diff --git a/src/brpc/ubshm/ub_ring.cpp b/src/brpc/ubshm/ub_ring.cpp
index ea342673..595b96d0 100644
--- a/src/brpc/ubshm/ub_ring.cpp
+++ b/src/brpc/ubshm/ub_ring.cpp
@@ -19,11 +19,13 @@
#include <gflags/gflags.h>
#include <unistd.h>
#include <ctime>
+#include <limits>
#include "bthread/bthread.h"
#include "butil/logging.h"
#include "brpc/ubshm/ub_ring.h"
#include "brpc/ubshm/ub_ring_manager.h"
#include "brpc/ubshm/shm/shm_ipc.h"
+#include "brpc/ubshm/ubr_msg_v2.h"
namespace brpc {
namespace ubring {
@@ -336,6 +338,10 @@ void *UBRing::UbrAsynClearCallback(void *args)
int UBRing::UbrTrxSend(const void *buf, uint32_t buf_len)
{
+ if (_trx == nullptr || _trx->data_format != UBR_DATA_FORMAT_LEGACY_64) {
+ errno = EPROTONOSUPPORT;
+ return UBRING_ERR;
+ }
if (UNLIKELY(CheckTrxSendPreCheck(_trx) != UBRING_OK)) {
return UBRING_ERR;
}
@@ -378,6 +384,10 @@ int UBRing::UbrTrxSend(const void *buf, uint32_t buf_len)
int UBRing::UbrTrxRecv(void *buf, uint32_t buf_len)
{
+ if (_trx == nullptr || _trx->data_format != UBR_DATA_FORMAT_LEGACY_64) {
+ errno = EPROTONOSUPPORT;
+ return UBRING_ERR;
+ }
RETURN_CODE rc = UBRING_OK;
if (UNLIKELY((rc = CheckTrxRecvParam(_trx, buf, buf_len)) != UBRING_OK)) {
return (rc == UBR_NOT_CONNECTED) ? 0 : rc;
@@ -393,6 +403,10 @@ int UBRing::UbrTrxRecv(void *buf, uint32_t buf_len)
int UBRing::UbrTrxRecvBlockMode(uint8_t *dest, uint32_t buf_len)
{
+ if (_trx == nullptr || _trx->data_format != UBR_DATA_FORMAT_LEGACY_64) {
+ errno = EPROTONOSUPPORT;
+ return UBRING_ERR;
+ }
RETURN_CODE rc = UBRING_OK;
if (UNLIKELY((rc = CheckTrxRecvParam(_trx, dest, buf_len)) != UBRING_OK)) {
return (rc == UBR_NOT_CONNECTED) ? 0 : rc;
@@ -449,11 +463,102 @@ int UBRing::UbrTrxRecvBlockMode(uint8_t *dest, uint32_t
buf_len)
return (int)total_copied;
}
+ssize_t UBRing::UbrIpcV2Writev(const struct iovec* iov, int iovcnt)
+{
+ size_t total = 0;
+ if (!ValidateIpcV2Iov(iov, iovcnt, total)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (total > static_cast<size_t>(std::numeric_limits<ssize_t>::max())) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+ if (_trx->ubr_tx.remote_data_q.addr == nullptr ||
+ _trx->ubr_tx.local_data_status_q.addr == nullptr ||
+ _trx->ubr_tx.remote_rx_event_q.addr == nullptr) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ UbrDataStatusQMsg* status = reinterpret_cast<UbrDataStatusQMsg*>(
+ _trx->ubr_tx.local_data_status_q.addr);
+ IpcV2TxView tx(
+ reinterpret_cast<IpcV2Slot*>(_trx->ubr_tx.remote_data_q.addr),
+ &status->tail, _trx->ubr_tx.capacity, &_trx->ubr_tx.write_pos);
+ size_t written = 0;
+ const IpcV2RingResult result = tx.TryWritev(iov, iovcnt, written);
+ if (result == IPC_V2_RING_RETRY) {
+ return UBRING_RETRY;
+ }
+ if (result == IPC_V2_RING_TOO_LARGE) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+ if (result != IPC_V2_RING_OK) {
+ errno = EBADMSG;
+ return -1;
+ }
+ if (written != 0) {
+ ++_trx->ubr_tx.out_io_id;
+ reinterpret_cast<UbrEventQMsg*>(
+ _trx->ubr_tx.remote_rx_event_q.addr)->io_id =
+ _trx->ubr_tx.out_io_id;
+ }
+ return static_cast<ssize_t>(written);
+}
+
+ssize_t UBRing::UbrIpcV2Readv(const struct iovec* iov, int iovcnt)
+{
+ size_t total = 0;
+ if (!ValidateIpcV2Iov(iov, iovcnt, total)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (_trx->ubr_rx.local_data_q.addr == nullptr ||
+ _trx->ubr_rx.remote_data_status_q.addr == nullptr) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ UbrDataStatusQMsg* status = reinterpret_cast<UbrDataStatusQMsg*>(
+ _trx->ubr_rx.remote_data_status_q.addr);
+ IpcV2RxView rx(
+ reinterpret_cast<IpcV2Slot*>(_trx->ubr_rx.local_data_q.addr),
+ &status->tail, _trx->ubr_rx.capacity, &_trx->ubr_rx.read_pos,
+ &_trx->ubr_rx.ipc_v2_read_offset);
+ const bool update_ep_eof_pos =
+ _trx->ubr_rx.read_pos == _trx->ubr_rx.ep_eof_pos;
+ size_t read = 0;
+ bool batch_end = false;
+ const IpcV2RingResult result =
+ rx.TryReadv(iov, iovcnt, read, batch_end);
+ if (result == IPC_V2_RING_RETRY) {
+ errno = EAGAIN;
+ return -1;
+ }
+ if (result != IPC_V2_RING_OK) {
+ errno = EBADMSG;
+ return -1;
+ }
+ if (update_ep_eof_pos) {
+ _trx->ubr_rx.ep_eof_pos = _trx->ubr_rx.read_pos;
+ }
+ return static_cast<ssize_t>(read);
+}
+
ssize_t UBRing::UbrTrxWritev(const struct iovec *iov, int iovcnt)
{
if (UNLIKELY(CheckTrxSendPreCheck(_trx) != UBRING_OK)) {
return UBRING_ERR;
}
+ if (_trx->data_format == UBR_DATA_FORMAT_IPC_V2) {
+ return UbrIpcV2Writev(iov, iovcnt);
+ }
+ if (_trx->data_format != UBR_DATA_FORMAT_LEGACY_64) {
+ errno = EPROTONOSUPPORT;
+ return UBRING_ERR;
+ }
size_t buf_len = 0;
for (int i = 0; i < iovcnt; i++) {
@@ -511,6 +616,13 @@ ssize_t UBRing::UbrTrxReadv(const struct iovec *iov, int
iovcnt)
if (UNLIKELY((rc = CheckTrxRecvParam(_trx, iov, (uint32_t)iovcnt)) !=
UBRING_OK)) {
return (rc == UBR_NOT_CONNECTED) ? 0 : rc;
}
+ if (_trx->data_format == UBR_DATA_FORMAT_IPC_V2) {
+ return UbrIpcV2Readv(iov, iovcnt);
+ }
+ if (_trx->data_format != UBR_DATA_FORMAT_LEGACY_64) {
+ errno = EPROTONOSUPPORT;
+ return UBRING_ERR;
+ }
UbrMsgFormat *data_msg = (UbrMsgFormat *)_trx->ubr_rx.local_data_q.addr;
uint32_t read_pos_end = _trx->ubr_rx.read_pos;
uint8_t flag = data_msg[read_pos_end].header[UBR_MSG_FLAG_INDEX];
@@ -529,6 +641,10 @@ ssize_t UBRing::UbrTrxReadv(const struct iovec *iov, int
iovcnt)
ssize_t UBRing::UbrTrxReadvBlockMode(const struct iovec *iov, int iovcnt)
{
+ if (_trx == nullptr || _trx->data_format != UBR_DATA_FORMAT_LEGACY_64) {
+ errno = EPROTONOSUPPORT;
+ return UBRING_ERR;
+ }
RETURN_CODE rc = UBRING_OK;
if (UNLIKELY((rc = CheckTrxRecvParam(_trx, iov, (uint32_t)iovcnt)) !=
UBRING_OK)) {
return (rc == UBR_NOT_CONNECTED) ? 0 : rc;
@@ -561,6 +677,12 @@ RETURN_CODE UBRing::IsUbrTrxReadable(uint32_t ep_event)
if (UNLIKELY(_trx->ubr_tx.trx_state != UBR_STATE_CONNECTED)) {
return UBRING_ERR;
}
+ if (_trx->data_format == UBR_DATA_FORMAT_IPC_V2) {
+ return IsIpcV2Readable(ep_event);
+ }
+ if (_trx->data_format != UBR_DATA_FORMAT_LEGACY_64) {
+ return UBRING_ERR;
+ }
uint64_t io_id = ((UbrEventQMsg
*)_trx->ubr_rx.local_rx_event_q.addr)->io_id;
if ((ep_event & EPOLLET) && io_id == _trx->ubr_rx.in_io_id) {
@@ -606,6 +728,12 @@ RETURN_CODE UBRing::IsUbrTrxWriteable(uint32_t ep_event)
LOG(ERROR) << "The trx is not connected state.";
return UBRING_ERR;
}
+ if (_trx->data_format == UBR_DATA_FORMAT_IPC_V2) {
+ return IsIpcV2Writeable(ep_event);
+ }
+ if (_trx->data_format != UBR_DATA_FORMAT_LEGACY_64) {
+ return UBRING_ERR;
+ }
UbrDataStatusQMsg *data_status_msg = (UbrDataStatusQMsg
*)_trx->ubr_tx.local_data_status_q.addr;
uint32_t cap = _trx->ubr_tx.capacity;
@@ -625,6 +753,82 @@ RETURN_CODE UBRing::IsUbrTrxWriteable(uint32_t ep_event)
return UBRING_OK;
}
+RETURN_CODE UBRing::IsIpcV2Readable(uint32_t ep_event)
+{
+ if (_trx->ubr_rx.local_rx_event_q.addr == nullptr ||
+ _trx->ubr_rx.local_data_q.addr == nullptr ||
+ _trx->ubr_rx.capacity < 2 ||
+ _trx->ubr_rx.read_pos >= _trx->ubr_rx.capacity ||
+ _trx->ubr_rx.ep_eof_pos >= _trx->ubr_rx.capacity) {
+ return UBRING_ERR;
+ }
+ const uint64_t io_id = reinterpret_cast<UbrEventQMsg*>(
+ _trx->ubr_rx.local_rx_event_q.addr)->io_id;
+ if ((ep_event & EPOLLET) && io_id == _trx->ubr_rx.in_io_id) {
+ return MPA_MUXER_NOT_READY;
+ }
+ const uint32_t pos = (ep_event & EPOLLET)
+ ? _trx->ubr_rx.ep_eof_pos : _trx->ubr_rx.read_pos;
+ IpcV2Slot* slots = reinterpret_cast<IpcV2Slot*>(
+ _trx->ubr_rx.local_data_q.addr);
+ IpcV2Slot& slot = slots[pos];
+ const uint32_t state =
+ __atomic_load_n(&slot.header.state, __ATOMIC_ACQUIRE);
+ if (state == IPC_V2_SLOT_EMPTY &&
+ (pos != _trx->ubr_rx.read_pos ||
+ _trx->ubr_rx.ipc_v2_read_offset == 0)) {
+ return MPA_MUXER_NOT_READY;
+ }
+ // Leave metadata validation to UbrIpcV2Readv so PollIn can observe
+ // EBADMSG and fail the connection instead of repeatedly polling it.
+ if (ep_event & EPOLLET) {
+ _trx->ubr_rx.in_io_id = io_id;
+ }
+ return UBRING_OK;
+}
+
+RETURN_CODE UBRing::IsIpcV2Writeable(uint32_t ep_event)
+{
+ if (_trx->ubr_tx.remote_data_q.addr == nullptr ||
+ _trx->ubr_tx.local_data_status_q.addr == nullptr ||
+ _trx->ubr_tx.capacity < 2 ||
+ _trx->ubr_tx.write_pos >= _trx->ubr_tx.capacity) {
+ return UBRING_ERR;
+ }
+ IpcV2Slot* slots = reinterpret_cast<IpcV2Slot*>(
+ _trx->ubr_tx.remote_data_q.addr);
+ const uint32_t current_state = __atomic_load_n(
+ &slots[_trx->ubr_tx.write_pos].header.state, __ATOMIC_ACQUIRE);
+ if (current_state != IPC_V2_SLOT_EMPTY &&
+ current_state != IPC_V2_SLOT_READY) {
+ return UBRING_ERR;
+ }
+ UbrDataStatusQMsg* status = reinterpret_cast<UbrDataStatusQMsg*>(
+ _trx->ubr_tx.local_data_status_q.addr);
+ const uint32_t tail = __atomic_load_n(&status->tail, __ATOMIC_ACQUIRE);
+ if (tail >= _trx->ubr_tx.capacity) {
+ return UBRING_ERR;
+ }
+ const uint32_t capacity = _trx->ubr_tx.capacity;
+ const uint64_t available = _trx->ubr_tx.write_pos > tail
+ ? static_cast<uint64_t>(tail) + capacity - _trx->ubr_tx.write_pos
+ : tail - _trx->ubr_tx.write_pos;
+ const uint32_t remain_slots = static_cast<uint32_t>(available);
+ if (remain_slots == 0) {
+ _trx->ubr_tx.ep_last_cap = 0;
+ return MPA_MUXER_NOT_READY;
+ }
+ if (current_state != IPC_V2_SLOT_EMPTY) {
+ return UBRING_ERR;
+ }
+ if ((ep_event & EPOLLET) && _trx->ubr_tx.ep_last_cap >= remain_slots) {
+ _trx->ubr_tx.ep_last_cap = remain_slots;
+ return MPA_MUXER_NOT_READY;
+ }
+ _trx->ubr_tx.ep_last_cap = remain_slots;
+ return UBRING_OK;
+}
+
RETURN_CODE UBRing::UbrSetTimeout(UbrTaskStep task_type, int timeout)
{
if (task_type >= UBR_TASK_STEP_NUM || timeout < 0) {
@@ -770,19 +974,14 @@ RETURN_CODE UBRing::UbrTrxMapRemoteShm(SHM *remote_shm)
return UBRING_OK;
}
-RETURN_CODE UBRing::UbrServerTrxInit(SHM *local_shm, SHM *remote_shm)
+RETURN_CODE UBRing::InitializeServerLegacyFormat()
{
- RETURN_CODE rc = UbrTrxMapShm(local_shm, remote_shm);
- if (UNLIKELY(rc != UBRING_OK)) {
- LOG(ERROR) <<"Trx map shared memory failed.";
- return rc;
- }
-
uint32_t local_data_msg_cap = (uint32_t)(_trx->ubr_rx.local_data_q.len /
UBR_MSG_LEN);
uint32_t remote_data_msg_cap = (uint32_t)(_trx->ubr_tx.remote_data_q.len /
UBR_MSG_LEN);
_trx->ubr_rx.capacity = local_data_msg_cap;
_trx->ubr_tx.capacity = remote_data_msg_cap;
- rc = UBRingManager::GetUbrDealMsgMaxCnt(_trx->ubr_rx.capacity,
&_trx->ubr_rx.deal_msg_max_cnt);
+ RETURN_CODE rc = UBRingManager::GetUbrDealMsgMaxCnt(
+ _trx->ubr_rx.capacity, &_trx->ubr_rx.deal_msg_max_cnt);
if (UNLIKELY(rc != UBRING_OK)) {
LOG(ERROR) << "Get ubring deal msg max cnt.";
return rc;
@@ -792,6 +991,28 @@ RETURN_CODE UBRing::UbrServerTrxInit(SHM *local_shm, SHM
*remote_shm)
((UbrDataStatusQMsg *)(_trx->ubr_tx.local_data_status_q.addr))->tail =
remote_data_msg_cap - 1;
((UbrDataStatusQMsg *)(_trx->ubr_rx.remote_data_status_q.addr))->tail =
local_data_msg_cap - 1;
+ return UBRING_OK;
+}
+
+RETURN_CODE UBRing::UbrServerTrxInit(SHM* local_shm, SHM* remote_shm,
+ UbrDataFormat format)
+{
+ RETURN_CODE rc = UbrTrxMapShm(local_shm, remote_shm);
+ if (UNLIKELY(rc != UBRING_OK)) {
+ LOG(ERROR) << "Trx map shared memory failed.";
+ return rc;
+ }
+
+ if (format == UBR_DATA_FORMAT_LEGACY_64) {
+ rc = InitializeServerLegacyFormat();
+ } else if (format == UBR_DATA_FORMAT_IPC_V2) {
+ rc = UbrPrepareIpcV2Format() == 0 ? UBRING_OK : UBRING_ERR;
+ } else {
+ rc = UBRING_ERR;
+ }
+ if (UNLIKELY(rc != UBRING_OK)) {
+ return rc;
+ }
if (UNLIKELY(UbrAddTimer() != UBRING_OK)) {
LOG(ERROR) << "Ubr add timer failed, local_name=" << local_shm->name;
@@ -807,10 +1028,58 @@ RETURN_CODE UBRing::UbrServerTrxInit(SHM *local_shm, SHM
*remote_shm)
((UbrEventQMsg *)_trx->ubr_rx.local_rx_event_q.addr)->flag =
UBR_STATE_CONNECTED;
_trx->ubr_tx.trx_state = UBR_STATE_CONNECTED;
_trx->ubr_rx.trx_state = UBR_STATE_CONNECTED;
+ _trx->data_format = format;
return UBRING_OK;
}
-int UBRing::UbrAllocateServerShm(SHM* remote_trx_shm, SHM* local_trx_shm) {
+int UBRing::UbrPrepareIpcV2Format()
+{
+ if (_trx == nullptr || _trx->data_format != UBR_DATA_FORMAT_NONE) {
+ LOG(ERROR) << "Prepare IPC_V2 format failed, invalid transaction
state.";
+ return -1;
+ }
+ if (_trx->ubr_rx.local_data_q.addr == nullptr ||
+ _trx->ubr_tx.remote_data_q.addr == nullptr ||
+ _trx->ubr_tx.local_data_status_q.addr == nullptr ||
+ _trx->ubr_rx.remote_data_status_q.addr == nullptr) {
+ LOG(ERROR) << "Prepare IPC_V2 format failed, queue is not mapped.";
+ return -1;
+ }
+
+ UbrDataStatusQMsg* local_status = reinterpret_cast<UbrDataStatusQMsg*>(
+ _trx->ubr_tx.local_data_status_q.addr);
+ UbrDataStatusQMsg* remote_status = reinterpret_cast<UbrDataStatusQMsg*>(
+ _trx->ubr_rx.remote_data_status_q.addr);
+ IpcV2QueueView tx_queue;
+ IpcV2QueueView rx_queue;
+ if (!MapIpcV2Queue(_trx->ubr_tx.remote_data_q.addr,
+ _trx->ubr_tx.remote_data_q.len,
+ &local_status->tail, tx_queue) ||
+ !MapIpcV2Queue(_trx->ubr_rx.local_data_q.addr,
+ _trx->ubr_rx.local_data_q.len,
+ &remote_status->tail, rx_queue)) {
+ LOG(ERROR) << "Prepare IPC_V2 format failed, invalid queue layout.";
+ return -1;
+ }
+ if (!IpcV2RxView::InitializeShared(rx_queue.slots, rx_queue.tail,
+ rx_queue.capacity)) {
+ LOG(ERROR) << "Prepare IPC_V2 receive queue failed.";
+ return -1;
+ }
+
+ _trx->ubr_tx.capacity = tx_queue.capacity;
+ _trx->ubr_tx.write_pos = 0;
+ _trx->ubr_tx.ep_last_cap = 0;
+ _trx->ubr_rx.capacity = rx_queue.capacity;
+ _trx->ubr_rx.read_pos = 0;
+ _trx->ubr_rx.ep_eof_pos = 0;
+ _trx->ubr_rx.ipc_v2_read_offset = 0;
+ _trx->data_format = UBR_DATA_FORMAT_IPC_V2;
+ return 0;
+}
+
+int UBRing::UbrAllocateServerShm(SHM* remote_trx_shm, SHM* local_trx_shm,
+ UbrDataFormat format) {
UbrSetSleepTask(UBR_TASK_ACCEPT_MAP_FRONT);
if (UNLIKELY((ShmRemoteMalloc(remote_trx_shm)) != UBRING_OK)) {
LOG(ERROR) << "Trx apply remote shared memory failed.";
@@ -831,7 +1100,8 @@ int UBRing::UbrAllocateServerShm(SHM* remote_trx_shm, SHM*
local_trx_shm) {
return -1;
}
_trx->type = TCP_TRX;
- if (UNLIKELY((UbrServerTrxInit(local_trx_shm, remote_trx_shm)) !=
UBRING_OK)) {
+ if (UNLIKELY((UbrServerTrxInit(
+ local_trx_shm, remote_trx_shm, format)) != UBRING_OK)) {
LOG(ERROR) << "Server trx init failed.";
UbrTrxFreeShm(_trx);
UBRingManager::ReleaseUbrTrxFromMgr(_trx);
@@ -849,7 +1119,8 @@ int UBRing::UbrAllocateLocalShm(SHM *local_trx_shm, const
char *shm_name)
}
_trx->type = TCP_TRX;
- if (UNLIKELY((ApplyAndMapLocalShm(local_trx_shm, shm_name)) != UBRING_OK))
{
+ RETURN_CODE rc = ApplyAndMapLocalShm(local_trx_shm, shm_name);
+ if (UNLIKELY(rc != UBRING_OK)) {
LOG(ERROR) << "Trx apply or map local shared memory failed,
local_name=" << shm_name;
_trx = nullptr;
return -1;
@@ -857,29 +1128,71 @@ int UBRing::UbrAllocateLocalShm(SHM *local_trx_shm,
const char *shm_name)
return 0;
}
-int UBRing::UbrMapRemoteShm(SHM *local_trx_shm, const char *local_name)
+int UBRing::UbrMapRemoteShm(SHM *local_trx_shm, const char *local_name,
+ uint64_t remote_shm_len, UbrDataFormat format)
{
- RETURN_CODE rc = UbrMapRemoteShmAddTimer(local_trx_shm, local_name);
+ RETURN_CODE rc = UbrMapRemoteShmAddTimer(
+ local_trx_shm, local_name, remote_shm_len, format);
if (UNLIKELY(rc != UBRING_OK)) {
LOG(ERROR) << "Connect Trx failed, local shm name=" <<
local_trx_shm->name;
return -1;
}
+ return 0;
+}
+
+void UBRing::InitializeClientLegacyFormat()
+{
+ // The local and remote Legacy initialization has already set the RX and
+ // TX capacities. Only prewrite the queues here.
PrewriteUbrRx(&_trx->ubr_rx);
PrewriteUbrTx(&_trx->ubr_tx);
+}
+
+RETURN_CODE UBRing::InitializeClientLocalLegacyFormat()
+{
+ _trx->ubr_rx.capacity =
+ (uint32_t)(_trx->ubr_rx.local_data_q.len / UBR_MSG_LEN);
+ RETURN_CODE rc = UBRingManager::GetUbrDealMsgMaxCnt(
+ _trx->ubr_rx.capacity, &_trx->ubr_rx.deal_msg_max_cnt);
+ if (UNLIKELY(rc != UBRING_OK)) {
+ LOG(ERROR) << "Get ubring deal msg max cnt.";
+ return rc;
+ }
+ return UBRING_OK;
+}
+
+void UBRing::InitializeClientRemoteLegacyFormat()
+{
+ _trx->ubr_tx.capacity =
+ (uint32_t)(_trx->ubr_tx.remote_data_q.len / UBR_MSG_LEN);
+}
+
+void UBRing::ActivateClientFormat()
+{
+ // Only publish the connected states here. UbrMapRemoteShmAddTimer has
+ // already started the timers; preserve that legacy ordering.
((UbrEventQMsg *)_trx->ubr_rx.remote_tx_event_q.addr)->flag =
UBR_STATE_CONNECTED;
((UbrEventQMsg *)_trx->ubr_rx.local_rx_event_q.addr)->flag =
UBR_STATE_CONNECTED;
_trx->ubr_tx.trx_state = UBR_STATE_CONNECTED;
_trx->ubr_rx.trx_state = UBR_STATE_CONNECTED;
- return 0;
}
-RETURN_CODE UBRing::UbrMapRemoteShmAddTimer(SHM *local_trx_shm, const char
*local_name)
+RETURN_CODE UBRing::UbrMapRemoteShmAddTimer(SHM *local_trx_shm, const char
*local_name,
+ uint64_t remote_shm_len,
+ UbrDataFormat format)
{
uint64_t start_time = GetCurNanoSeconds();
- size_t remote_server_len = UBR_MSG_LEN * (((UbrDataStatusQMsg
*)(_trx->ubr_tx.local_data_status_q.addr))->tail + 1) +
- UBR_MSG_LEN * ((DATAQ_ADDR_OFFSET / UBR_MSG_LEN)
+ 1);
- SHM remote_trx_shm = {nullptr, remote_server_len, 0, {0},
local_trx_shm->fd};
+ // Use the base Hello length, not a tail initialized for a particular
format.
+ // Bound the conversion and the current Legacy capacity before mapping.
+ // ShmRemoteMalloc additionally checks the minimum size and allocation
unit.
+ if (remote_shm_len > std::numeric_limits<size_t>::max() ||
+ remote_shm_len / UBR_MSG_LEN > std::numeric_limits<uint32_t>::max()) {
+ LOG(ERROR) << "Remote shared memory length is too large: " <<
remote_shm_len;
+ return SHM_ERR_INPUT_INVALID;
+ }
+ SHM remote_trx_shm = {nullptr, static_cast<size_t>(remote_shm_len),
+ 0, {0}, local_trx_shm->fd};
int result = snprintf(remote_trx_shm.name,
SHM_MAX_NAME_BUFF_LEN,
"%s_%s_%s",
@@ -896,6 +1209,22 @@ RETURN_CODE UBRing::UbrMapRemoteShmAddTimer(SHM
*local_trx_shm, const char *loca
LOG(ERROR) << "Connect Trx map shared memory failed, remote shm=" <<
remote_trx_shm.name;
return rc;
}
+ if (format == UBR_DATA_FORMAT_LEGACY_64) {
+ InitializeClientRemoteLegacyFormat();
+ rc = InitializeClientLocalLegacyFormat();
+ if (LIKELY(rc == UBRING_OK)) {
+ InitializeClientLegacyFormat();
+ }
+ } else if (format == UBR_DATA_FORMAT_IPC_V2) {
+ rc = UbrPrepareIpcV2Format() == 0 ? UBRING_OK : UBRING_ERR;
+ } else {
+ rc = UBRING_ERR;
+ }
+ if (UNLIKELY(rc != UBRING_OK)) {
+ LOG(ERROR) << "Initialize client data format failed, local_name="
+ << local_name << ", format=" << format;
+ return rc;
+ }
if (UNLIKELY(UbrAddTimer() != UBRING_OK)) {
LOG(ERROR) << "Ubr add timer failed, local_name=" << local_name;
@@ -914,6 +1243,8 @@ RETURN_CODE UBRing::UbrMapRemoteShmAddTimer(SHM
*local_trx_shm, const char *loca
return UBRING_ERR_TIMEOUT;
}
+ ActivateClientFormat();
+ _trx->data_format = format;
return UBRING_OK;
}
@@ -952,14 +1283,6 @@ RETURN_CODE UBRing::ApplyAndMapLocalShm(SHM
*local_trx_shm, const char *local_na
}
((UbrDataStatusQMsg *)_trx->ubr_tx.local_data_status_q.addr)->timeout =
FLAGS_ub_connect_timeout_s;
- _trx->ubr_rx.capacity = (uint32_t)(_trx->ubr_rx.local_data_q.len /
UBR_MSG_LEN);
- rc = UBRingManager::GetUbrDealMsgMaxCnt(_trx->ubr_rx.capacity,
&_trx->ubr_rx.deal_msg_max_cnt);
- if (rc != UBRING_OK) {
- LOG(ERROR) << "Get ubring deal msg max cnt, local shm name=" <<
local_trx_shm->name;
- ShmLocalFree(local_trx_shm);
- UBRingManager::ReleaseUbrTrxFromMgr(_trx);
- return rc;
- }
return UBRING_OK;
}
@@ -976,7 +1299,6 @@ RETURN_CODE UBRing::ApplyAndMapRemoteShm(SHM
*remote_trx_shm)
ShmRemoteFree(remote_trx_shm);
return rc;
}
- _trx->ubr_tx.capacity = (uint32_t)(_trx->ubr_tx.remote_data_q.len /
UBR_MSG_LEN);
return UBRING_OK;
}
diff --git a/src/brpc/ubshm/ub_ring.h b/src/brpc/ubshm/ub_ring.h
index 0a02dd2f..bd667d1f 100644
--- a/src/brpc/ubshm/ub_ring.h
+++ b/src/brpc/ubshm/ub_ring.h
@@ -107,16 +107,36 @@ public:
return UBRING_OK;
}
- int UbrAllocateServerShm(SHM* remote_trx_shm, SHM* local_trx_shm);
+ int UbrAllocateServerShm(SHM* remote_trx_shm, SHM* local_trx_shm,
+ UbrDataFormat format);
- int UbrMapRemoteShm(SHM *local_trx_shm, const char *local_name);
+ // Prepare the IPC_V2 queue layout and connection-local cursors. This does
+ // not start timers, publish CONNECTED or enable the data path.
+ int UbrPrepareIpcV2Format();
+
+ UbrDataFormat data_format() const {
+ return _trx == nullptr ? UBR_DATA_FORMAT_NONE : _trx->data_format;
+ }
+
+#ifdef UNIT_TEST
+ void SetTrxForTest(UbrTrx* trx) { _trx = trx; }
+#endif
+
+ int UbrMapRemoteShm(SHM *local_trx_shm, const char *local_name,
+ uint64_t remote_shm_len, UbrDataFormat format);
int UbrAllocateLocalShm(SHM *local_trx_shm, const char *shm_name);
- RETURN_CODE UbrMapRemoteShmAddTimer(SHM *local_trx_shm, const char
*local_name);
+ RETURN_CODE UbrMapRemoteShmAddTimer(SHM *local_trx_shm, const char
*local_name,
+ uint64_t remote_shm_len,
+ UbrDataFormat format);
static inline RETURN_CODE CheckTrxSendPreCheck(UbrTrx *trx)
{
+ if (UNLIKELY(trx == nullptr)) {
+ LOG(ERROR) << "Trx send failed, trx is null.";
+ return UBRING_ERR;
+ }
if (UNLIKELY(trx->ubr_tx.trx_state != UBR_STATE_CONNECTED)) {
LOG(ERROR) << "Trx send failed, trx is not connected state.";
return UBRING_ERR;
@@ -202,6 +222,10 @@ public:
}
private:
+ ssize_t UbrIpcV2Writev(const struct iovec* iov, int iovcnt);
+ ssize_t UbrIpcV2Readv(const struct iovec* iov, int iovcnt);
+ RETURN_CODE IsIpcV2Readable(uint32_t ep_event);
+ RETURN_CODE IsIpcV2Writeable(uint32_t ep_event);
RETURN_CODE UbrTrxMapLocalShm(SHM *local_shm);
RETURN_CODE UbrTrxMapRemoteShm(SHM *remote_shm);
RETURN_CODE ApplyAndMapLocalShm(SHM *local_trx_shm, const char
*local_name);
@@ -211,7 +235,13 @@ private:
ssize_t StartReadv(UbrTrx *trx, const struct iovec *iov, int iovcnt,
size_t remain_buf_len);
void PreWriteAddr(uint8_t *addr, size_t len);
RETURN_CODE WritevHasEnoughSpace(size_t buf_len);
- RETURN_CODE UbrServerTrxInit(SHM *local_shm, SHM *remote_shm);
+ RETURN_CODE UbrServerTrxInit(SHM* local_shm, SHM* remote_shm,
+ UbrDataFormat format);
+ RETURN_CODE InitializeServerLegacyFormat();
+ RETURN_CODE InitializeClientLocalLegacyFormat();
+ void InitializeClientRemoteLegacyFormat();
+ void InitializeClientLegacyFormat();
+ void ActivateClientFormat();
static RETURN_CODE UbrClearResourceCheck(UbrTrx *trx, uint64_t start_time,
UbrCloseType close_type);
static RETURN_CODE ClearTrxResource(UbrTrx *trx, uint64_t start_time,
UbrCloseType close_type, int op=0);
diff --git a/src/brpc/ubshm/shm/shm_mgr.h b/src/brpc/ubshm/ubr_data_format.h
similarity index 60%
copy from src/brpc/ubshm/shm/shm_mgr.h
copy to src/brpc/ubshm/ubr_data_format.h
index 597f5e4b..dc834f03 100644
--- a/src/brpc/ubshm/shm/shm_mgr.h
+++ b/src/brpc/ubshm/ubr_data_format.h
@@ -15,37 +15,19 @@
// specific language governing permissions and limitations
// under the License.
-#ifndef BRPC_SHM_MGR_H
-#define BRPC_SHM_MGR_H
-
-#include <stdint.h>
-#include "brpc/ubshm/common/common.h"
-#include "brpc/ubshm/shm/shm_def.h"
+#ifndef BRPC_UBSHM_UBR_DATA_FORMAT_H
+#define BRPC_UBSHM_UBR_DATA_FORMAT_H
namespace brpc {
namespace ubring {
-void SetShmType(SHM_TYPE type);
-
-RETURN_CODE ShmMgrInit(void);
-
-void ShmMgrFini(void);
-
-RETURN_CODE ShmLocalMalloc(SHM *shm);
-
-RETURN_CODE ShmLocalCalloc(SHM *shm);
-
-RETURN_CODE ShmLocalFree(SHM *shm);
-
-RETURN_CODE ShmRemoteMalloc(SHM *shm);
-
-RETURN_CODE ShmRemoteFree(SHM *shm);
-
-RETURN_CODE ShmLocalMmap(SHM *shm, int prot);
-RETURN_CODE ShmMunmap(SHM *shm);
+enum UbrDataFormat {
+ UBR_DATA_FORMAT_NONE = 0,
+ UBR_DATA_FORMAT_LEGACY_64 = 1,
+ UBR_DATA_FORMAT_IPC_V2 = 2,
+};
-RETURN_CODE ShmFree(SHM *shm);
-}
-}
+} // namespace ubring
+} // namespace brpc
-#endif //BRPC_SHM_MGR_H
\ No newline at end of file
+#endif // BRPC_UBSHM_UBR_DATA_FORMAT_H
diff --git a/src/brpc/ubshm/ubr_msg_v2.cpp b/src/brpc/ubshm/ubr_msg_v2.cpp
new file mode 100644
index 00000000..4821f0ee
--- /dev/null
+++ b/src/brpc/ubshm/ubr_msg_v2.cpp
@@ -0,0 +1,276 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "brpc/ubshm/ubr_msg_v2.h"
+
+#include <limits>
+#include <string.h>
+
+namespace brpc {
+namespace ubring {
+
+bool ValidateIpcV2Iov(const struct iovec* iov, int iovcnt, size_t& total) {
+ total = 0;
+ if (iovcnt < 0 || (iovcnt > 0 && iov == nullptr)) {
+ return false;
+ }
+ for (int i = 0; i < iovcnt; ++i) {
+ if (iov[i].iov_len != 0 && iov[i].iov_base == nullptr) {
+ return false;
+ }
+ if (iov[i].iov_len > std::numeric_limits<size_t>::max() - total) {
+ return false;
+ }
+ total += iov[i].iov_len;
+ }
+ return true;
+}
+
+void IpcV2Slot::Initialize() {
+ header.payload_len = 0;
+ header.flags = 0;
+ header.reserved = 0;
+ __atomic_store_n(&header.state, IPC_V2_SLOT_EMPTY, __ATOMIC_RELEASE);
+}
+
+IpcV2SlotResult IpcV2Slot::TryConsume(void* destination, size_t size,
+ uint32_t& offset, size_t& copied,
+ bool& batch_end) {
+ copied = 0;
+ batch_end = false;
+ if (size == 0) {
+ return IPC_V2_SLOT_OK;
+ }
+ if (destination == nullptr) {
+ return IPC_V2_SLOT_INVALID;
+ }
+ const uint32_t state =
+ __atomic_load_n(&header.state, __ATOMIC_ACQUIRE);
+ if (state == IPC_V2_SLOT_EMPTY) {
+ return offset == 0 ? IPC_V2_SLOT_NOT_READY : IPC_V2_SLOT_INVALID;
+ }
+ if (state != IPC_V2_SLOT_READY) {
+ return IPC_V2_SLOT_INVALID;
+ }
+ const uint32_t length = header.payload_len;
+ const uint32_t flags = header.flags;
+ if (length == 0 || length > PAYLOAD_SIZE || offset >= length ||
+ (flags & ~static_cast<uint32_t>(IPC_V2_SLOT_EOF)) != 0 ||
+ header.reserved != 0) {
+ return IPC_V2_SLOT_INVALID;
+ }
+ const size_t remaining = length - offset;
+ copied = size < remaining ? size : remaining;
+ memcpy(destination, payload + offset, copied);
+ offset += static_cast<uint32_t>(copied);
+ if (offset == length) {
+ batch_end = (flags & IPC_V2_SLOT_EOF) != 0;
+ offset = 0;
+ // Do not access this slot after returning ownership to Producer.
+ __atomic_store_n(&header.state, IPC_V2_SLOT_EMPTY, __ATOMIC_RELEASE);
+ }
+ return IPC_V2_SLOT_OK;
+}
+
+bool IpcV2Slot::CalculateCapacity(size_t data_bytes, uint32_t& capacity) {
+ capacity = 0;
+ const size_t slots = data_bytes / SLOT_SIZE;
+ if (slots < 2 || slots > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
+ capacity = static_cast<uint32_t>(slots);
+ return true;
+}
+
+IpcV2RingResult IpcV2TxView::TryWritev(const struct iovec* iov, int iovcnt,
+ size_t& written) {
+ written = 0;
+ size_t total = 0;
+ if (!ValidateIpcV2Iov(iov, iovcnt, total)) {
+ return IPC_V2_RING_INVALID;
+ }
+ if (total == 0) {
+ return IPC_V2_RING_OK;
+ }
+ if (_slots == nullptr || _tail == nullptr || _write_pos == nullptr ||
+ _capacity < 2 || *_write_pos >= _capacity) {
+ return IPC_V2_RING_INVALID;
+ }
+ const uint32_t current_state = __atomic_load_n(
+ &_slots[*_write_pos].header.state, __ATOMIC_ACQUIRE);
+ if (current_state != IPC_V2_SLOT_EMPTY &&
+ current_state != IPC_V2_SLOT_READY) {
+ return IPC_V2_RING_INVALID;
+ }
+ const size_t required = total / IpcV2Slot::PAYLOAD_SIZE +
+ (total % IpcV2Slot::PAYLOAD_SIZE != 0);
+ if (required >= _capacity) {
+ return IPC_V2_RING_TOO_LARGE;
+ }
+ const uint32_t tail = __atomic_load_n(_tail, __ATOMIC_ACQUIRE);
+ if (tail >= _capacity) {
+ return IPC_V2_RING_INVALID;
+ }
+ const uint64_t available = *_write_pos > tail
+ ? static_cast<uint64_t>(tail) + _capacity - *_write_pos
+ : tail - *_write_pos;
+ if (available < required) {
+ return IPC_V2_RING_RETRY;
+ }
+ for (size_t i = 0; i < required; ++i) {
+ const uint32_t pos = static_cast<uint32_t>(
+ (static_cast<uint64_t>(*_write_pos) + i) % _capacity);
+ if (__atomic_load_n(&_slots[pos].header.state, __ATOMIC_ACQUIRE) !=
+ IPC_V2_SLOT_EMPTY) {
+ return IPC_V2_RING_INVALID;
+ }
+ }
+
+ int iov_index = 0;
+ size_t iov_offset = 0;
+ size_t remaining = total;
+ while (remaining != 0) {
+ IpcV2Slot& slot = _slots[*_write_pos];
+ const size_t slot_length = remaining < IpcV2Slot::PAYLOAD_SIZE
+ ? remaining : IpcV2Slot::PAYLOAD_SIZE;
+ size_t slot_offset = 0;
+ while (slot_offset < slot_length) {
+ while (iov_index < iovcnt && iov[iov_index].iov_len == 0) {
+ ++iov_index;
+ }
+ const size_t available = iov[iov_index].iov_len - iov_offset;
+ const size_t copy_length = available < slot_length - slot_offset
+ ? available : slot_length - slot_offset;
+ memcpy(slot.payload + slot_offset,
+ static_cast<const uint8_t*>(iov[iov_index].iov_base) +
+ iov_offset,
+ copy_length);
+ slot_offset += copy_length;
+ iov_offset += copy_length;
+ if (iov_offset == iov[iov_index].iov_len) {
+ ++iov_index;
+ iov_offset = 0;
+ }
+ }
+ slot.header.payload_len = static_cast<uint32_t>(slot_length);
+ slot.header.flags = remaining == slot_length
+ ? static_cast<uint32_t>(IPC_V2_SLOT_EOF) : 0u;
+ slot.header.reserved = 0;
+ __atomic_store_n(&slot.header.state, IPC_V2_SLOT_READY,
+ __ATOMIC_RELEASE);
+ *_write_pos = (*_write_pos + 1) % _capacity;
+ remaining -= slot_length;
+ }
+ written = total;
+ return IPC_V2_RING_OK;
+}
+
+bool IpcV2RxView::InitializeShared(IpcV2Slot* slots, uint32_t* tail,
+ uint32_t capacity) {
+ if (slots == nullptr || tail == nullptr || capacity < 2) {
+ return false;
+ }
+ for (uint32_t i = 0; i < capacity; ++i) {
+ slots[i].Initialize();
+ }
+ __atomic_store_n(tail, capacity - 1, __ATOMIC_RELEASE);
+ return true;
+}
+
+IpcV2RingResult IpcV2RxView::TryReadv(const struct iovec* iov, int iovcnt,
+ size_t& read, bool& batch_end) {
+ read = 0;
+ batch_end = false;
+ size_t destination_size = 0;
+ if (!ValidateIpcV2Iov(iov, iovcnt, destination_size)) {
+ return IPC_V2_RING_INVALID;
+ }
+ if (destination_size == 0) {
+ return IPC_V2_RING_OK;
+ }
+ if (_slots == nullptr || _tail == nullptr || _read_pos == nullptr ||
+ _read_offset == nullptr || _capacity < 2 ||
+ *_read_pos >= _capacity) {
+ return IPC_V2_RING_INVALID;
+ }
+
+ int iov_index = 0;
+ size_t iov_offset = 0;
+ while (read < destination_size) {
+ while (iov_index < iovcnt && iov[iov_index].iov_len == 0) {
+ ++iov_index;
+ }
+ size_t copied = 0;
+ bool slot_batch_end = false;
+ const IpcV2SlotResult result = _slots[*_read_pos].TryConsume(
+ static_cast<uint8_t*>(iov[iov_index].iov_base) + iov_offset,
+ iov[iov_index].iov_len - iov_offset, *_read_offset, copied,
+ slot_batch_end);
+ if (result == IPC_V2_SLOT_NOT_READY) {
+ return read == 0 ? IPC_V2_RING_RETRY : IPC_V2_RING_OK;
+ }
+ if (result != IPC_V2_SLOT_OK || copied == 0) {
+ return IPC_V2_RING_INVALID;
+ }
+ read += copied;
+ iov_offset += copied;
+ if (iov_offset == iov[iov_index].iov_len) {
+ ++iov_index;
+ iov_offset = 0;
+ }
+ if (*_read_offset == 0) {
+ // TryConsume releases EMPTY first; tail follows that release.
+ __atomic_store_n(_tail, *_read_pos, __ATOMIC_RELEASE);
+ *_read_pos = (*_read_pos + 1) % _capacity;
+ if (slot_batch_end) {
+ batch_end = true;
+ return IPC_V2_RING_OK;
+ }
+ }
+ }
+ return IPC_V2_RING_OK;
+}
+
+bool MapIpcV2Queue(uint8_t* data_addr, size_t data_len, uint32_t* tail,
+ IpcV2QueueView& queue) {
+ queue = IpcV2QueueView();
+ if (data_addr == nullptr || tail == nullptr ||
+ reinterpret_cast<uintptr_t>(data_addr) % alignof(IpcV2Slot) != 0 ||
+ reinterpret_cast<uintptr_t>(tail) % alignof(uint32_t) != 0) {
+ return false;
+ }
+ uint32_t capacity = 0;
+ if (!IpcV2Slot::CalculateCapacity(data_len, capacity)) {
+ return false;
+ }
+ queue.slots = reinterpret_cast<IpcV2Slot*>(data_addr);
+ queue.tail = tail;
+ queue.capacity = capacity;
+ return true;
+}
+
+bool InitializeIpcV2ReceiveQueue(uint8_t* data_addr, size_t data_len,
+ uint32_t* tail, IpcV2QueueView& queue) {
+ if (!MapIpcV2Queue(data_addr, data_len, tail, queue)) {
+ return false;
+ }
+ return IpcV2RxView::InitializeShared(queue.slots, queue.tail,
+ queue.capacity);
+}
+
+} // namespace ubring
+} // namespace brpc
diff --git a/src/brpc/ubshm/ubr_msg_v2.h b/src/brpc/ubshm/ubr_msg_v2.h
new file mode 100644
index 00000000..a08b4642
--- /dev/null
+++ b/src/brpc/ubshm/ubr_msg_v2.h
@@ -0,0 +1,176 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#ifndef BRPC_UBSHM_UBR_MSG_V2_H
+#define BRPC_UBSHM_UBR_MSG_V2_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/uio.h>
+#include <type_traits>
+
+namespace brpc {
+namespace ubring {
+
+enum IpcV2SlotState : uint32_t {
+ IPC_V2_SLOT_UNINITIALIZED = 0,
+ IPC_V2_SLOT_EMPTY = 1,
+ IPC_V2_SLOT_READY = 2,
+};
+
+enum IpcV2SlotFlags : uint32_t {
+ // End of one lower-level write batch, not an RPC or connection boundary.
+ IPC_V2_SLOT_EOF = 1u << 0,
+};
+
+enum IpcV2SlotResult {
+ IPC_V2_SLOT_OK,
+ IPC_V2_SLOT_NOT_READY,
+ IPC_V2_SLOT_INVALID,
+};
+
+enum IpcV2RingResult {
+ IPC_V2_RING_OK,
+ IPC_V2_RING_RETRY,
+ IPC_V2_RING_INVALID,
+ IPC_V2_RING_TOO_LARGE,
+};
+
+struct IpcV2SlotHeader {
+ // Access state atomically. The producer publishes READY with release and
+ // the consumer observes it with acquire; recycling EMPTY is symmetric.
+ uint32_t state;
+ uint32_t payload_len;
+ uint32_t flags;
+ uint32_t reserved;
+};
+
+struct alignas(64) IpcV2Slot {
+ enum : size_t {
+ SLOT_SIZE = 4096,
+ PAYLOAD_OFFSET = 16,
+ PAYLOAD_SIZE = SLOT_SIZE - PAYLOAD_OFFSET,
+ };
+
+ IpcV2SlotHeader header;
+ uint8_t payload[PAYLOAD_SIZE];
+
+ // Initialization happens before the peer is allowed to access this ring.
+ void Initialize();
+
+ IpcV2SlotResult TryConsume(void* destination, size_t size,
+ uint32_t& offset, size_t& copied,
+ bool& batch_end);
+
+ // `data_bytes` excludes the separately allocated control area. Trailing
+ // bytes that do not fit one slot are intentionally unused.
+ static bool CalculateCapacity(size_t data_bytes, uint32_t& capacity);
+};
+
+static_assert(std::is_standard_layout<IpcV2SlotHeader>::value,
+ "IPC_V2 header must have a predictable layout");
+static_assert(sizeof(IpcV2SlotHeader) == IpcV2Slot::PAYLOAD_OFFSET,
+ "IPC_V2 header must occupy 16 bytes");
+static_assert(offsetof(IpcV2SlotHeader, state) == 0,
+ "Unexpected IPC_V2 state offset");
+static_assert(offsetof(IpcV2SlotHeader, payload_len) == 4,
+ "Unexpected IPC_V2 payload length offset");
+static_assert(offsetof(IpcV2SlotHeader, flags) == 8,
+ "Unexpected IPC_V2 flags offset");
+static_assert(offsetof(IpcV2SlotHeader, reserved) == 12,
+ "Unexpected IPC_V2 reserved offset");
+static_assert(std::is_standard_layout<IpcV2Slot>::value,
+ "IPC_V2 slot must have a predictable layout");
+static_assert(offsetof(IpcV2Slot, header) == 0,
+ "IPC_V2 header must be first");
+static_assert(offsetof(IpcV2Slot, payload) == IpcV2Slot::PAYLOAD_OFFSET,
+ "Unexpected IPC_V2 payload offset");
+static_assert(sizeof(IpcV2Slot) == IpcV2Slot::SLOT_SIZE,
+ "IPC_V2 slot must occupy 4096 bytes");
+static_assert(alignof(IpcV2Slot) == 64,
+ "IPC_V2 slot must be 64-byte aligned");
+static_assert(__atomic_always_lock_free(sizeof(uint32_t), nullptr),
+ "IPC_V2 slot state requires lock-free 32-bit atomics");
+
+bool ValidateIpcV2Iov(const struct iovec* iov, int iovcnt, size_t& total);
+
+// These views implement only one SPSC direction. UBRing owns notification,
+// activation, teardown and shared-memory lifetime management.
+class IpcV2TxView {
+public:
+ IpcV2TxView(IpcV2Slot* slots, uint32_t* tail, uint32_t capacity,
+ uint32_t* write_pos)
+ : _slots(slots), _tail(tail), _capacity(capacity),
+ _write_pos(write_pos) {}
+
+ IpcV2RingResult TryWritev(const struct iovec* iov, int iovcnt,
+ size_t& written);
+
+private:
+ IpcV2TxView(const IpcV2TxView&) = delete;
+ IpcV2TxView& operator=(const IpcV2TxView&) = delete;
+
+ IpcV2Slot* _slots;
+ uint32_t* _tail;
+ uint32_t _capacity;
+ uint32_t* _write_pos;
+};
+
+class IpcV2RxView {
+public:
+ IpcV2RxView(IpcV2Slot* slots, uint32_t* tail, uint32_t capacity,
+ uint32_t* read_pos, uint32_t* read_offset)
+ : _slots(slots), _tail(tail), _capacity(capacity),
+ _read_pos(read_pos), _read_offset(read_offset) {}
+
+ static bool InitializeShared(IpcV2Slot* slots, uint32_t* tail,
+ uint32_t capacity);
+
+ IpcV2RingResult TryReadv(const struct iovec* iov, int iovcnt,
+ size_t& read, bool& batch_end);
+
+private:
+ IpcV2RxView(const IpcV2RxView&) = delete;
+ IpcV2RxView& operator=(const IpcV2RxView&) = delete;
+
+ IpcV2Slot* _slots;
+ uint32_t* _tail;
+ uint32_t _capacity;
+ uint32_t* _read_pos;
+ uint32_t* _read_offset;
+};
+
+struct IpcV2QueueView {
+ IpcV2QueueView() : slots(nullptr), tail(nullptr), capacity(0) {}
+
+ IpcV2Slot* slots;
+ uint32_t* tail;
+ uint32_t capacity;
+};
+
+// Adapt an existing UBRing DataQ and its existing status tail to IPC_V2.
+// No shared memory is modified unless initialization succeeds.
+bool MapIpcV2Queue(uint8_t* data_addr, size_t data_len,
+ uint32_t* tail, IpcV2QueueView& queue);
+
+bool InitializeIpcV2ReceiveQueue(uint8_t* data_addr, size_t data_len,
+ uint32_t* tail, IpcV2QueueView& queue);
+
+} // namespace ubring
+} // namespace brpc
+
+#endif // BRPC_UBSHM_UBR_MSG_V2_H
diff --git a/src/brpc/ubshm/ubr_trx.h b/src/brpc/ubshm/ubr_trx.h
index b4e89645..4111b8ee 100644
--- a/src/brpc/ubshm/ubr_trx.h
+++ b/src/brpc/ubshm/ubr_trx.h
@@ -23,6 +23,7 @@
#include "brpc/ubshm/shm/shm_def.h"
#include "brpc/ubshm/common/common.h"
#include "brpc/ubshm/common/thread_lock.h"
+#include "brpc/ubshm/ubr_data_format.h"
#include "brpc/ubshm/ubr_msg.h"
/*
+----------------------------------------------------------------------------+
@@ -124,6 +125,7 @@ typedef struct TagUbrRx {
uint32_t deal_msg_num;
uint32_t deal_msg_max_cnt;
uint32_t ep_eof_pos;
+ uint32_t ipc_v2_read_offset;
volatile EventQState trx_state;
} UbrRx;
@@ -133,6 +135,7 @@ typedef struct TagUbrTrx {
uint64_t ubr_id;
uint32_t trx_mgr_index;
UbrTrxType type;
+ UbrDataFormat data_format;
SHM local_shm;
SHM remote_shm;
int timer_fd;
@@ -159,4 +162,4 @@ typedef enum {
}
}
-#endif //BRPC_UBR_TRX_H
\ No newline at end of file
+#endif //BRPC_UBR_TRX_H
diff --git a/test/brpc_ubring_unittest.cpp b/test/brpc_ubring_unittest.cpp
index 295d3f12..c9b9c91d 100644
--- a/test/brpc_ubring_unittest.cpp
+++ b/test/brpc_ubring_unittest.cpp
@@ -15,9 +15,14 @@
// limitations under the License.
#include <gtest/gtest.h>
+#include <algorithm>
+#include <atomic>
+#include <chrono>
#include <cstring>
+#include <limits>
#include <gflags/gflags.h>
#include <string>
+#include <thread>
#include "butil/macros.h"
#include "butil/sys_byteorder.h"
#include "brpc/socket.h"
@@ -30,6 +35,7 @@
#include "brpc/ubshm/ub_ring_manager.h"
#include "brpc/ubshm/ub_ring.h"
#include "brpc/ubshm/ubr_msg.h"
+#include "brpc/ubshm/ubr_msg_v2.h"
namespace brpc {
namespace ubring {
@@ -44,6 +50,542 @@ extern bool g_skip_ub_init;
} // namespace brpc
namespace {
+TEST(IpcV2LayoutTest, fixed_layout) {
+ using brpc::ubring::IpcV2Slot;
+
+ EXPECT_EQ(4096u, sizeof(IpcV2Slot));
+ EXPECT_EQ(64u, alignof(IpcV2Slot));
+ EXPECT_EQ(16u, offsetof(IpcV2Slot, payload));
+ EXPECT_EQ(4080u, sizeof(((IpcV2Slot*)nullptr)->payload));
+ EXPECT_EQ(0, brpc::ubring::UBR_DATA_FORMAT_NONE);
+ EXPECT_EQ(1, brpc::ubring::UBR_DATA_FORMAT_LEGACY_64);
+ EXPECT_EQ(2, brpc::ubring::UBR_DATA_FORMAT_IPC_V2);
+}
+
+TEST(IpcV2RingTest, fixed_writev_readv_boundaries_and_wrap) {
+ using namespace brpc::ubring;
+ IpcV2Slot slots[4] = {};
+ uint32_t tail = 0;
+ uint32_t write_pos = 0;
+ uint32_t read_pos = 0;
+ uint32_t read_offset = 0;
+ IpcV2TxView tx(slots, &tail, 4, &write_pos);
+ IpcV2RxView rx(slots, &tail, 4, &read_pos, &read_offset);
+ EXPECT_EQ(IPC_V2_SLOT_UNINITIALIZED,
+ __atomic_load_n(&slots[0].header.state, __ATOMIC_ACQUIRE));
+ ASSERT_TRUE(IpcV2RxView::InitializeShared(slots, &tail, 4));
+ EXPECT_EQ(3u, __atomic_load_n(&tail, __ATOMIC_ACQUIRE));
+
+ std::string source(IpcV2Slot::PAYLOAD_SIZE + 19, '\0');
+ for (size_t i = 0; i < source.size(); ++i) {
+ source[i] = static_cast<char>((i * 29 + 7) & 0xff);
+ }
+ struct iovec write_iov[4] = {
+ {nullptr, 0},
+ {&source[0], 7},
+ {&source[7], IpcV2Slot::PAYLOAD_SIZE - 3},
+ {&source[IpcV2Slot::PAYLOAD_SIZE + 4], 15},
+ };
+ size_t written = 99;
+ ASSERT_EQ(IPC_V2_RING_OK, tx.TryWritev(write_iov, 4, written));
+ EXPECT_EQ(source.size(), written);
+ EXPECT_EQ(2u, write_pos);
+ EXPECT_EQ(IpcV2Slot::PAYLOAD_SIZE, slots[0].header.payload_len);
+ EXPECT_EQ(0u, slots[0].header.flags);
+ EXPECT_EQ(19u, slots[1].header.payload_len);
+ EXPECT_EQ(static_cast<uint32_t>(IPC_V2_SLOT_EOF), slots[1].header.flags);
+
+ std::string output(source.size(), '?');
+ struct iovec first_read = {&output[0], 31};
+ size_t read = 99;
+ bool batch_end = true;
+ ASSERT_EQ(IPC_V2_RING_OK, rx.TryReadv(&first_read, 1, read, batch_end));
+ EXPECT_EQ(31u, read);
+ EXPECT_FALSE(batch_end);
+ EXPECT_EQ(0u, read_pos);
+ EXPECT_EQ(31u, read_offset);
+ EXPECT_EQ(3u, __atomic_load_n(&tail, __ATOMIC_ACQUIRE));
+ struct iovec remaining_iov[4] = {
+ {nullptr, 0},
+ {&output[31], 5},
+ {&output[36], IpcV2Slot::PAYLOAD_SIZE - 20},
+ {&output[IpcV2Slot::PAYLOAD_SIZE + 16], 3},
+ };
+ ASSERT_EQ(IPC_V2_RING_OK, rx.TryReadv(remaining_iov, 4, read, batch_end));
+ EXPECT_EQ(source.size() - 31, read);
+ EXPECT_TRUE(batch_end);
+ EXPECT_EQ(source, output);
+ EXPECT_EQ(2u, read_pos);
+ EXPECT_EQ(0u, read_offset);
+ EXPECT_EQ(1u, __atomic_load_n(&tail, __ATOMIC_ACQUIRE));
+
+ std::string oversized(IpcV2Slot::PAYLOAD_SIZE * 4, 'x');
+ struct iovec oversized_iov = {&oversized[0], oversized.size()};
+ EXPECT_EQ(IPC_V2_RING_TOO_LARGE,
+ tx.TryWritev(&oversized_iov, 1, written));
+ EXPECT_EQ(0u, written);
+
+ char values[4] = {'a', 'b', 'c', 'd'};
+ for (int i = 0; i < 3; ++i) {
+ struct iovec item = {&values[i], 1};
+ ASSERT_EQ(IPC_V2_RING_OK, tx.TryWritev(&item, 1, written));
+ }
+ struct iovec fourth = {&values[3], 1};
+ EXPECT_EQ(IPC_V2_RING_RETRY, tx.TryWritev(&fourth, 1, written));
+ EXPECT_EQ(0u, written);
+ char result[4] = {};
+ struct iovec one = {&result[0], 1};
+ ASSERT_EQ(IPC_V2_RING_OK, rx.TryReadv(&one, 1, read, batch_end));
+ ASSERT_TRUE(batch_end);
+ ASSERT_EQ(IPC_V2_RING_OK, tx.TryWritev(&fourth, 1, written));
+ for (int i = 1; i < 4; ++i) {
+ struct iovec destination = {&result[i], 1};
+ ASSERT_EQ(IPC_V2_RING_OK,
+ rx.TryReadv(&destination, 1, read, batch_end));
+ ASSERT_TRUE(batch_end);
+ }
+ EXPECT_EQ(0, memcmp(values, result, sizeof(values)));
+}
+
+TEST(IpcV2RingTest, rejects_invalid_iovec_without_publication) {
+ using namespace brpc::ubring;
+ IpcV2Slot slots[2] = {};
+ uint32_t tail = 0;
+ uint32_t write_pos = 0;
+ uint32_t read_pos = 0;
+ uint32_t read_offset = 0;
+ IpcV2TxView tx(slots, &tail, 2, &write_pos);
+ IpcV2RxView rx(slots, &tail, 2, &read_pos, &read_offset);
+ size_t bytes = 99;
+ bool batch_end = true;
+ char value = 'x';
+ struct iovec one_byte = {&value, 1};
+ EXPECT_EQ(IPC_V2_RING_INVALID, tx.TryWritev(&one_byte, 1, bytes));
+ EXPECT_EQ(IPC_V2_RING_INVALID,
+ rx.TryReadv(&one_byte, 1, bytes, batch_end));
+ ASSERT_TRUE(IpcV2RxView::InitializeShared(slots, &tail, 2));
+ EXPECT_EQ(IPC_V2_RING_INVALID, tx.TryWritev(nullptr, 1, bytes));
+ EXPECT_EQ(IPC_V2_RING_INVALID, tx.TryWritev(nullptr, -1, bytes));
+ EXPECT_EQ(IPC_V2_RING_INVALID,
+ rx.TryReadv(nullptr, 1, bytes, batch_end));
+ struct iovec null_data = {nullptr, 1};
+ EXPECT_EQ(IPC_V2_RING_INVALID, tx.TryWritev(&null_data, 1, bytes));
+ EXPECT_EQ(IPC_V2_RING_OK, tx.TryWritev(nullptr, 0, bytes));
+ EXPECT_EQ(0u, bytes);
+ EXPECT_EQ(IPC_V2_RING_OK, rx.TryReadv(nullptr, 0, bytes, batch_end));
+ EXPECT_EQ(0u, bytes);
+ EXPECT_FALSE(batch_end);
+ EXPECT_EQ(IPC_V2_SLOT_EMPTY,
+ __atomic_load_n(&slots[0].header.state, __ATOMIC_ACQUIRE));
+}
+
+TEST(IpcV2RingTest, concurrent_writev_readv) {
+ using namespace brpc::ubring;
+ IpcV2Slot slots[8] = {};
+ uint32_t tail = 0;
+ uint32_t write_pos = 0;
+ uint32_t read_pos = 0;
+ uint32_t read_offset = 0;
+ ASSERT_TRUE(IpcV2RxView::InitializeShared(slots, &tail, 8));
+ IpcV2TxView tx(slots, &tail, 8, &write_pos);
+ IpcV2RxView rx(slots, &tail, 8, &read_pos, &read_offset);
+ const uint32_t rounds = 200;
+ const auto deadline = std::chrono::steady_clock::now() +
+ std::chrono::seconds(10);
+ std::atomic<bool> stop(false);
+ std::string producer_error;
+ std::string consumer_error;
+ const auto message_size = [](uint32_t sequence) {
+ return 1 + (sequence * 53) % (IpcV2Slot::PAYLOAD_SIZE * 2);
+ };
+ const auto fill = [](uint32_t sequence, char* data, size_t length) {
+ for (size_t i = 0; i < length; ++i) {
+ data[i] = static_cast<char>((sequence * 11 + i * 37) & 0xff);
+ }
+ };
+ std::thread producer([&] {
+ std::string input(IpcV2Slot::PAYLOAD_SIZE * 2, '\0');
+ for (uint32_t sequence = 0; sequence < rounds; ++sequence) {
+ const size_t length = message_size(sequence);
+ fill(sequence, &input[0], length);
+ const size_t first = length < 3 ? length : 3;
+ struct iovec parts[3] = {
+ {&input[0], first},
+ {nullptr, 0},
+ {&input[first], length - first},
+ };
+ for (;;) {
+ size_t written = 0;
+ const IpcV2RingResult result = tx.TryWritev(parts, 3, written);
+ if (result == IPC_V2_RING_OK && written == length) {
+ break;
+ }
+ if (result != IPC_V2_RING_RETRY ||
+ stop.load(std::memory_order_relaxed) ||
+ std::chrono::steady_clock::now() >= deadline) {
+ producer_error = "valid writev failed or timed out";
+ stop.store(true, std::memory_order_relaxed);
+ return;
+ }
+ std::this_thread::yield();
+ }
+ }
+ });
+
+ std::string output(IpcV2Slot::PAYLOAD_SIZE * 2, '\0');
+ std::string expected(IpcV2Slot::PAYLOAD_SIZE * 2, '\0');
+ for (uint32_t sequence = 0; sequence < rounds; ++sequence) {
+ const size_t length = message_size(sequence);
+ fill(sequence, &expected[0], length);
+ size_t received = 0;
+ bool ended = false;
+ while (!ended && !stop.load(std::memory_order_relaxed)) {
+ const size_t chunk = std::min(size_t(113), length - received);
+ struct iovec destination = {&output[received], chunk};
+ size_t read = 0;
+ const IpcV2RingResult result =
+ rx.TryReadv(&destination, 1, read, ended);
+ if (result == IPC_V2_RING_RETRY) {
+ if (std::chrono::steady_clock::now() >= deadline) {
+ consumer_error = "consumer timed out";
+ break;
+ }
+ std::this_thread::yield();
+ continue;
+ }
+ if (result != IPC_V2_RING_OK || read == 0 || read > chunk) {
+ consumer_error = "valid readv failed";
+ break;
+ }
+ received += read;
+ }
+ if (!consumer_error.empty() || stop.load(std::memory_order_relaxed)) {
+ break;
+ }
+ if (received != length ||
+ memcmp(output.data(), expected.data(), length) != 0) {
+ consumer_error = "batch length, order or content mismatch";
+ break;
+ }
+ }
+ stop.store(true, std::memory_order_relaxed);
+ producer.join();
+ EXPECT_TRUE(producer_error.empty()) << producer_error;
+ EXPECT_TRUE(consumer_error.empty()) << consumer_error;
+}
+
+TEST(IpcV2QueueMappingTest, reuses_existing_tail_and_preserves_neighbors) {
+ using namespace brpc::ubring;
+ struct alignas(64) QueueStorage {
+ IpcV2Slot slots[2];
+ uint8_t trailing[31];
+ } storage;
+ memset(&storage, 0xa5, sizeof(storage));
+ UbrDataStatusQMsg status = {};
+ status.tail = 99;
+ status.timeout = 12345;
+ status.heart_beat = 7;
+
+ IpcV2QueueView queue;
+ ASSERT_TRUE(InitializeIpcV2ReceiveQueue(
+ reinterpret_cast<uint8_t*>(storage.slots), sizeof(storage),
+ &status.tail, queue));
+ EXPECT_EQ(storage.slots, queue.slots);
+ EXPECT_EQ(&status.tail, queue.tail);
+ EXPECT_EQ(2u, queue.capacity);
+ EXPECT_EQ(1u, __atomic_load_n(&status.tail, __ATOMIC_ACQUIRE));
+ EXPECT_EQ(12345u, status.timeout);
+ EXPECT_EQ(7u, status.heart_beat);
+ EXPECT_EQ(IPC_V2_SLOT_EMPTY,
+ __atomic_load_n(&storage.slots[0].header.state,
+ __ATOMIC_ACQUIRE));
+ EXPECT_EQ(IPC_V2_SLOT_EMPTY,
+ __atomic_load_n(&storage.slots[1].header.state,
+ __ATOMIC_ACQUIRE));
+ const uint8_t* trailing = reinterpret_cast<const uint8_t*>(&storage) +
+ sizeof(storage.slots);
+ for (size_t i = 0; i < sizeof(storage) - sizeof(storage.slots); ++i) {
+ EXPECT_EQ(0xa5, trailing[i]);
+ }
+}
+
+TEST(IpcV2QueueMappingTest, rejects_invalid_layout_without_modification) {
+ using namespace brpc::ubring;
+ IpcV2Slot slots[2] = {};
+ uint32_t tail = 77;
+ IpcV2QueueView queue;
+ EXPECT_FALSE(MapIpcV2Queue(nullptr, sizeof(slots), &tail, queue));
+ EXPECT_FALSE(MapIpcV2Queue(
+ reinterpret_cast<uint8_t*>(slots), sizeof(IpcV2Slot), &tail, queue));
+ EXPECT_FALSE(MapIpcV2Queue(
+ reinterpret_cast<uint8_t*>(slots) + 1, sizeof(slots) - 1,
+ &tail, queue));
+ alignas(uint32_t) uint8_t tail_storage[sizeof(uint32_t) + 1] = {};
+ EXPECT_FALSE(MapIpcV2Queue(
+ reinterpret_cast<uint8_t*>(slots), sizeof(slots),
+ reinterpret_cast<uint32_t*>(tail_storage + 1), queue));
+ EXPECT_EQ(nullptr, queue.slots);
+ EXPECT_EQ(nullptr, queue.tail);
+ EXPECT_EQ(0u, queue.capacity);
+ EXPECT_EQ(77u, tail);
+}
+
+TEST(IpcV2FormatPreparationTest, prepares_layout_without_activation) {
+ using namespace brpc::ubring;
+ IpcV2Slot local_slots[2] = {};
+ IpcV2Slot remote_slots[3] = {};
+ UbrDataStatusQMsg local_status = {};
+ UbrDataStatusQMsg remote_status = {};
+ local_status.tail = 91;
+ local_status.timeout = 111;
+ local_status.heart_beat = 3;
+ remote_status.tail = 92;
+ remote_status.timeout = 222;
+ remote_status.heart_beat = 4;
+ UbrTrx trx = {};
+ trx.ubr_tx.remote_data_q.addr =
+ reinterpret_cast<uint8_t*>(remote_slots);
+ trx.ubr_tx.remote_data_q.len = sizeof(remote_slots);
+ trx.ubr_tx.local_data_status_q.addr =
+ reinterpret_cast<uint8_t*>(&local_status);
+ trx.ubr_rx.local_data_q.addr = reinterpret_cast<uint8_t*>(local_slots);
+ trx.ubr_rx.local_data_q.len = sizeof(local_slots);
+ trx.ubr_rx.remote_data_status_q.addr =
+ reinterpret_cast<uint8_t*>(&remote_status);
+
+ UBRing ring;
+ ring.SetTrxForTest(&trx);
+ ASSERT_EQ(0, ring.UbrPrepareIpcV2Format());
+ EXPECT_EQ(UBR_DATA_FORMAT_IPC_V2, ring.data_format());
+ EXPECT_EQ(3u, trx.ubr_tx.capacity);
+ EXPECT_EQ(0u, trx.ubr_tx.write_pos);
+ EXPECT_EQ(2u, trx.ubr_rx.capacity);
+ EXPECT_EQ(0u, trx.ubr_rx.read_pos);
+ EXPECT_EQ(0u, trx.ubr_rx.ipc_v2_read_offset);
+ EXPECT_EQ(UBR_STATE_NONE, trx.ubr_tx.trx_state);
+ EXPECT_EQ(UBR_STATE_NONE, trx.ubr_rx.trx_state);
+ EXPECT_EQ(0, trx.timer_fd);
+ EXPECT_EQ(0, trx.hb_timer_fd);
+ EXPECT_EQ(91u, local_status.tail);
+ EXPECT_EQ(1u, __atomic_load_n(&remote_status.tail, __ATOMIC_ACQUIRE));
+ EXPECT_EQ(111u, local_status.timeout);
+ EXPECT_EQ(3u, local_status.heart_beat);
+ EXPECT_EQ(222u, remote_status.timeout);
+ EXPECT_EQ(4u, remote_status.heart_beat);
+ EXPECT_EQ(IPC_V2_SLOT_EMPTY,
+ __atomic_load_n(&local_slots[0].header.state,
+ __ATOMIC_ACQUIRE));
+ EXPECT_EQ(IPC_V2_SLOT_UNINITIALIZED,
+ __atomic_load_n(&remote_slots[0].header.state,
+ __ATOMIC_ACQUIRE));
+ EXPECT_EQ(-1, ring.UbrPrepareIpcV2Format());
+}
+
+TEST(IpcV2FormatPreparationTest, failure_keeps_connection_unselected) {
+ using namespace brpc::ubring;
+ alignas(64) uint8_t local_storage[sizeof(IpcV2Slot) * 2 + 1] = {};
+ IpcV2Slot remote_slots[2] = {};
+ UbrDataStatusQMsg local_status = {};
+ UbrDataStatusQMsg remote_status = {};
+ local_status.tail = 71;
+ remote_status.tail = 72;
+ UbrTrx trx = {};
+ trx.ubr_tx.remote_data_q.addr =
+ reinterpret_cast<uint8_t*>(remote_slots);
+ trx.ubr_tx.remote_data_q.len = sizeof(remote_slots);
+ trx.ubr_tx.local_data_status_q.addr =
+ reinterpret_cast<uint8_t*>(&local_status);
+ trx.ubr_rx.local_data_q.addr = local_storage + 1;
+ trx.ubr_rx.local_data_q.len = sizeof(local_storage) - 1;
+ trx.ubr_rx.remote_data_status_q.addr =
+ reinterpret_cast<uint8_t*>(&remote_status);
+
+ UBRing ring;
+ ring.SetTrxForTest(&trx);
+ EXPECT_EQ(-1, ring.UbrPrepareIpcV2Format());
+ EXPECT_EQ(UBR_DATA_FORMAT_NONE, ring.data_format());
+ EXPECT_EQ(0u, trx.ubr_tx.capacity);
+ EXPECT_EQ(0u, trx.ubr_rx.capacity);
+ EXPECT_EQ(71u, local_status.tail);
+ EXPECT_EQ(72u, remote_status.tail);
+}
+
+struct IpcV2UbrTestContext {
+ IpcV2UbrTestContext() {
+ memset(local_slots, 0, sizeof(local_slots));
+ memset(remote_slots, 0, sizeof(remote_slots));
+ memset(&local_status, 0, sizeof(local_status));
+ memset(&remote_status, 0, sizeof(remote_status));
+ memset(&local_rx_event, 0, sizeof(local_rx_event));
+ memset(&local_tx_event, 0, sizeof(local_tx_event));
+ memset(&remote_rx_event, 0, sizeof(remote_rx_event));
+ trx.local_shm.addr = reinterpret_cast<uint8_t*>(local_slots);
+ trx.ubr_tx.remote_data_q.addr =
+ reinterpret_cast<uint8_t*>(remote_slots);
+ trx.ubr_tx.remote_data_q.len = sizeof(remote_slots);
+ trx.ubr_tx.local_data_status_q.addr =
+ reinterpret_cast<uint8_t*>(&local_status);
+ trx.ubr_tx.local_tx_event_q.addr =
+ reinterpret_cast<uint8_t*>(&local_tx_event);
+ trx.ubr_tx.remote_rx_event_q.addr =
+ reinterpret_cast<uint8_t*>(&remote_rx_event);
+ trx.ubr_rx.local_data_q.addr =
+ reinterpret_cast<uint8_t*>(local_slots);
+ trx.ubr_rx.local_data_q.len = sizeof(local_slots);
+ trx.ubr_rx.remote_data_status_q.addr =
+ reinterpret_cast<uint8_t*>(&remote_status);
+ trx.ubr_rx.local_rx_event_q.addr =
+ reinterpret_cast<uint8_t*>(&local_rx_event);
+ ring.SetTrxForTest(&trx);
+ }
+
+ bool PrepareAndConnect() {
+ if (ring.UbrPrepareIpcV2Format() != 0 ||
+ !brpc::ubring::IpcV2RxView::InitializeShared(
+ remote_slots, &local_status.tail, CAPACITY)) {
+ return false;
+ }
+ trx.ubr_tx.trx_state = brpc::ubring::UBR_STATE_CONNECTED;
+ trx.ubr_rx.trx_state = brpc::ubring::UBR_STATE_CONNECTED;
+ return true;
+ }
+
+ enum { CAPACITY = 4 };
+ brpc::ubring::IpcV2Slot local_slots[CAPACITY];
+ brpc::ubring::IpcV2Slot remote_slots[CAPACITY];
+ brpc::ubring::UbrDataStatusQMsg local_status;
+ brpc::ubring::UbrDataStatusQMsg remote_status;
+ brpc::ubring::UbrEventQMsg local_rx_event;
+ brpc::ubring::UbrEventQMsg local_tx_event;
+ brpc::ubring::UbrEventQMsg remote_rx_event;
+ brpc::ubring::UbrTrx trx{};
+ brpc::ubring::UBRing ring;
+};
+
+TEST(IpcV2UbrDispatchTest, writev_readv_and_notifications) {
+ using namespace brpc::ubring;
+ IpcV2UbrTestContext context;
+ ASSERT_TRUE(context.PrepareAndConnect());
+ EXPECT_EQ(UBRING_OK, context.ring.IsUbrTrxWriteable(0));
+
+ std::string source(IpcV2Slot::PAYLOAD_SIZE + 19, '\0');
+ for (size_t i = 0; i < source.size(); ++i) {
+ source[i] = static_cast<char>((i * 13 + 5) & 0xff);
+ }
+ struct iovec source_iov[2] = {
+ {&source[0], 17},
+ {&source[17], source.size() - 17},
+ };
+ ASSERT_EQ(static_cast<ssize_t>(source.size()),
+ context.ring.UbrTrxWritev(source_iov, 2));
+ EXPECT_EQ(1u, context.trx.ubr_tx.out_io_id);
+ EXPECT_EQ(1u, context.remote_rx_event.io_id);
+
+ uint32_t peer_read_pos = 0;
+ uint32_t peer_read_offset = 0;
+ IpcV2RxView peer_rx(
+ context.remote_slots, &context.local_status.tail,
+ IpcV2UbrTestContext::CAPACITY, &peer_read_pos, &peer_read_offset);
+ std::string peer_output(source.size(), '?');
+ struct iovec peer_destination = {&peer_output[0], peer_output.size()};
+ size_t peer_read = 0;
+ bool peer_batch_end = false;
+ ASSERT_EQ(IPC_V2_RING_OK,
+ peer_rx.TryReadv(&peer_destination, 1, peer_read,
+ peer_batch_end));
+ EXPECT_TRUE(peer_batch_end);
+ EXPECT_EQ(source, peer_output);
+
+ char empty_value = 0;
+ struct iovec empty_destination = {&empty_value, 1};
+ errno = 0;
+ EXPECT_EQ(-1, context.ring.UbrTrxReadv(&empty_destination, 1));
+ EXPECT_EQ(EAGAIN, errno);
+ EXPECT_EQ(MPA_MUXER_NOT_READY, context.ring.IsUbrTrxReadable(0));
+
+ uint32_t peer_write_pos = 0;
+ IpcV2TxView peer_tx(
+ context.local_slots, &context.remote_status.tail,
+ IpcV2UbrTestContext::CAPACITY, &peer_write_pos);
+ size_t peer_written = 0;
+ ASSERT_EQ(IPC_V2_RING_OK,
+ peer_tx.TryWritev(source_iov, 2, peer_written));
+ context.local_rx_event.io_id = 1;
+ EXPECT_EQ(UBRING_OK, context.ring.IsUbrTrxReadable(0));
+
+ std::string output(source.size(), '?');
+ struct iovec first = {&output[0], 31};
+ ASSERT_EQ(31, context.ring.UbrTrxReadv(&first, 1));
+ EXPECT_EQ(0u, context.trx.ubr_rx.read_pos);
+ EXPECT_EQ(31u, context.trx.ubr_rx.ipc_v2_read_offset);
+ struct iovec remaining = {&output[31], output.size() - 31};
+ ASSERT_EQ(static_cast<ssize_t>(output.size() - 31),
+ context.ring.UbrTrxReadv(&remaining, 1));
+ EXPECT_EQ(2u, context.trx.ubr_rx.read_pos);
+ EXPECT_EQ(0u, context.trx.ubr_rx.ipc_v2_read_offset);
+ EXPECT_EQ(source, output);
+}
+
+TEST(IpcV2UbrDispatchTest, backpressure_and_error_mapping) {
+ using namespace brpc::ubring;
+ IpcV2UbrTestContext context;
+ ASSERT_TRUE(context.PrepareAndConnect());
+ char values[4] = {'a', 'b', 'c', 'd'};
+ for (int i = 0; i < 3; ++i) {
+ struct iovec source = {&values[i], 1};
+ ASSERT_EQ(1, context.ring.UbrTrxWritev(&source, 1));
+ }
+ const uint64_t notified_io_id = context.remote_rx_event.io_id;
+ struct iovec fourth = {&values[3], 1};
+ EXPECT_EQ(UBRING_RETRY, context.ring.UbrTrxWritev(&fourth, 1));
+ EXPECT_EQ(notified_io_id, context.remote_rx_event.io_id);
+ EXPECT_EQ(MPA_MUXER_NOT_READY,
+ context.ring.IsUbrTrxWriteable(0));
+
+ std::string oversized(
+ IpcV2Slot::PAYLOAD_SIZE * IpcV2UbrTestContext::CAPACITY, 'x');
+ struct iovec too_large = {&oversized[0], oversized.size()};
+ errno = 0;
+ EXPECT_EQ(-1, context.ring.UbrTrxWritev(&too_large, 1));
+ EXPECT_EQ(EMSGSIZE, errno);
+ EXPECT_EQ(notified_io_id, context.remote_rx_event.io_id);
+
+ context.local_slots[0].header.payload_len =
+ IpcV2Slot::PAYLOAD_SIZE + 1;
+ context.local_slots[0].header.flags = IPC_V2_SLOT_EOF;
+ context.local_slots[0].header.reserved = 0;
+ __atomic_store_n(&context.local_slots[0].header.state,
+ IPC_V2_SLOT_READY, __ATOMIC_RELEASE);
+ char destination = 0;
+ struct iovec invalid = {&destination, 1};
+ EXPECT_EQ(UBRING_OK, context.ring.IsUbrTrxReadable(0));
+ errno = 0;
+ EXPECT_EQ(-1, context.ring.UbrTrxReadv(&invalid, 1));
+ EXPECT_EQ(EBADMSG, errno);
+
+ context.trx.data_format = UBR_DATA_FORMAT_NONE;
+ errno = 0;
+ EXPECT_EQ(UBRING_ERR, context.ring.UbrTrxWritev(&fourth, 1));
+ EXPECT_EQ(EPROTONOSUPPORT, errno);
+}
+
+TEST(IpcV2UbrDispatchTest, rejects_uninitialized_remote_ring) {
+ using namespace brpc::ubring;
+ IpcV2UbrTestContext context;
+ ASSERT_EQ(0, context.ring.UbrPrepareIpcV2Format());
+ context.trx.ubr_tx.trx_state = UBR_STATE_CONNECTED;
+ context.trx.ubr_rx.trx_state = UBR_STATE_CONNECTED;
+
+ EXPECT_EQ(UBRING_ERR, context.ring.IsUbrTrxWriteable(0));
+ char value = 'x';
+ struct iovec source = {&value, 1};
+ errno = 0;
+ EXPECT_EQ(-1, context.ring.UbrTrxWritev(&source, 1));
+ EXPECT_EQ(EBADMSG, errno);
+ EXPECT_EQ(0u, context.trx.ubr_tx.out_io_id);
+ EXPECT_EQ(0u, context.remote_rx_event.io_id);
+}
+
struct __attribute__((packed)) HelloMessageLayout {
uint16_t msg_len;
uint16_t hello_ver;
@@ -76,6 +618,12 @@ TEST(HelloFormatExtensionTest,
serialize_deserialize_roundtrip) {
decoded.Deserialize(buffer);
EXPECT_EQ(extension.extension_len, decoded.extension_len);
EXPECT_EQ(extension.format_id, decoded.format_id);
+
+ extension.format_id = brpc::ubring::UBR_DATA_FORMAT_IPC_V2;
+ extension.Serialize(buffer);
+ decoded.Deserialize(buffer);
+ EXPECT_EQ(extension.extension_len, decoded.extension_len);
+ EXPECT_EQ(extension.format_id, decoded.format_id);
}
TEST(HelloFormatExtensionTest, serialize_uses_network_byte_order) {
@@ -108,6 +656,41 @@ TEST(HelloFormatExtensionTest, deserialize_unknown_format)
{
EXPECT_EQ(0x1234, extension.format_id);
}
+TEST(HelloFormatExtensionTest, selects_format_for_local_backend) {
+ using namespace brpc::ubring;
+ EXPECT_EQ(UBR_DATA_FORMAT_IPC_V2,
+ PreferredDataFormatForShmType(SHM_TYPE_IPC));
+ EXPECT_EQ(UBR_DATA_FORMAT_LEGACY_64,
+ PreferredDataFormatForShmType(SHM_TYPE_UBS));
+ EXPECT_EQ(UBR_DATA_FORMAT_NONE,
+ PreferredDataFormatForShmType(SHM_TYPE_UB));
+ EXPECT_EQ(UBR_DATA_FORMAT_NONE,
+ PreferredDataFormatForShmType(SHM_TYPE_UNSUPPORT));
+}
+
+TEST(HelloFormatExtensionTest, selects_only_matching_supported_format) {
+ using namespace brpc::ubring;
+ EXPECT_EQ(UBR_DATA_FORMAT_IPC_V2,
+ SelectDataFormat(UBR_DATA_FORMAT_IPC_V2,
+ UBR_DATA_FORMAT_IPC_V2));
+ EXPECT_EQ(UBR_DATA_FORMAT_LEGACY_64,
+ SelectDataFormat(UBR_DATA_FORMAT_LEGACY_64,
+ UBR_DATA_FORMAT_LEGACY_64));
+ EXPECT_EQ(UBR_DATA_FORMAT_NONE,
+ SelectDataFormat(UBR_DATA_FORMAT_IPC_V2,
+ UBR_DATA_FORMAT_LEGACY_64));
+ EXPECT_EQ(UBR_DATA_FORMAT_NONE,
+ SelectDataFormat(UBR_DATA_FORMAT_LEGACY_64,
+ UBR_DATA_FORMAT_IPC_V2));
+ EXPECT_EQ(UBR_DATA_FORMAT_NONE,
+ SelectDataFormat(UBR_DATA_FORMAT_IPC_V2, 0x1234));
+ EXPECT_EQ(UBR_DATA_FORMAT_NONE,
+ SelectDataFormat(UBR_DATA_FORMAT_NONE,
+ UBR_DATA_FORMAT_NONE));
+ EXPECT_EQ(UBR_DATA_FORMAT_NONE,
+ SelectDataFormat(static_cast<UbrDataFormat>(0x1234), 0x1234));
+}
+
TEST_F(HelloMessageTest, serialize_deserialize_roundtrip) {
msg.msg_len = 64;
msg.hello_ver = 2;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]