Eliminate the SI receive buffer length requirement
[ric-plt/lib/rmr.git] / docs / user-guide.rst
old mode 100644 (file)
new mode 100755 (executable)
index c0b7bc8..cc84a2c
@@ -9,18 +9,22 @@
 RMR User's Guide 
 ============================================================================================ 
  
-The RIC Message Router (RMR) is a library which applications 
-use to send and receive messages where the message routing, 
-endpoint selection, is based on the message type rather than 
-on traditional DNS names or IP addresses. Because the user 
-documentation for RMR is a collection of UNIX manpages 
-(included in the development package, and available via the 
-man command when installed), there is no separate "User's 
-Guide." To provide something for the document scrapers to 
-find, this is a collection of the RMR manual pages formatted 
-directly from their source which might be a bit ragged when 
-combined into a single markup document. Read the manual pages 
-:) 
+The RIC Message Router (RMR) is a library for peer-to-peer 
+communication. Applications use the library to send and 
+receive messages where the message routing and endpoint 
+selection is based on the message type rather than DNS host 
+name-IP port combinations. 
+This document contains information that developers need to 
+know to use the RMR library. Because the primary 
+documentation for the RMR library is a collection of UNIX 
+manpages (included in the development package, and available 
+via the man command when installed), there is no separate 
+"User's Guide." To provide something for the document 
+scrapers to find, this is a collection of the RMR manual 
+pages formatted directly from their source, which might be a 
+bit ragged when combined into a single markup document. Read 
+the manual pages :) 
  
  
  
@@ -65,8 +69,8 @@ is described in rmr.h, and is illustrated below.
      int len;
      unsigned char* payload;
      unsigned char* xaction;
-     uint sub_id;
-     uint tp_state;
+     int sub_id;
+     int tp_state;
  } rmr_mbuf_t;
  
  
@@ -128,8 +132,7 @@ xaction
   and wait for the reply; the underlying RMR processing 
   expects that the matching reply message will also contain 
   the same data in the *xaction* field. 
+   
  
 sub_id 
    
@@ -147,21 +150,21 @@ tp_state
    
   For C applications making use of RMR, the state of a 
   transport based failure will often be available via errno. 
-  However, some wrapper environments may not have direct access 
-  to the C-lib errno value. RMR send and receive operations 
-  will place the current value of 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 RMR_OK. 
+  However, some wrapper environments may not have direct 
+  access to the C-lib errno value. RMR send and receive 
+  operations will place the current value of 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 RMR_OK. 
  
  
 RETURN VALUE 
 -------------------------------------------------------------------------------------------- 
  
-The function returns a pointer to a rmr_mbuf structure, or NULL 
-on error. 
+The function returns a pointer to a rmr_mbuf structure, or 
+NULL on error. 
  
 ERRORS 
 -------------------------------------------------------------------------------------------- 
@@ -176,12 +179,12 @@ ENOMEM
 SEE ALSO 
 -------------------------------------------------------------------------------------------- 
  
-rmr_tralloc_msg(3), rmr_call(3), rmr_free_msg(3), rmr_init(3), 
-rmr_init_trace(3), rmr_get_trace(3), rmr_get_trlen(3), 
-rmr_payload_size(3), rmr_send_msg(3), rmr_rcv_msg(3), 
-rmr_rcv_specific(3), rmr_rts_msg(3), rmr_ready(3), rmr_fib(3), 
-rmr_has_str(3), rmr_tokenise(3), rmr_mk_ring(3), 
-rmr_ring_free(3), rmr_set_trace(3) 
+rmr_tralloc_msg(3), rmr_call(3), rmr_free_msg(3), 
+rmr_init(3), rmr_init_trace(3), rmr_get_trace(3), 
+rmr_get_trlen(3), rmr_payload_size(3), rmr_send_msg(3), 
+rmr_rcv_msg(3), rmr_rcv_specific(3), rmr_rts_msg(3), 
+rmr_ready(3), rmr_fib(3), rmr_has_str(3), rmr_tokenise(3), 
+rmr_mk_ring(3), rmr_ring_free(3), rmr_set_trace(3) 
  
  
 NAME 
@@ -404,7 +407,7 @@ that received the message.
 Messages which are received while waiting for the response 
 are queued internally by RMR, and are returned to the user 
 application when rmr_rcv_msg is invoked. These messages are 
-returned in th order received, one per call to rmr_rcv_msg. 
+returned in the order received, one per call to rmr_rcv_msg. 
  
 Call Timeout 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
@@ -413,7 +416,7 @@ The rmr_call function implements a timeout failsafe to
 prevent, in most cases, the function from blocking forever. 
 The timeout period is **not** based on time (calls to clock 
 are deemed too expensive for a low latency system level 
-library, but instead the period is based on the number of 
+library), but instead the period is based on the number of 
 received messages which are not the response. Using a 
 non-time mechanism for *timeout* prevents the async queue 
 from filling (which would lead to message drops) in an 
@@ -430,7 +433,7 @@ parameter which the user application may set.
 Retries 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  
-The send operations in RMr will retry *soft* send failures 
+The send operations in RMR will retry *soft* send failures 
 until one of three conditions occurs: 
  
  
@@ -442,7 +445,7 @@ until one of three conditions occurs:
  
 2. 
    
-  The underlying transport reports a * hard * failure 
+  The underlying transport reports a *hard* failure 
    
  
 3. 
@@ -450,12 +453,12 @@ until one of three conditions occurs:
   The maximum number of retry loops has been attempted 
  
  
-A retry loop consists of approximately 1000 send attemps ** 
-without** any intervening calls to * sleep() * or * usleep(). 
-The number of retry loops defaults to 1, thus a maximum of 
+A retry loop consists of approximately 1000 send attempts 
+**without** any intervening calls to *sleep()* or *usleep().* 
+The number of retry loops defaults to 1, thus a maximum of 
 1000 send attempts is performed before returning to the user 
 application. This value can be set at any point after RMr 
-initialisation using the * rmr_set_stimeout() * function 
+initialisation using the *rmr_set_stimeout()* function 
 allowing the user application to completely disable retires 
 (set to 0), or to increase the number of retry loops. 
  
@@ -473,14 +476,13 @@ underlying mechanisms (software or hardware) will be reported
 immediately to the user application. 
  
 It should be noted that depending on the underlying transport 
-mechanism being used, it is extremly possible that during 
-normal operations that retry conditions are very likely to 
-happen. These are completely out of RMr's control, and there 
-is nothing that RMr can do to avoid or midigate these other 
-than by allowing RMr to retry the send operation, and even 
-then it is possible (e.g. during connection reattempts), that 
-a single retry loop is not enough to guarentee a successful 
-send. 
+mechanism being used, it is extremely likely that retry 
+conditions will happen during normal operations. These are 
+completely out of RMR's control, and there is nothing that 
+RMR can do to avoid or mitigate these other than by allowing 
+RMR to retry the send operation, and even then it is possible 
+(e.g., during connection reattempts), that a single retry 
+loop is not enough to guarantee a successful send. 
  
 RETURN VALUE 
 -------------------------------------------------------------------------------------------- 
@@ -535,17 +537,18 @@ EINVAL
  
 EAGAIN 
    
