From f68c2ced7de2bdfc475a9282cde91a67d83325de Mon Sep 17 00:00:00 2001 From: "E. Scott Daniels" Date: Thu, 8 Aug 2019 10:15:43 -0400 Subject: [PATCH] Add transport provider status to message buffer This change adds a new field to the message buffer: tp_status. The field allows the underlying "errno" value set by the transport provider to be returned, in addition to the RMR status, as a part of the overall message. In some environments wrapper code (bindings) does not hav access to the errno value and thus was unable to have the additional information on send and receive failures. The new field constitutes a backwards compatable change to RMR's API, however the python wrapper API does not change. Minor version bump, but the wrapper will see only a patch level bump. Signed-off-by: E. Scott Daniels Change-Id: I1b8a863318e2b2515441f7339b41c2856e46c1e4 --- CHANGES | 10 ++++ CMakeLists.txt | 4 +- doc/src/man/rmr_alloc_msg.3.xfm | 55 +++++++++++++++------- src/bindings/rmr-python/Changelog.md | 4 ++ src/bindings/rmr-python/rmr/rmr.py | 13 ++--- src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py | 10 ++++ src/bindings/rmr-python/setup.py | 4 +- src/bindings/rmr-python/tox.ini | 2 +- src/rmr/common/include/rmr.h | 9 ++++ src/rmr/nng/src/rmr_nng.c | 24 ++++++++-- src/rmr/nng/src/sr_nng_static.c | 18 +++++++ test/rmr_nng_api_static_test.c | 4 ++ 12 files changed, 123 insertions(+), 34 deletions(-) diff --git a/CHANGES b/CHANGES index 955d4fd..3e1ae6d 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,16 @@ API and build change and fixe summaries. Doc correctsions and/or changes are not mentioned here; see the commit messages. +2019 August 8; version 1.1.0 (API change) + This change should be backward compatable/non-breaking + A new field has been added to the message buffer (rmr_mbuf_t). + This field (tp_state) is used to communicate the errno value + that the transport mechanism might set during send and/or + receive operations. C programmes should continue to use + errno directly, but in some environments wrappers may not be + able to access errno and this provides the value to them. + See the rmr_alloc_msg manual page for more details. + 2019 August 6; version 1.0.45 (build changes) Support for the Nanomsg transport library has been dropped. The library librmr.* will no longer be included in packages. diff --git a/CMakeLists.txt b/CMakeLists.txt index 094cb54..f1aab62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,8 +35,8 @@ project( rmr LANGUAGES C ) cmake_minimum_required( VERSION 3.5 ) set( major_version "1" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this -set( minor_version "0" ) -set( patch_level "45" ) +set( minor_version "1" ) +set( patch_level "0" ) set( install_root "${CMAKE_INSTALL_PREFIX}" ) set( install_inc "include/rmr" ) diff --git a/doc/src/man/rmr_alloc_msg.3.xfm b/doc/src/man/rmr_alloc_msg.3.xfm index cd9da7f..5377871 100644 --- a/doc/src/man/rmr_alloc_msg.3.xfm +++ b/doc/src/man/rmr_alloc_msg.3.xfm @@ -1,6 +1,6 @@ .if false ================================================================================== - Copyright (c) 2019 Nokia + Copyright (c) 2019 Nokia Copyright (c) 2018-2019 AT&T Intellectual Property. Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,17 +41,17 @@ rmr_mbuf_t* rmr_alloc_msg( void* ctx, int size ); &uindent &h2(DESCRIPTION) -The &cw(rmr_alloc_msg) function is used to allocate a buffer which the user +The &cw(rmr_alloc_msg) function is used to allocate a buffer which the user programme can write into and then send through the RMR library. The buffer is allocated such that sending it requires no additional copying -out of the buffer. +out of the buffer. If the value passed in &cw(size) is 0, then the default size supplied on the -&ital(rmr_init) call will be used. +&ital(rmr_init) call will be used. The &ital(ctx) parameter is the void context pointer that was returned by the &ital(rmr_init) function. &space -The pointer to the message buffer returned is a structure which has some +The pointer to the message buffer returned is a structure which has some user application visible fields; the structure is described in &cw(rmr.h,) and is illustrated below. @@ -63,52 +63,75 @@ typedef struct { int len; unsigned char* payload; unsigned char* xaction; + int sub_id; + int tp_state; } rmr_mbuf_t; &ex_end &space &beg_dlist(.75i : ^&bold_font ) -&ditem(state ) Is the current buffer state. Following a call to &cw(rmr_send_msg) +&ditem(state) Is the current buffer state. Following a call to &cw(rmr_send_msg) the state indicates whether the buffer was successfully sent which determines exactly what the payload points to. If the send failed, the payload referenced -by the buffer is the message that failed to send (allowing the application to -attempt a retransmission). +by the buffer is the message that failed to send (allowing the application to +attempt a retransmission). When the state is &cw(RMR_OK) the buffer represents an empty buffer that the application may fill in in preparation to send. &half_space -&ditem(mtype ) When sending a message, the application is expected to set this field +&ditem(mtype) When sending a message, the application is expected to set this field to the appropriate message type value (as determined by the user programme). Upon send this value determines how the RMR library will route the message. For a buffer which has been received, this field will contain the message type that was -set by the sending application. +set by the sending application. &half_space -&ditem(len ) The application using a buffer to send a message is expected to set the +&ditem(len) The application using a buffer to send a message is expected to set the length value to the actual number of bytes that it placed into the message. This is likely less than the total number of bytes that the message can carry. For a message buffer that is passed to the application as the result of a receive -call, this will be the value that the sending application supplied and should +call, this will be the value that the sending application supplied and should indicate the number of bytes in the payload which are valid. &half_space -&ditem(payload ) The payload is a pointer to the actual received data. The +&ditem(payload) The payload is a pointer to the actual received data. The user programme may read and write from/to the memory referenced by the payload up until the point in time that the buffer is used on a &cw(rmr_send, rmr_call) -or &cw(rmr_reply) function call. +or &cw(rmr_reply) function call. Once the buffer has been passed back to a RMR library function the user programme should &bold(NOT) make use of the payload pointer. &half_space -&ditem(xaction) The &ital(xaction) field is a pointer to a fixed sized area in -the message into which the user may write a transaction ID. +&ditem(xaction) The &ital(xaction) field is a pointer to a fixed sized area in +the message into which the user may write a transaction ID. The ID is optional with the exception of when the user application uses the &cw(rmr_call) function to send a message and wait for the reply; the underlying RMR processing expects that the matching reply message will also contain the same data in the &ital(xaction) field. &end_dlist +&half_space +&ditem(sub_id) +This value is the subscription ID. It, in combination with the message type is used +by rmr to determine the target endpoint when sending a message. +If the application to application protocol does not warrant the use of a subscription +ID, the RMR constant RMR_VOID_SUBID should be placed in this field. +When an application is forwarding or returning a buffer to the sender, it is the +application's responsibility to set/reset this value. + +&half_space +&ditem(tp_state) +For C applications making use of RMR, the state of a transport based failure will +often be available via &cw(errno.) +However, some wrapper environments may not have direct access to the C-lib &cw(errno) +value. +RMR send and receive operations will place the current value of &cw(errno) into this +field which should make it available to wrapper functions. +User applications are strongly cautioned against relying on the value of errno as +some transport mechanisms may not set this value on all calls. +This value should also be ignored any time the message status is &cw(RMR_OK.) + &h2(RETURN VALUE) The function returns a pointer to a &cw(rmr_mbuf) structure, or NULL on error. diff --git a/src/bindings/rmr-python/Changelog.md b/src/bindings/rmr-python/Changelog.md index a1c0836..101f9e8 100644 --- a/src/bindings/rmr-python/Changelog.md +++ b/src/bindings/rmr-python/Changelog.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.10.4] - 8/08/2019 + * Fix underlying problem getting errno from some environments; now references new RMR message field to get errno value. + * Add /usr/local/lib64 to tox environment variable to support systems where libraries natually install in lib64 rather than lib. + ## [0.10.3] - 7/31/2019 * (Correctly) Include license here per Jira RICPLT-1855 diff --git a/src/bindings/rmr-python/rmr/rmr.py b/src/bindings/rmr-python/rmr/rmr.py index bc65237..aeb9e18 100644 --- a/src/bindings/rmr-python/rmr/rmr.py +++ b/src/bindings/rmr-python/rmr/rmr.py @@ -18,7 +18,7 @@ import uuid import json from ctypes import RTLD_GLOBAL, Structure, c_int, POINTER, c_char, c_char_p, c_void_p, memmove, cast from ctypes import CDLL -from ctypes import create_string_buffer, pythonapi +from ctypes import create_string_buffer # https://docs.python.org/3.7/library/ctypes.html # https://stackoverflow.com/questions/2327344/ctypes-loading-a-c-shared-library-that-has-dependencies/30845750#30845750 @@ -89,13 +89,6 @@ def _state_to_status(stateno): return sdict.get(stateno, "UNKNOWN STATE") -def _errno(): - """Suss out the C error number value which might be useful in understanding - an underlying reason when RMr returns a failure. - """ - return c_int.in_dll(pythonapi, "errno").value - - ############## # PUBLIC API ############## @@ -116,6 +109,7 @@ class rmr_mbuf_t(Structure): unsigned char* payload; // transported data unsigned char* xaction; // pointer to fixed length transaction id bytes int sub_id; // subscription id + int tp_state; // transport state (a.k.a errno) // these things are off limits to the user application void* tp_buf; // underlying transport allocated pointer (e.g. nng message) void* header; // internal message header (whole buffer: header+payload) @@ -142,6 +136,7 @@ class rmr_mbuf_t(Structure): ), # according to th following the python bytes are already unsinged https://bytes.com/topic/python/answers/695078-ctypes-unsigned-char ("xaction", POINTER(c_char)), ("sub_id", c_int), + ("tp_state", c_int), ] @@ -292,7 +287,7 @@ def message_summary(ptr_to_rmr_buf_t): "payload max size": rmr_payload_size(ptr_to_rmr_buf_t), "meid": meid, "message source": get_src(ptr_to_rmr_buf_t), - "errno": _errno(), + "errno": ptr_to_rmr_buf_t.contents.tp_state, } diff --git a/src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py b/src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py index 395bfb3..6037e1f 100644 --- a/src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py +++ b/src/bindings/rmr-python/rmr/rmr_mocks/rmr_mocks.py @@ -34,6 +34,10 @@ def rcv_mock_generator(msg_payload, msg_type, msg_state, jsonb, timeout=0): sbuf.contents.payload = payload sbuf.contents.len = len(payload) sbuf.contents.state = msg_state + if msg_state != 0: # set something in transport state if 'error' + sbuf.contents.tp_state = 99 + else: + sbuf.contents.tp_state = 0 return sbuf return f @@ -48,6 +52,10 @@ def send_mock_generator(msg_state): def f(_unused, sbuf): sbuf.contents.state = msg_state + if msg_state != 0: # set something in transport state if 'error' + sbuf.contents.tp_state = 99 + else: + sbuf.contents.tp_state = 0 return sbuf return f @@ -63,6 +71,7 @@ class _Sbuf_Contents: self.payload = "" self.xaction = uuid.uuid1().hex.encode("utf-8") self.sub_id = 0 + self.tp_state = 0 def __str__(self): return str( @@ -73,6 +82,7 @@ class _Sbuf_Contents: "payload": self.payload, "xaction": self.xaction, "sub_id": self.sub_id, + "tp_state": self.tp_state, } ) diff --git a/src/bindings/rmr-python/setup.py b/src/bindings/rmr-python/setup.py index daa205e..1df9311 100644 --- a/src/bindings/rmr-python/setup.py +++ b/src/bindings/rmr-python/setup.py @@ -29,9 +29,9 @@ def _long_descr(): setup( name="rmr", - version="0.10.3", + version="0.10.4", packages=find_packages(), - author="Tommy Carpenter", + author="Tommy Carpenter, E. Scott Daniels", description="Python wrapper for RIC RMR", url="https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/lib/rmr", license="Apache 2.0", diff --git a/src/bindings/rmr-python/tox.ini b/src/bindings/rmr-python/tox.ini index c6f498d..d1ecb24 100644 --- a/src/bindings/rmr-python/tox.ini +++ b/src/bindings/rmr-python/tox.ini @@ -22,7 +22,7 @@ deps= pytest coverage pytest-cov -setenv = LD_LIBRARY_PATH = /usr/local/lib/ +setenv = LD_LIBRARY_PATH = /usr/local/lib/:/usr/local/lib64 commands=pytest --verbose --cov {envsitepackagesdir}/rmr --cov-report html [testenv:flake8] diff --git a/src/rmr/common/include/rmr.h b/src/rmr/common/include/rmr.h index a824a2a..5802cc6 100644 --- a/src/rmr/common/include/rmr.h +++ b/src/rmr/common/include/rmr.h @@ -78,6 +78,14 @@ extern "C" { (All fields are exposed such that if a wrapper needs to dup the storage as it passes into or out of their environment they dup it all, not just what we choose to expose.) + + NOTE: + State is the RMR state of processing on the message. The transport state (tp_state) + will be set to mirror the value of errno for wrappers unable to access errno directly, + but will only be set if state is not RMR_OK. Even then, the value may be suspect as + the underlying transport mechanism may not set errno. It is strongly recommended that + user applications use tp_state only for dianostic purposes to convey additional information + in a log message. */ typedef struct { int state; // state of processing @@ -86,6 +94,7 @@ typedef struct { unsigned char* payload; // transported data unsigned char* xaction; // pointer to fixed length transaction id bytes int sub_id; // subscription id + int tp_state; // transport state (errno) valid only if state != RMR_OK, and even then may not be valid // these things are off limits to the user application void* tp_buf; // underlying transport allocated pointer (e.g. nng message) diff --git a/src/rmr/nng/src/rmr_nng.c b/src/rmr/nng/src/rmr_nng.c index 7e0b37e..3529c46 100644 --- a/src/rmr/nng/src/rmr_nng.c +++ b/src/rmr/nng/src/rmr_nng.c @@ -254,6 +254,7 @@ extern rmr_mbuf_t* rmr_rts_msg( void* vctx, rmr_mbuf_t* msg ) { errno = EINVAL; // if msg is null, this is their clue if( msg != NULL ) { msg->state = RMR_ERR_BADARG; + msg->tp_state = errno; } return msg; } @@ -262,6 +263,7 @@ extern rmr_mbuf_t* rmr_rts_msg( void* vctx, rmr_mbuf_t* msg ) { if( msg->header == NULL ) { fprintf( stderr, "[ERR] rmr_send_msg: message had no header\n" ); msg->state = RMR_ERR_NOHDR; + msg->tp_state = errno; return msg; } @@ -345,6 +347,7 @@ extern rmr_mbuf_t* rmr_call( void* vctx, rmr_mbuf_t* msg ) { if( msg->state != RMR_ERR_RETRY ) { msg->state = RMR_ERR_CALLFAILED; // errno not available to all wrappers; don't stomp if marked retry } + msg->tp_state = errno; return msg; } @@ -365,10 +368,11 @@ extern rmr_mbuf_t* rmr_rcv_msg( void* vctx, rmr_mbuf_t* old_msg ) { rmr_mbuf_t* qm; // message that was queued on the ring if( (ctx = (uta_ctx_t *) vctx) == NULL ) { + errno = EINVAL; if( old_msg != NULL ) { old_msg->state = RMR_ERR_BADARG; + old_msg->tp_state = errno; } - errno = EINVAL; return old_msg; } errno = 0; @@ -402,10 +406,11 @@ extern rmr_mbuf_t* rmr_torcv_msg( void* vctx, rmr_mbuf_t* old_msg, int ms_to ) { rmr_mbuf_t* msg; if( (ctx = (uta_ctx_t *) vctx) == NULL ) { + errno = EINVAL; if( old_msg != NULL ) { old_msg->state = RMR_ERR_BADARG; + old_msg->tp_state = errno; } - errno = EINVAL; return old_msg; } @@ -457,6 +462,7 @@ extern rmr_mbuf_t* rmr_torcv_msg( void* vctx, rmr_mbuf_t* old_msg, int ms_to ) { nready = epoll_wait( eps->ep_fd, eps->events, 1, ms_to ); // block until something or timedout if( nready <= 0 ) { // we only wait on ours, so we assume ready means it's ours msg->state = RMR_ERR_TIMEOUT; + msg->tp_state = errno; } else { return rcv_msg( ctx, msg ); // receive it and return it } @@ -480,10 +486,11 @@ extern rmr_mbuf_t* rmr_rcv_specific( void* vctx, rmr_mbuf_t* msg, char* expect, int exp_len = 0; // length of expected ID if( (ctx = (uta_ctx_t *) vctx) == NULL ) { + errno = EINVAL; if( msg != NULL ) { msg->state = RMR_ERR_BADARG; + msg->tp_state = errno; } - errno = EINVAL; return msg; } @@ -827,6 +834,7 @@ extern rmr_mbuf_t* rmr_mt_rcv( void* vctx, rmr_mbuf_t* mbuf, int max_wait ) { errno = EINVAL; if( mbuf ) { mbuf->state = RMR_ERR_BADARG; + mbuf->tp_state = errno; } return mbuf; } @@ -835,6 +843,7 @@ extern rmr_mbuf_t* rmr_mt_rcv( void* vctx, rmr_mbuf_t* mbuf, int max_wait ) { errno = EINVAL; if( mbuf != NULL ) { mbuf->state = RMR_ERR_NOTSUPP; + mbuf->tp_state = errno; } return mbuf; } @@ -897,6 +906,9 @@ extern rmr_mbuf_t* rmr_mt_rcv( void* vctx, rmr_mbuf_t* mbuf, int max_wait ) { } } + if( mbuf ) { + mbuf->tp_state = errno; + } return mbuf; } @@ -928,9 +940,10 @@ extern rmr_mbuf_t* rmr_mt_call( void* vctx, rmr_mbuf_t* mbuf, int call_id, int m long nano_sec; // max wait xlated to nano seconds int state; + errno = EINVAL; if( (ctx = (uta_ctx_t *) vctx) == NULL || mbuf == NULL ) { - errno = EINVAL; if( mbuf ) { + mbuf->tp_state = errno; mbuf->state = RMR_ERR_BADARG; } return mbuf; @@ -938,11 +951,13 @@ extern rmr_mbuf_t* rmr_mt_call( void* vctx, rmr_mbuf_t* mbuf, int call_id, int m if( ! (ctx->flags & CFL_MTC_ENABLED) ) { mbuf->state = RMR_ERR_NOTSUPP; + mbuf->tp_state = errno; return mbuf; } if( call_id > MAX_CALL_ID || call_id < 2 ) { // 0 and 1 are reserved; user app cannot supply them mbuf->state = RMR_ERR_BADARG; + mbuf->tp_state = errno; return mbuf; } @@ -984,6 +999,7 @@ extern rmr_mbuf_t* rmr_mt_call( void* vctx, rmr_mbuf_t* mbuf, int call_id, int m mbuf = mtosend_msg( ctx, mbuf, 0 ); // use internal function so as not to strip call-id; should be nil on success! if( mbuf ) { if( mbuf->state != RMR_OK ) { + mbuf->tp_state = errno; return mbuf; // timeout or unable to connect or no endpoint are most likely issues } } diff --git a/src/rmr/nng/src/sr_nng_static.c b/src/rmr/nng/src/sr_nng_static.c index 79e6793..4a71826 100644 --- a/src/rmr/nng/src/sr_nng_static.c +++ b/src/rmr/nng/src/sr_nng_static.c @@ -402,6 +402,9 @@ static inline rmr_mbuf_t* realloc_msg( rmr_mbuf_t* old_msg, int tr_len ) { reuse. They have their reasons I guess. Thus, we will free the old transport buffer if user passes the message in; at least our mbuf will be reused. + + When msg->state is not ok, this function must set tp_state in the message as some API + fucntions return the message directly and do not propigate errno into the message. */ static rmr_mbuf_t* rcv_msg( uta_ctx_t* ctx, rmr_mbuf_t* old_msg ) { int state; @@ -428,11 +431,14 @@ static rmr_mbuf_t* rcv_msg( uta_ctx_t* ctx, rmr_mbuf_t* old_msg ) { msg->state = nng_recvmsg( ctx->nn_sock, (nng_msg **) &msg->tp_buf, NO_FLAGS ); // blocks hard until received if( (msg->state = xlate_nng_state( msg->state, RMR_ERR_RCVFAILED )) != RMR_OK ) { + msg->tp_state = errno; return msg; } + msg->tp_state = 0; if( msg->tp_buf == NULL ) { // if state is good this _should_ not be nil, but parninoia says check anyway msg->state = RMR_ERR_EMPTY; + msg->tp_state = 0; return msg; } @@ -446,6 +452,7 @@ static rmr_mbuf_t* rcv_msg( uta_ctx_t* ctx, rmr_mbuf_t* old_msg ) { msg->mtype, msg->state, msg->len, msg->payload - (unsigned char *) msg->header ); } else { msg->state = RMR_ERR_EMPTY; + msg->tp_state = 0; msg->len = 0; msg->alloc_len = rsize; msg->payload = NULL; @@ -509,6 +516,9 @@ static void* rcv_payload( uta_ctx_t* ctx, rmr_mbuf_t* old_msg ) { Called by rmr_send_msg() and rmr_rts_msg(), etc. and thus we assume that all pointer validation has been done prior. + + When msg->state is not ok, this function must set tp_state in the message as some API + fucntions return the message directly and do not propigate errno into the message. */ static rmr_mbuf_t* send_msg( uta_ctx_t* ctx, rmr_mbuf_t* msg, nng_socket nn_sock, int retries ) { int state; @@ -560,6 +570,7 @@ static rmr_mbuf_t* send_msg( uta_ctx_t* ctx, rmr_mbuf_t* msg, nng_socket nn_sock // future: this should not happen as all buffers we deal with are zc buffers; might make sense to remove the test and else msg->state = RMR_ERR_SENDFAILED; errno = ENOTSUP; + msg->tp_state = errno; return msg; /* NOT SUPPORTED @@ -607,6 +618,10 @@ static rmr_mbuf_t* send_msg( uta_ctx_t* ctx, rmr_mbuf_t* msg, nng_socket nn_sock message type is used. If the initial lookup, with a subid, fails, then a second lookup using just the mtype is tried. + When msg->state is not OK, this function must set tp_state in the message as + some API fucntions return the message directly and do not propigate errno into + the message. + CAUTION: this is a non-blocking send. If the message cannot be sent, then it will return with an error and errno set to eagain. If the send is a limited fanout, then the returned status is the status of the last @@ -629,6 +644,7 @@ static rmr_mbuf_t* mtosend_msg( void* vctx, rmr_mbuf_t* msg, int max_to ) { if( msg != NULL ) { msg->state = RMR_ERR_BADARG; errno = EINVAL; // must ensure it's not eagain + msg->tp_state = errno; } return msg; } @@ -638,6 +654,7 @@ static rmr_mbuf_t* mtosend_msg( void* vctx, rmr_mbuf_t* msg, int max_to ) { fprintf( stderr, "rmr_send_msg: ERROR: message had no header\n" ); msg->state = RMR_ERR_NOHDR; errno = EBADMSG; // must ensure it's not eagain + msg->tp_state = errno; return msg; } @@ -667,6 +684,7 @@ static rmr_mbuf_t* mtosend_msg( void* vctx, rmr_mbuf_t* msg, int max_to ) { msg->state = RMR_ERR_NOENDPT; errno = ENXIO; // must ensure it's not eagain + msg->tp_state = errno; return msg; // caller can resend (maybe) or free } diff --git a/test/rmr_nng_api_static_test.c b/test/rmr_nng_api_static_test.c index a3c8350..ca2e6f2 100644 --- a/test/rmr_nng_api_static_test.c +++ b/test/rmr_nng_api_static_test.c @@ -184,12 +184,14 @@ static int rmr_api_test( ) { msg->len = 100; msg->mtype = 1; msg->state = 999; + msg->tp_state = 999; errno = 999; msg = rmr_send_msg( rmc, msg ); errors += fail_if_nil( msg, "send_msg_ did not return a message on send " ); if( msg ) { errors += fail_not_equal( msg->state, RMR_ERR_NOENDPT, "send_msg did not return no endpoints before rtable added " ); errors += fail_if( errno == 0, "send_msg did not set errno " ); + errors += fail_if( msg->tp_state == 999, "send_msg did not set tp_state (1)" ); } gen_rt( rmc ); // --- after this point there is a dummy route table so send and rts calls should be ok @@ -197,6 +199,7 @@ static int rmr_api_test( ) { msg->len = 100; msg->mtype = 1; msg->state = 999; + msg->tp_state = 999; errno = 999; msg = rmr_send_msg( rmc, msg ); errors += fail_if_nil( msg, "send_msg_ did not return a message on send " ); @@ -205,6 +208,7 @@ static int rmr_api_test( ) { errors += fail_if( errno != 0, "send_msg set errno for send that should work " ); v = rmr_payload_size( msg ); errors += fail_if( v != 2048, "send_msg did not allocate new buffer with correct size " ); + errors += fail_if( msg->tp_state == 999, "send_msg did not set tp_state (2)" ); } rmr_set_stimeout( NULL, 0 ); -- 2.16.6