-  The underlying message system wsa interrupted or the 
-  device was busy; the message was **not** sent, and user 
-  application should call this function with the messag
-  again. 
+  The underlying message system was interrupted or the 
+  device was busy; the message was **not** sent, and the 
+  user application should call this function with th
+  message again. 
  
  
 EXAMPLE 
 -------------------------------------------------------------------------------------------- 
  
-The following code bit shows one way of using the rmr_call 
-function, and illustrates how the transaction ID must be set. 
+The following code snippet shows one way of using the 
+rmr_call function, and illustrates how the transaction ID 
+must be set. 
  
  
 :: 
@@ -554,26 +557,24 @@ function, and illustrates how the transaction ID must be set.
      int retry_delay = 50000;            // retry delay (usec)
      static rmr_mbuf_t*  mbuf = NULL;    // response msg
      msg_t*  pm;                         // private message (payload)
-     m// get a send buffer and reference the payload 
+     // get a send buffer and reference the payload
      mbuf = rmr_alloc_msg( mr, RMR_MAX_RCV_BYTES );
      pm = (msg_t*) mbuf->payload;
-     p// generate an xaction ID and fill in payload with data and msg type
+     // generate an xaction ID and fill in payload with data and msg type
      snprintf( mbuf->xaction, RMR_MAX_XID, "%s", gen_xaction() );
      snprintf( pm->req, sizeof( pm->req ), "{ \\"req\\": \\"num users\\"}" );
      mbuf->mtype = MT_REQ;
-     
      msg = rmr_call( mr, msg );
      if( ! msg ) {               // probably a timeout and no msg received
          return NULL;            // let errno trickle up
-     } 
+     }
      if( mbuf->state != RMR_OK ) {
          while( retries_left-- > 0 &&             // loop as long as eagain
-                errno == EAGAIN && 
-                (msg = rmr_call( mr, msg )) != NULL && 
+                errno == EAGAIN &&
+                (msg = rmr_call( mr, msg )) != NULL &&
                 mbuf->state != RMR_OK ) {
              usleep( retry_delay );
          }
-     
          if( mbuf == NULL || mbuf->state != RMR_OK ) {
              rmr_free_msg( mbuf );        // safe if nil
              return NULL;
@@ -596,7 +597,7 @@ rmr_tokenise(3), rmr_mk_ring(3), rmr_ring_free(3)
 NAME 
 -------------------------------------------------------------------------------------------- 
  
-rmr_wh_open 
+rmr_close 
  
 SYNOPSIS 
 -------------------------------------------------------------------------------------------- 
@@ -768,19 +769,11 @@ has been received.
 The context (ctx) pointer passed in is the pointer returned 
 by the call to rmr_init(). 
  
-**NOTE:** There is no support for epoll in Nanomsg, thus his 
-function is only supported when linking with the NNG version 
-of RMr and the file descriptor returned when using the 
-Nanomsg verfsion will always return an error. 
 RETURN VALUE 
 -------------------------------------------------------------------------------------------- 
  
 The rmr_get_rcvfd function returns a file descriptor greater 
-or equal to 0 on success and -1 on error. If this function is 
-called from a user application linked against the Nanomsg RMr 
-library, calls will always return -1 with errno set to 
-EINVAL. 
+or equal to 0 on success and -1 on error. 
  
 ERRORS 
 -------------------------------------------------------------------------------------------- 
@@ -817,25 +810,21 @@ function. Error checking has been omitted for clarity.
      rmr_mbuf_t* msg = NULL;
      int nready;
      int i;
-  
      mrc = rmr_init( "43086", RMR_MAX_RCV_BYTES, RMRFL_NONE );
      rcv_fd = rmr_get_rcvfd( mrc );
-  
-     rep_fd = epoll_create1( 0 );    _    B    ,// initialise epoll environment
+     ep_fd = epoll_create1( 0 );             // initialise epoll environment
      epe.events = EPOLLIN;
      epe.data.fd = rcv_fd;
      epoll_ctl( ep_fd, EPOLL_CTL_ADD, rcv_fd, &epe );    // add our info to the mix
-  
      while( 1 ) {
-         nready = epoll_wait( ep_fd, events, 10, -1 );       // -1 == block forever (no timeout)
-         for( i = 0; i < nready && i < 10; i++ ) {           // loop through to find what is ready
-             if( events[i].data.fd == rcv_fd ) {             // RMr has something
+         nready = epoll_wait( ep_fd, events, 10, -1 );   // -1 == block forever (no timeout)
+         for( i = 0; i < nready && i < 10; i++ ) {       // loop through to find what is ready
+             if( events[i].data.fd == rcv_fd ) {         // RMR has something
                  msg = rmr_rcv_msg( mrc, msg );
                  if( msg ) {
                      // do something with msg
                  }
              }
-  
              // check for other ready fds....
          }
      }
@@ -1214,12 +1203,15 @@ RMRFL_MTCALL
    
   Enable multi-threaded call support. 
    
-  &ditem Some underlying transport providers (e.g. SI95) 
-  enable locking to be turned off if the user application is 
-  single threaded, or otherwise can guarantee that RMR 
-  functions will not be invoked concurrently from different 
-  threads. Turning off locking can help make message receipt 
-  more efficient. If this flag is set when the underlying 
+RMRFL_NOLOCK 
+   
+  Some underlying transport providers (e.g. SI95) enable 
+  locking to be turned off if the user application is single 
+  threaded, or otherwise can guarantee that RMR functions 
+  will not be invoked concurrently from different threads. 
+  Turning off locking can help make message receipt more 
+  efficient. If this flag is set when the underlying 
   transport does not support disabling locks, it will be 
   ignored. 
  
@@ -1251,30 +1243,199 @@ an additional process is necessary the user application must
 ENVIRONMENT 
 -------------------------------------------------------------------------------------------- 
  
-As a part of the initialisation process rmr_init will look 
-into the available environment variables to influence it's 
-setup. The following variables will be used when found. 
+As a part of the initialisation process rmr_init reads 
+environment variables to configure itself. The following 
+variables are used if found. 
  
  
  
-RMR_SEED_RT 
+RMR_ASYNC_CONN 
+   
+  Allows the async connection mode to be turned off (by 
+  setting the value to 0). When set to 1, or missing from 
+  the environment, RMR will invoke the connection interface 
+  in the transport mechanism using the non-blocking (async) 
+  mode. This will likely result in many "soft failures" 
+  (retry) until the connection is established, but allows 
+  the application to continue unimpeded should the 
+  connection be slow to set up. 
+   
+RMR_BIND_IF 
    
-  Assumes this is the filename of the seed route table file 
-  to use. In normal situations, the library will wait for an 
-  update from the route table generator (expected within a 
-  few seconds of initialisation) before being able to send 
-  messages. However, in some situations where a bootstrap 
-  table is necessary, this is the means to supply it to the 
-  library. 
+  This provides the interface that RMR will bind listen 
+  ports to, allowing for a single interface to be used 
+  rather than listening across all interfaces. This should 
+  be the IP address assigned to the interface that RMR 
+  should listen on, and if not defined RMR will listen on 
+  all interfaces. 
+   
+RMR_CTL_PORT 
+   
+  This variable defines the port that RMR should open for 
+  communications with Route Manager, and other RMR control 
+  applications. If not defined, the port 4561 is assumed. 
+   
+  Previously, the RMR_RTG_SVC (route table generator service 
+  port) was used to define this port. However, a future 
+  version of Route Manager will require RMR to connect and 
+  request tables, thus that variable is now used to supply 
+  the Route Manager's well-known address and port. 
+   
+  To maintain backwards compatibility with the older Route 
+  Manager versions, the presence of this variable in the 
+  environment will shift RMR's behaviour with respect to the 
+  default value used when RMR_RTG_SVC is **not** defined. 
+   
+  When RMR_CTL_PORT is **defined:** RMR assumes that Route 
+  Manager requires RMR to connect and request table updates 
+  is made, and the default well-known address for Route 
+  manager is used (routemgr:4561). 
+   
+  When RMR_CTL_PORT is **undefined:** RMR assumes that Route 
+  Manager will connect and push table updates, thus the 
+  default listen port (4561) is used. 
+   
+  To avoid any possible misinterpretation and/or incorrect 
+  assumptions on the part of RMR, it is recommended that 
+  both the RMR_CTL_PORT and RMR_RTG_SVC be defined. In the 
+  case where both variables are defined, RMR will behave 
+  exactly as is communicated with the variable's values. 
    
  
 RMR_RTG_SVC 
    
-  The route table generator assumes that RMr is listening on 
-  a well known port (4561) by default, but this environment 
-  variable can be used to change the listening port if 
-  needed. The value of the variable is expected to be just 
-  the port. 
+  The value of this variable depends on the Route Manager in 
+  use. 
+   
+  When the Route Manager is expecting to connect to an xAPP 
+  and push route tables, this variable must indicate the 
+  port which RMR should use to listen for these connections. 
+   
+  When the Route Manager is expecting RMR to connect and 
+  request a table update during initialisation, the variable 
+  should be the host of the Route Manager process. 
+   
+  The RMR_CTL_PORT variable (added with the support of 
+  sending table update requests to Route manager), controls 
+  the behaviour if this variable is not set. See the 
+  description of that variable for details. 
+   
+RMR_HR_LOG 
+   
+  By default RMR writes messages to standard error 
+  (incorrectly referred to as log messages) in human 
+  readable format. If this environment variable is set to 0, 
+  the format of standard error messages might be written in 
+  some format not easily read by humans. If missing, a value 
+  of 1 is assumed. 
+   
+RMR_LOG_VLEVEL 
+   
+  This is a numeric value which corresponds to the verbosity 
+  level used to limit messages written to standard error. 
+  The lower the number the less chatty RMR functions are 
+  during execution. The following is the current 
+  relationship between the value set on this variable and 
+  the messages written: 
+   
+0 
+   
+  Off; no messages of any sort are written. 
+   
+1 
+   
+  Only critical messages are written (default if this 
+  variable does not exist) 
+   
+2 
+   
+  Errors and all messages written with a lower value. 
+   
+3 
+   
+  Warnings and all messages written with a lower value. 
+   
+4 
+   
+  Informational and all messages written with a lower 
+  value. 
+   
+5 
+   
+  Debugging mode -- all messages written, however this 
+  requires RMR to have been compiled with debugging 
+  support enabled. 
+RMR_RTG_ISRAW 
+   
+  **Deprecated.** Should be set to 1 if the route table 
+  generator is sending "plain" messages (not using RMR to 
+  send messages), 0 if the RTG is using RMR to send. The 
+  default is 1 as we don't expect the RTG to use RMR. 
+   
+  This variable is only recognised when using the NNG 
+  transport library as it is not possible to support NNG 
+  "raw" communications with other transport libraries. It is 
+  also necessary to match the value of this variable with 
+  the capabilities of the Route Manager; at some point in 
+  the future RMR will assume that all Route Manager messages 
+  will arrive via an RMR connection and will ignore this 
+  variable. 
+RMR_SEED_RT 
+   
+  This is used to supply a static route table which can be 
+  used for debugging, testing, or if no route table 
+  generator process is being used to supply the route table. 
+  If not defined, no static table is used and RMR will not 
+  report *ready* until a table is received. The static route 
+  table may contain both the route table (between newrt 
+  start and end records), and the MEID map (between meid_map 
+  start and end records). 
+RMR_SRC_ID 
+   
+  This is either the name or IP address which is placed into 
+  outbound messages as the message source. This will used 
+  when an RMR based application uses the rmr_rts_msg() 
+  function to return a response to the sender. If not 
+  supplied RMR will use the hostname which in some container 
+  environments might not be routable. 
+   
+  The value of this variable is also used for Route Manager 
+  messages which are sent via an RMR connection. 
+RMR_VCTL_FILE 
+   
+  This supplies the name of a verbosity control file. The 
+  core RMR functions do not produce messages unless there is 
+  a critical failure. However, the route table collection 
+  thread, not a part of the main message processing 
+  component, can write additional messages to standard 
+  error. If this variable is set, RMR will extract the 
+  verbosity level for these messages (0 is silent) from the 
+  first line of the file. Changes to the file are detected 
+  and thus the level can be changed dynamically, however RMR 
+  will only suss out this variable during initialisation, so 
+  it is impossible to enable verbosity after startup. 
+RMR_WARNINGS 
+   
+  If set to 1, RMR will write some warnings which are 
+  non-performance impacting. If the variable is not defined, 
+  or set to 0, RMR will not write these additional warnings. 
  
  
 RETURN VALUE 
@@ -1423,24 +1584,19 @@ are queued on a *normal* receive queue and will be delivered
 to the user application with the next invocation of 
 *rmr_mt_rcv()* or *rmr_rvv_msg().* by RMR, and are returned 
 to the user application when rmr_rcv_msg is invoked. These 
-messages are returned in th order received, one per call to 
+messages are returned in the order received, one per call to 
 rmr_rcv_msg. 
  
-NOTE: Currently the multi-threaded functions are supported 
-only when the NNG transport mechanism is being used. It will 
-not be possible to link a programme using the Nanomsg version 
-of the library when references to this function are present. 
 The Transaction ID 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  
 The user application is responsible for setting the value of 
 the transaction ID field before invoking *rmr_mt_call.* The 
 transaction ID is a RMR_MAX_XID byte field that is used to 
-match the response message when it arrives. RMr will compare 
+match the response message when it arrives. RMR will compare 
 **all** of the bytes in the field, so the caller must ensure 
 that they are set correctly to avoid missing the response 
-message. (The application which returns the response message 
+message. The application which returns the response message 
 is also expected to ensure that the return buffer has the 
 matching transaction ID. This can be done transparently if 
 the application uses the *rmr_rts_msg()* function and does 
@@ -1449,7 +1605,7 @@ not adjust the transaction ID.
 Retries 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  
-The send operations in RMr will retry *soft* send failures 
+The send operations in RMR will retry *soft* send failures 
 until one of three conditions occurs: 
  
  
@@ -1461,7 +1617,7 @@ until one of three conditions occurs:
  
 2. 
    
-  The underlying transport reports a * hard * failure 
+  The underlying transport reports a *hard* failure 
    
  
 3. 
@@ -1469,12 +1625,12 @@ until one of three conditions occurs:
   The maximum number of retry loops has been attempted 
  
  
-A retry loop consists of approximately 1000 send attemps ** 
-without** any intervening calls to * sleep() * or * usleep(). 
-The number of retry loops defaults to 1, thus a maximum of 
+A retry loop consists of approximately 1000 send attempts 
+**without** any intervening calls to *sleep()* or *usleep().* 
+The number of retry loops defaults to 1, thus a maximum of 
 1000 send attempts is performed before returning to the user 
 application. This value can be set at any point after RMr 
-initialisation using the * rmr_set_stimeout() * function 
+initialisation using the *rmr_set_stimeout()* function 
 allowing the user application to completely disable retires 
 (set to 0), or to increase the number of retry loops. 
  
@@ -1492,14 +1648,13 @@ underlying mechanisms (software or hardware) will be reported
 immediately to the user application. 
  
 It should be noted that depending on the underlying transport 
-mechanism being used, it is extremly possible that during 
-normal operations that retry conditions are very likely to 
-happen. These are completely out of RMr's control, and there 
-is nothing that RMr can do to avoid or midigate these other 
-than by allowing RMr to retry the send operation, and even 
-then it is possible (e.g. during connection reattempts), that 
-a single retry loop is not enough to guarentee a successful 
-send. 
+mechanism being used, it is extremely likely that retry 
+conditions will happen during normal operations. These are 
+completely out of RMR's control, and there is nothing that 
+RMR can do to avoid or mitigate these other than by allowing 
+RMR to retry the send operation, and even then it is possible 
+(e.g., during connection reattempts), that a single retry 
+loop is not enough to guarantee a successful send. 
  
 RETURN VALUE 
 -------------------------------------------------------------------------------------------- 
@@ -1593,26 +1748,25 @@ function, and illustrates how the transaction ID must be set.
      int retries_left = 5;               // max retries on dev not available
      static rmr_mbuf_t*  mbuf = NULL;    // response msg
      msg_t*  pm;                         // private message (payload)
-     m// get a send buffer and reference the payload 
+     // get a send buffer and reference the payload
      mbuf = rmr_alloc_msg( mr, RMR_MAX_RCV_BYTES );
      pm = (msg_t*) mbuf->payload;
-     p// generate an xaction ID and fill in payload with data and msg type
+     // generate an xaction ID and fill in payload with data and msg type
      rmr_bytes2xact( mbuf, xid, RMR_MAX_XID );
      snprintf( pm->req, sizeof( pm->req ), "{ \\"req\\": \\"num users\\"}" );
      mbuf->mtype = MT_USR_RESP;
-     
-     msg = rmr_mt_call( mr, msg, my_id, 100 );    e    :// wait up to 100ms
+     msg = rmr_mt_call( mr, msg, my_id, 100 );        // wait up to 100ms
      if( ! msg ) {               // probably a timeout and no msg received
          return NULL;            // let errno trickle up
-     } 
+     }
      if( mbuf->state != RMR_OK ) {
          while( retries_left-- > 0 &&             // loop as long as eagain
-                mbuf->state == RMR_ERR_RETRY && 
-                (msg = rmr_mt_call( mr, msg )) != NULL && 
+                mbuf->state == RMR_ERR_RETRY &&
+                (msg = rmr_mt_call( mr, msg )) != NULL &&
                 mbuf->state != RMR_OK ) {
              usleep( retry_delay );
          }
-     
+  
          if( mbuf == NULL || mbuf->state != RMR_OK ) {
              rmr_free_msg( mbuf );        // safe if nil
              return NULL;
@@ -1669,17 +1823,17 @@ buffer is available to supply, the receive function will
 allocate one. 
  
 The *old_msg* parameter allows the user to pass a previously 
-generated RMr message back to RMr for reuse. Optionally, the 
+generated RMR message back to RMR for reuse. Optionally, the 
 user application may pass a nil pointer if no reusable 
 message is available. When a timeout occurs, and old_msg was 
 not nil, the state will be returned by returning a pointer to 
 the old message with the state set. 
  
 It is possible to use the *rmr_rcv_msg()* function instead of 
-this function. Doing so might be advantagous if the user 
+this function. Doing so might be advantageous if the user 
 programme does not always start the multi-threaded mode and 
 the use of *rmr_rcv_msg()* would make the flow of the code 
-more simple. The advantags of using this function are the 
+more simple. The advantages of using this function are the 
 ability to set a timeout without using epoll, and a small 
 performance gain (if multi-threaded mode is enabled, and the 
 *rmr_rcv_msg()* function is used, it simply invokes this 
@@ -1689,11 +1843,6 @@ cost of a second call that results). Similarly, the
 mode with the same "pass through" overhead to using this 
 function directly. 
  
-NOTE: Currently the multi-threaded functions are supported 
-only when the NNG transport mechanism is being used. It will 
-not be possible to link a programme using the nanomsg version 
-of the library when references to this function are present. 
 RETURN VALUE 
 -------------------------------------------------------------------------------------------- 
  
@@ -1927,11 +2076,38 @@ payload), or a NULL pointer in the case of an extreme error.
 ERRORS 
 -------------------------------------------------------------------------------------------- 
  
-The *state* field in the message buffer will indicate either 
-RMR_OK or RMR_ERR_EMPTY if an empty message was received. If 
-a nil pointer is returned, or any other state value was set 
-in the message buffer, errno will be set to one of the 
-following: 
+The *state* field in the message buffer will indicate RMR_OK 
+when the message receive process was successful and the 
+message can be used by the caller. Depending on the 
+underlying transport mechanism, one of the following RMR 
+error stats may be returned: 
+RMR_ERR_EMPTY 
+   
+  The message received had no payload, or was completely 
+  empty. 
+   
+RMR_ERR_TIMEOUT 
+   
+  For some transport mechanisms, or if reading the receive 
+  queue from multiple threads, it is possible for one thread 
+  to find no data waiting when it queries the queue. When 
+  this state is reported, the message buffer does not 
+  contain message data and the user application should 
+  reinvoke the receive function. 
+When an RMR error state is reported, the underlying errno 
+value might provide more information. The following is a list 
+of possible values that might accompany the states listed 
+above: 
+RMR_ERR_EMPTY if an empty message was received. If a nil 
+pointer is returned, or any other state value was set in the 
+message buffer, errno will be set to one of the following: 
  
  
  
@@ -2091,7 +2267,7 @@ The rmr_realloc_payload function returns a pointer to the
 message buffer with the payload which is large enough to hold 
 *new_len* bytes. If the *clone* option is true, this will be 
 a pointer to the newly cloned message buffer; the original 
-message buffer pointer may still be used to referenced that 
+message buffer pointer may still be used to reference that 
 message. It is the calling application's responsibility to 
 free the memory associateed with both messages using the 
 rmr_free_msg() function. 
@@ -2138,8 +2314,8 @@ received.
      if( msg == NULL ) {
        fprintf( stderr, "[ERR] realloc returned a nil pointer: %s\\n", strerror( errno ) );
      } else {
-     }    e// populate and send ack message
-     }}
+       // populate and send ack message
+     }
  }
  
  
@@ -2182,7 +2358,7 @@ as rmr_send_msg.
 Retries 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  
-The send operations in RMr will retry *soft* send failures 
+The send operations in RMR will retry *soft* send failures 
 until one of three conditions occurs: 
  
  
@@ -2194,7 +2370,7 @@ until one of three conditions occurs:
  
 2. 
    
-  The underlying transport reports a * hard * failure 
+  The underlying transport reports a *hard* failure 
    
  
 3. 
@@ -2202,12 +2378,12 @@ until one of three conditions occurs:
   The maximum number of retry loops has been attempted 
  
  
-A retry loop consists of approximately 1000 send attemps ** 
-without** any intervening calls to * sleep() * or * usleep(). 
-The number of retry loops defaults to 1, thus a maximum of 
+A retry loop consists of approximately 1000 send attempts 
+**without** any intervening calls to *sleep()* or *usleep().* 
+The number of retry loops defaults to 1, thus a maximum of 
 1000 send attempts is performed before returning to the user 
 application. This value can be set at any point after RMr 
-initialisation using the * rmr_set_stimeout() * function 
+initialisation using the *rmr_set_stimeout()* function 
 allowing the user application to completely disable retires 
 (set to 0), or to increase the number of retry loops. 
  
@@ -2225,14 +2401,13 @@ underlying mechanisms (software or hardware) will be reported
 immediately to the user application. 
  
 It should be noted that depending on the underlying transport 
-mechanism being used, it is extremly possible that during 
-normal operations that retry conditions are very likely to 
-happen. These are completely out of RMr's control, and there 
-is nothing that RMr can do to avoid or midigate these other 
-than by allowing RMr to retry the send operation, and even 
-then it is possible (e.g. during connection reattempts), that 
-a single retry loop is not enough to guarentee a successful 
-send. 
+mechanism being used, it is extremely likely that retry 
+conditions will happen during normal operations. These are 
+completely out of RMR's control, and there is nothing that 
+RMR can do to avoid or mitigate these other than by allowing 
+RMR to retry the send operation, and even then it is possible 
+(e.g., during connection reattempts), that a single retry 
+loop is not enough to guarantee a successful send. 
  
 PAYLOAD SIZE 
 -------------------------------------------------------------------------------------------- 
@@ -2243,7 +2418,7 @@ the message payload than the allocated message has. In the
 case of a received message, it is possible that the response 
 needs to be larger than the payload associated with the 
 inbound message. In order to use the return to sender 
-function, the source infomration in the orignal message must 
+function, the source information in the original message must 
 be present in the response; information which cannot be added 
 to a message buffer allocated through the standard RMR 
 allocation function. To allocate a buffer with a larger 
@@ -2261,7 +2436,7 @@ state in this buffer will reflect the overall send operation
 state and should be RMR_OK. 
  
 If the state in the returned buffer is anything other than 
-UT_OK, the user application may need to attempt a 
+RMR_OK, the user application may need to attempt a 
 retransmission of the message, or take other action depending 
 on the setting of errno as described below. 
  
@@ -2420,7 +2595,7 @@ library.)
 Retries 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  
-The send operations in RMr will retry *soft* send failures 
+The send operations in RMR will retry *soft* send failures 
 until one of three conditions occurs: 
  
  
@@ -2432,7 +2607,7 @@ until one of three conditions occurs:
  
 2. 
    
-  The underlying transport reports a * hard * failure 
+  The underlying transport reports a *hard* failure 
    
  
 3. 
@@ -2440,12 +2615,12 @@ until one of three conditions occurs:
   The maximum number of retry loops has been attempted 
  
  
-A retry loop consists of approximately 1000 send attemps ** 
-without** any intervening calls to * sleep() * or * usleep(). 
-The number of retry loops defaults to 1, thus a maximum of 
+A retry loop consists of approximately 1000 send attempts 
+**without** any intervening calls to *sleep()* or *usleep().* 
+The number of retry loops defaults to 1, thus a maximum of 
 1000 send attempts is performed before returning to the user 
 application. This value can be set at any point after RMr 
-initialisation using the * rmr_set_stimeout() * function 
+initialisation using the *rmr_set_stimeout()* function 
 allowing the user application to completely disable retires 
 (set to 0), or to increase the number of retry loops. 
  
@@ -2463,14 +2638,13 @@ underlying mechanisms (software or hardware) will be reported
 immediately to the user application. 
  
 It should be noted that depending on the underlying transport 
-mechanism being used, it is extremly possible that during 
-normal operations that retry conditions are very likely to 
-happen. These are completely out of RMr's control, and there 
-is nothing that RMr can do to avoid or midigate these other 
-than by allowing RMr to retry the send operation, and even 
-then it is possible (e.g. during connection reattempts), that 
-a single retry loop is not enough to guarentee a successful 
-send. 
+mechanism being used, it is extremely likely that retry 
+conditions will happen during normal operations. These are 
+completely out of RMR's control, and there is nothing that 
+RMR can do to avoid or mitigate these other than by allowing 
+RMR to retry the send operation, and even then it is possible 
+(e.g., during connection reattempts), that a single retry 
+loop is not enough to guarantee a successful send. 
  
 RETURN VALUE 
 -------------------------------------------------------------------------------------------- 
@@ -2624,23 +2798,23 @@ cycles.
      static rmr_mbuf_t*  send_msg = NULL;        // message to send; reused on each call
      msg_t*  send_pm;                            // payload for send
      msg_t*  pm;                                 // our message format in the received payload
-     mif( send_msg  == NULL ) {
-         send_msg = rmr_alloc_msg( mr, MAX_SIZE );    r// new buffer to send
-      }
-      // reference payload and fill in message type
+     if( send_msg  == NULL ) {
+         send_msg = rmr_alloc_msg( mr, MAX_SIZE ); // new buffer to send
+     }
+     // reference payload and fill in message type
      pm = (msg_t*) send_msg->payload;
      send_msg->mtype = MT_ANSWER;
      msg->len = generate_data( pm );       // something that fills the payload in
      msg = rmr_send_msg( mr, send_msg );   // ensure new pointer used after send
-     mif( ! msg ) {
-     m    !return ERROR;
-     m} else {
-     m    sif( msg->state != RMR_OK ) {
-     m    s    m// check for RMR_ERR_RETRY, and resend if needed
-     m    s    m// else return error
-     m    s}
-     m}
-     mreturn OK;
+     if( ! msg ) {
+         return ERROR;
+     } else {
+         if( msg->state != RMR_OK ) {
+             // check for RMR_ERR_RETRY, and resend if needed
+             // else return error
+         }
+     }
+     return OK;
  
  
  
@@ -2653,6 +2827,46 @@ rmr_rts_msg(3), rmr_ready(3), rmr_mk_ring(3),
 rmr_ring_free(3), rmr_torcv_rcv(3), rmr_wh_send_msg(3) 
  
  
+NAME 
+-------------------------------------------------------------------------------------------- 
+rmr_set_fack 
+SYNOPSIS 
+-------------------------------------------------------------------------------------------- 
+:: 
+  
+ #include <rmr/rmr.h>
+ void rmr_set_fack( void* vctx );
+DESCRIPTION 
+-------------------------------------------------------------------------------------------- 
+The rmr_set_fack function enables *fast TCP acknowledgements* 
+if the underlying transport library supports it. This might 
+be useful for applications which must send messages at a 
+maximum rate. 
+RETURN VALUE 
+-------------------------------------------------------------------------------------------- 
+There is no return value. 
+ERRORS 
+-------------------------------------------------------------------------------------------- 
+This function does not generate any errors. 
+SEE ALSO 
+-------------------------------------------------------------------------------------------- 
+rmr_init(3), 
 NAME 
 -------------------------------------------------------------------------------------------- 
  
@@ -2723,9 +2937,9 @@ function is called.
 :: 
   
      #define NO_FLAGS    0
-     char*    Oport = "43086";     // port for message router listen
-     int         rmax_size = 4096;    // max message size for default allocations
-     void*     mr_context;         // message router context
+     char* port = "43086";     // port for message router listen
+     int   max_size = 4096;    // max message size for default allocations
+     void* mr_context;         // message router context
      mr_context = rmr_init( port, max_size, NO_FLAGS );
      if( mr_context != NULL ) {
          rmr_set_stimeout( mr_context, 0 );    // turn off retries
@@ -2755,7 +2969,7 @@ SYNOPSIS
 :: 
   
  #include <rmr/rmr.h>
- int rmr_bytes2payload( rmr_mbuf_t* mbuf, unsigned char* data, int len )
+ int rmr_set_trace( rmr_mbuf_t* mbuf, unsigned char* data, int len )
  
  
  
@@ -2789,6 +3003,99 @@ rmr_ring_free(3), rmr_str2meid(3), rmr_str2xact(3),
 rmr_wh_open(3), rmr_wh_send_msg(3) 
  
  
+NAME 
+-------------------------------------------------------------------------------------------- 
+rmr_set_vlevel 
+SYNOPSIS 
+-------------------------------------------------------------------------------------------- 
+:: 
+  
+ #include <rmr/rmr.h>
+ #include <rmr/rmr_logging.h>
+ void rmr_set_vlevel( int new_level )
+DESCRIPTION 
+-------------------------------------------------------------------------------------------- 
+The rmr_set_vlevel allows the user programme to set the 
+verbosity level which is used to determine the messages RMR 
+writes to standard error. The new_vlevel value must be one of 
+the following constants which have the indicated meanings: 
+RMR_VL_OFF 
+   
+  Turns off all message writing. This includes the stats and 
+  debugging messages generated by the route collector thread 
+  which are normally affected only by the externally managed 
+  verbose level file (and related environment variable). 
+   
+RMR_VL_CRIT 
+   
+  Write only messages of critical importance. From the point 
+  of view of RMR, when a critical proper behaviour of the 
+  library cannot be expected or guaranteed. 
+RMR_VL_ERR 
+   
+  Include error messages in the output. An error is an event 
+  from which RMR has no means to recover. Continued proper 
+  execution is likely except where the affected connection 
+  and/or component mentioned in the error is concerned. 
+RMR_VL_WARN 
+   
+  Include warning messages in the output. A warning 
+  indicates an event which is not considered to be normal, 
+  but is expected and continued acceptable behaviour of the 
+  system is assured. 
+RMR_VL_INFO 
+   
+  Include informational messagees in the output. 
+  Informational messages include some diagnostic information 
+  which explain the activities of RMR. 
+RMR_VL_DEBUG 
+   
+  Include all debugging messages in the output. Debugging 
+  must have also been enabled during the build as a 
+  precaution to accidentally enabling this level of output 
+  as it can grossly affect performance. 
+Generally RMR does not write messages to the standard error 
+device from *critical path* functions, therefore it is 
+usually not harmful to enable a verbosity level of either 
+RMR_VL_CRIT or RMR_VL_ERR. 
+Messages written from the route table collection thread are 
+still governed by the value placed into the verbose level 
+control file (see the man page for rmr_init()); those 
+messages are affected only when logging is completely 
+disabled by passing RMR_VL_OFF to this function. 
+The verbosity level can also be set via an environment 
+variable prior to the start of the RMR based application. The 
+environment variable is read only during initialisation; if 
+the programme must change the value during execution, this 
+function must be used. The default value, if this function is 
+never called, and the environment variable is not present, is 
+RMR_VL_ERR. 
+SEE ALSO 
+-------------------------------------------------------------------------------------------- 
+rmr_init(3) 
 NAME 
 -------------------------------------------------------------------------------------------- 
  
@@ -3206,10 +3513,10 @@ SYNOPSIS
 DESCRIPTION 
 -------------------------------------------------------------------------------------------- 
  
-The rmr_trace_ref function return a pointer to the trace area 
-in the message, and optionally populate the user programme 
-supplied size integer with the trace area size, if *sizeptr* 
-is not nil. 
+The rmr_trace_ref function returns a pointer to the trace 
+area in the message, and optionally populates the user 
+programme supplied size integer with the trace area size, if 
+*sizeptr* is not nil. 
  
 RETURN VALUE 
 -------------------------------------------------------------------------------------------- 
@@ -3245,7 +3552,7 @@ SYNOPSIS
 :: 
   
  #include <rmr/rmr.h>
- rmr_mbuf_t* rmr_tralloc_msg( void* vctx, int size, 
+ rmr_mbuf_t* rmr_tralloc_msg( void* vctx, int size,
                               int trace_size, unsigned const char *tr_data );
  
  
@@ -3253,14 +3560,14 @@ SYNOPSIS
 DESCRIPTION 
 -------------------------------------------------------------------------------------------- 
  
-The rmr_alloc_msg function is used to allocate a buffer which 
-the user programme can write into and then send through the a 
-library. The buffer is allocated such that sending it 
+The rmr_tralloc_msg function is used to allocate a buffer 
+which the user programme can write into and then send through 
+the library. The buffer is allocated such that sending it 
 requires no additional copying from the buffer as it passes 
 through the underlying transport mechanism. 
  
 The *size* parameter is used to set the payload length in the 
-message and If it is 0, then the default size supplied on the 
+message. If it is 0, then the default size supplied on the 
 *rmr_init* call will be used. In addition to allocating the 
 payload, a space in the buffer is reserved for *trace* data 
 (tr_size bytes), and the bytes pointed to by *tr_data* are 
@@ -3339,9 +3646,9 @@ xaction
   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 rmr_call function to send a message 
-  and wait for the reply; the underlying a processing 
-  expects that the matching reply message will also contain 
-  the same data in the *xaction* field. 
+  and wait for the reply; the underlying processing expects 
+  that the matching reply message will also contain the same 
+  data in the *xaction* field. 
  
  
 RETURN VALUE 
@@ -3374,7 +3681,205 @@ rmr_mk_ring(3), rmr_ring_free(3), rmr_set_trace(3)
 NAME 
 -------------------------------------------------------------------------------------------- 
  
-rmr_wh_open 
+rmr_wh_call 
+SYNOPSIS 
+-------------------------------------------------------------------------------------------- 
+:: 
+  
+ #include <rmr/rmr.h>
+ rmr_mbuf_t* rmr_wh_call( void* vctx, rmr_whid_t whid, rmr_mbuf_t* msg, int call_id, int max_wait )
+DESCRIPTION 
+-------------------------------------------------------------------------------------------- 
+The rmr_wh_call function accepts a message buffer (msg) from 
+the user application and attempts to send it using the 
+wormhole ID provided (whid). If the send is successful, the 
+call will block until either a response message is received, 
+or the max_wait number of milliseconds has passed. In order 
+for the response to be recognised as a response, the remote 
+process **must** use rmr_rts_msg() to send their response. 
+Like *rmr_wh_send_msg,* this function attempts to send the 
+message directly to a process at the other end of a wormhole 
+which was created with *rmr_wh_open().* When sending message 
+via wormholes, the normal RMR routing based on message type 
+is ignored, and the caller may leave the message type 
+unspecified in the message buffer (unless it is needed by the 
+receiving process). The call_id parameter is a number in the 
+range of 2 through 255 and is used to identify the calling 
+thread in order to properly match a response message when it 
+arrives. Providing this value, and ensuring the proper 
+uniqueness, is the responsibility of the user application and 
+as such the ability to use the rmr_wh_call() function from 
+potentially non-threaded concurrent applications (such as 
+Go's goroutines) is possible. 
+Retries 
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+The send operations in RMR will retry *soft* send failures 
+until one of three conditions occurs: 
+1. 
+   
+  The message is sent without error 
+   
+2. 
+   
+  The underlying transport reports a *hard* failure 
+   
+3. 
+   
+  The maximum number of retry loops has been attempted 
+A retry loop consists of approximately 1000 send attempts 
+**without** any intervening calls to *sleep()* or *usleep().* 
+The number of retry loops defaults to 1, thus a maximum of 
+1000 send attempts is performed before returning to the user 
+application. This value can be set at any point after RMr 
+initialisation using the *rmr_set_stimeout()* function 
+allowing the user application to completely disable retires 
+(set to 0), or to increase the number of retry loops. 
+Transport Level Blocking 
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+The underlying transport mechanism used to send messages is 
+configured in *non-blocking* mode. This means that if a 
+message cannot be sent immediately the transport mechanism 
+will **not** pause with the assumption that the inability to 
+send will clear quickly (within a few milliseconds). This 
+means that when the retry loop is completely disabled (set to 
+0), that the failure to accept a message for sending by the 
+underlying mechanisms (software or hardware) will be reported 
+immediately to the user application. 
+It should be noted that depending on the underlying transport 
+mechanism being used, it is extremely likely that retry 
+conditions will happen during normal operations. These are 
+completely out of RMR's control, and there is nothing that 
+RMR can do to avoid or mitigate these other than by allowing 
+RMR to retry the send operation, and even then it is possible 
+(e.g., during connection reattempts), that a single retry 
+loop is not enough to guarantee a successful send. 
+RETURN VALUE 
+-------------------------------------------------------------------------------------------- 
+On success, new message buffer, with the payload containing 
+the response from the remote endpoint is returned. The state 
+in this buffer will reflect the overall send operation state 
+and should be RMR_OK. 
+If a message is returned with a state which is anything other 
+than RMR_OK, the indication is that the send was not 
+successful. The user application must check the state and 
+determine the course of action. If the return value is NULL, 
+no message, the indication is that there was no response 
+received within the timeout (max_wait) period of time. 
+ERRORS 
+-------------------------------------------------------------------------------------------- 
+The following values may be passed back in the *state* field 
+of the returned message buffer. 
+RMR_ERR_WHID 
+   
+  The wormhole ID passed in was not associated with an open 
+  wormhole, or was out of range for a valid ID. 
+RMR_ERR_NOWHOPEN 
+   
+  No wormholes exist, further attempt to validate the ID are 
+  skipped. 
+RMR_ERR_BADARG 
+   
+  The message buffer pointer did not refer to a valid 
+  message. 
+RMR_ERR_NOHDR 
+   
+  The header in the message buffer was not valid or 
+  corrupted. 
+EXAMPLE 
+-------------------------------------------------------------------------------------------- 
+The following is a simple example of how the a wormhole is 
+created (rmr_wh_open) and then how rmr_wh_send_msg function 
+is used to send messages. Some error checking is omitted for 
+clarity. 
+:: 
+  
+ #include <rmr/rmr.h>    // system headers omitted for clarity
+ int main() {
+    rmr_whid_t whid = -1;   // wormhole id for sending
+    void* mrc;      //msg router context
+         int i;
+    rmr_mbuf_t*  sbuf;      // send buffer
+    int     count = 0;
+    mrc = rmr_init( "43086", RMR_MAX_RCV_BYTES, RMRFL_NONE );
+    if( mrc == NULL ) {
+       fprintf( stderr, "[FAIL] unable to initialise RMR environment\\n" );
+       exit( 1 );
+    }
+    while( ! rmr_ready( mrc ) ) {        // wait for routing table info
+       sleep( 1 );
+    }
+    sbuf = rmr_alloc_msg( mrc, 2048 );
+    while( 1 ) {
+      if( whid < 0 ) {
+        whid = rmr_wh_open( mrc, "localhost:6123" );  // open fails if endpoint refuses conn
+           if( RMR_WH_CONNECTED( wh ) ) {
+            snprintf( sbuf->payload, 1024, "periodic update from sender: %d", count++ );
+            sbuf->len =  strlen( sbuf->payload );
+            sbuf = rmr_wh_call( mrc, whid, sbuf, 1000 );        // expect a response in 1s or less
+            if( sbuf != NULL && sbuf->state = RMR_OK ) {
+              sprintf( stderr, "response: %s\\n", sbuf->payload );    // assume they sent a string
+            } else {
+              sprintf( stderr, "response not received, or send error\\n" );
+            }
+         }
+       }
+       sleep( 5 );
+    }
+ }
+SEE ALSO 
+-------------------------------------------------------------------------------------------- 
+rmr_alloc_msg(3), rmr_call(3), rmr_free_msg(3), rmr_init(3), 
+rmr_payload_size(3), rmr_rcv_msg(3), rmr_rcv_specific(3), 
+rmr_rts_msg(3), rmr_ready(3), rmr_fib(3), rmr_has_str(3), 
+rmr_tokenise(3), rmr_mk_ring(3), rmr_ring_free(3), 
+rmr_set_stimeout(3), rmr_wh_open(3), rmr_wh_close(3), 
+rmr_wh_state(3) 
+NAME 
+-------------------------------------------------------------------------------------------- 
+rmr_wh_close 
  
 SYNOPSIS 
 -------------------------------------------------------------------------------------------- 
@@ -3395,10 +3900,10 @@ the wormhole id passed in. Future calls to rmr_wh_send_msg
 with this ID will fail. 
  
 The underlying TCP connection to the remote endpoint is 
-**not** closed as this session may be reqruired for 
-regularlly routed messages (messages routed based on message 
-type). There is no way to force a TCP session to be closed at 
-this point in time. 
+**not** closed as this session may be required for regularly 
+routed messages (messages routed based on message type). 
+There is no way to force a TCP session to be closed at this 
+point in time. 
  
 SEE ALSO 
 -------------------------------------------------------------------------------------------- 
@@ -3476,7 +3981,7 @@ EINVAL
  
 EACCESS 
    
-  The user applicarion does not have the ability to 
+  The user application does not have the ability to 
   establish a wormhole to the indicated target (or maybe any 
   target). 
  
@@ -3495,8 +4000,8 @@ EXAMPLE
     rmr_whid_t wh;
     rmc = rmr_init( "43086", 4096, 0 ); // init context
     wh = rmr_wh_open( rmc, "localhost:6123" );
-    if( !RMR_WH_CONNECTED( wh ) ) { 
-     f fprintf( stderr, "unable to connect wormhole: %s\\n",
+    if( !RMR_WH_CONNECTED( wh ) ) {
+      fprintf( stderr, "unable to connect wormhole: %s\\n",
               strerror( errno ) );
     }
  
@@ -3509,8 +4014,8 @@ rmr_alloc_msg(3), rmr_call(3), rmr_free_msg(3),
 rmr_get_rcvfd(3), rmr_payload_size(3), rmr_send_msg(3), 
 rmr_rcv_msg(3), rmr_rcv_specific(3), rmr_rts_msg(3), 
 rmr_ready(3), rmr_fib(3), rmr_has_str(3), rmr_tokenise(3), 
-rmr_mk_ring(3), rmr_ring_free(3), rmr_wh_send_msg(3), 
-rmr_wh_close(3) 
+rmr_mk_ring(3), rmr_ring_free(3), rmr_wh_close(3), 
+rmr_wh_send_msg(3), rmr_wh_state(3) 
  
  
 NAME 
@@ -3537,20 +4042,20 @@ the user application and attempts to send it using the
 wormhole ID provided (id). Unlike *rmr_send_msg,* this 
 function attempts to send the message directly to a process 
 at the other end of a wormhole which was created with 
-*rmr_wh-open().* When sending message via wormholes, the 
-normal RMr routing based on message type is ignored, and the 
+*rmr_wh_open().* When sending message via wormholes, the 
+normal RMR routing based on message type is ignored, and the 
 caller may leave the message type unspecified in the message 
 buffer (unless it is needed by the receiving process). 
  
 The message buffer (msg) used to send is the same format as 
-used for regular RMr send and reply to sender operations, 
+used for regular RMR send and reply to sender operations, 
 thus any buffer allocated by these means, or calls to 
 *rmr_rcv_msg()* can be passed to this function. 
  
 Retries 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  
-The send operations in RMr will retry *soft* send failures 
+The send operations in RMR will retry *soft* send failures 
 until one of three conditions occurs: 
  
  
@@ -3562,7 +4067,7 @@ until one of three conditions occurs:
  
 2. 
    
-  The underlying transport reports a * hard * failure 
+  The underlying transport reports a *hard* failure 
    
  
 3. 
@@ -3570,12 +4075,12 @@ until one of three conditions occurs:
   The maximum number of retry loops has been attempted 
  
  
-A retry loop consists of approximately 1000 send attemps ** 
-without** any intervening calls to * sleep() * or * usleep(). 
-The number of retry loops defaults to 1, thus a maximum of 
+A retry loop consists of approximately 1000 send attempts 
+**without** any intervening calls to *sleep()* or *usleep().* 
+The number of retry loops defaults to 1, thus a maximum of 
 1000 send attempts is performed before returning to the user 
 application. This value can be set at any point after RMr 
-initialisation using the * rmr_set_stimeout() * function 
+initialisation using the *rmr_set_stimeout()* function 
 allowing the user application to completely disable retires 
 (set to 0), or to increase the number of retry loops. 
  
@@ -3593,14 +4098,13 @@ underlying mechanisms (software or hardware) will be reported
 immediately to the user application. 
  
 It should be noted that depending on the underlying transport 
-mechanism being used, it is extremly possible that during 
-normal operations that retry conditions are very likely to 
-happen. These are completely out of RMr's control, and there 
-is nothing that RMr can do to avoid or midigate these other 
-than by allowing RMr to retry the send operation, and even 
-then it is possible (e.g. during connection reattempts), that 
-a single retry loop is not enough to guarentee a successful 
-send. 
+mechanism being used, it is extremely likely that retry 
+conditions will happen during normal operations. These are 
+completely out of RMR's control, and there is nothing that 
+RMR can do to avoid or mitigate these other than by allowing 
+RMR to retry the send operation, and even then it is possible 
+(e.g., during connection reattempts), that a single retry 
+loop is not enough to guarantee a successful send. 
  
 RETURN VALUE 
 -------------------------------------------------------------------------------------------- 
@@ -3733,7 +4237,7 @@ clarity.
  
 :: 
   
- #include <rmr/rmr.h>    .// system headers omitted for clarity
+ #include <rmr/rmr.h>    // system headers omitted for clarity
  int main() {
     rmr_whid_t whid = -1;   // wormhole id for sending
     void* mrc;      //msg router context
@@ -3742,23 +4246,23 @@ clarity.
     int     count = 0;
     mrc = rmr_init( "43086", RMR_MAX_RCV_BYTES, RMRFL_NONE );
     if( mrc == NULL ) {
-       fprintf( stderr, "[FAIL] unable to initialise RMr environment\\n" );
+       fprintf( stderr, "[FAIL] unable to initialise RMR environment\\n" );
        exit( 1 );
     }
-    while( ! rmr_ready( mrc ) ) {    e    i// wait for routing table info
+    while( ! rmr_ready( mrc ) ) {        // wait for routing table info
        sleep( 1 );
     }
     sbuf = rmr_alloc_msg( mrc, 2048 );
     while( 1 ) {
       if( whid < 0 ) {
         whid = rmr_wh_open( mrc, "localhost:6123" );  // open fails if endpoint refuses conn
-        w   if( RMR_WH_CONNECTED( wh ) ) { 
+           if( RMR_WH_CONNECTED( wh ) ) {
             snprintf( sbuf->payload, 1024, "periodic update from sender: %d", count++ );
             sbuf->len =  strlen( sbuf->payload );
             sbuf = rmr_wh_send_msg( mrc, whid, sbuf );
-           }
-       }
-       sleep( 5 );
+         }
+      }
+      sleep( 5 );
     }
  }
  
@@ -3771,5 +4275,73 @@ rmr_alloc_msg(3), rmr_call(3), rmr_free_msg(3), rmr_init(3),
 rmr_payload_size(3), rmr_rcv_msg(3), rmr_rcv_specific(3), 
 rmr_rts_msg(3), rmr_ready(3), rmr_fib(3), rmr_has_str(3), 
 rmr_tokenise(3), rmr_mk_ring(3), rmr_ring_free(3), 
-rmr_set_stimeout(3), rmr_wh_open(3), rmr_wh_close(3) 
+rmr_set_stimeout(3), rmr_wh_open(3), rmr_wh_close(3), 
+rmr_wh_state(3) 
+NAME 
+-------------------------------------------------------------------------------------------- 
+rmr_wh_state 
+SYNOPSIS 
+-------------------------------------------------------------------------------------------- 
+:: 
+  
+ #include <rmr/rmr.h>
+ int rmr_wh_state( void* vctx, rmr_whid_t whid )
+DESCRIPTION 
+-------------------------------------------------------------------------------------------- 
+The rmr_wh_state function will return the current state of 
+the connection associated with the given wormhole (whid). The 
+return value indicates whether the connection is open 
+(RMR_OK), or closed (any other return value). 
+When using some transport mechanisms (e.g. NNG), it may not 
+be possible for RMR to know the actual state and the 
+connection may always be reported as "open." 
+RETURN 
+-------------------------------------------------------------------------------------------- 
+The following values are potential return values. 
+RMR_OK 
+   
+  The wormhole ID is valid and the connection is "open." 
+   
+RMR_ERR_WHID 
+   
+  THe wormhole ID passed into the function was not valid. 
+   
+RMR_ERR_NOENDPT 
+   
+  The wormhole is not open (not connected). 
+   
+RMR_ERR_BADARG 
+   
+  The context passed to the function was nil or invalid. 
+   
+RMR_ERR_NOWHOPEN 
+   
+  Wormholes have not been initialised (no wormhole open call 
+  has been made). 
+   
+SEE ALSO 
+-------------------------------------------------------------------------------------------- 
  
+rmr_wh_open(3), rmr_wh_send_msg(3), rmr_wh_close(3)