Contexts
+Contexts in Scalability Protocols provide for isolation of protocol-specific +state machines and associated data, allowing multiple concurrent state machines (or transactions) +to coexist on a single socket.
+For example, a REP server may wish to allow many requests to be serviced concurrently, +even though some jobs may take significant time to process. Contexts provide for this ability.
+Not all protocols have contexts, because many protocols simply have no state to manage. +The following protocols support contexts:
+-
+
- REP +
- REQ +
- RESPONDENT +
- SURVEYOR +
- SUB +
For these protocols, the socket will also have a single, default, context that is used when +performing send or receive operations on the socket directly.
+Other protocols are stateless, at least with respect to message processing, and have no use for +contexts. For the same reason, raw mode sockets do not support contexts.
++ + tip +
+Developers with experience with [libnanomsg] may be used to using raw sockets for concurrency. +Contexts provide a superior solution, as they are much easier to use, less error prone, and allow +for easy control of the amount of concurrency used on any given socket.
+One drawback of contexts is that they cannot be used with file descriptor polling using
+nng_socket_get_recv_poll_fd or nng_socket_get_send_poll_fd.
Context Structure
+#define NNG_CTX_INITIALIZER // opaque value
+
+typedef struct nng_ctx_s nng_ctx;
+
+The nng_ctx structure represents context. This is a handle, and
+the members of it are opaque. However, unlike a pointer, it is passed by value.
A context may be initialized statically with the NNG_CTX_INITIALIZER macro,
+to ensure that it cannot be confused with a valid open context.
Creating a Context
+int nng_ctx_open(nng_ctx *ctxp, nng_socket s);
+
+The nng_ctx_open function creates a separate context to be used with the socket s,
+and returns it at the location pointed by ctxp.
Context Identity
+int nng_ctx_id(nng_ctx c);
+
+The nng_ctx_id function returns a positive identifier for the context c if it is valid.
+Otherwise it returns -1.
A context is considered valid if it was ever opened with nng_ctx_open function.
+Contexts that are allocated on the stack or statically should be initialized with the macro NNG_CTX_INITIALIZER
+to ensure that they cannot be confused with a valid context before they are opened.
Closing a Context
+int nng_ctx_close(nng_ctx ctx);
+
+The nng_ctx_close function closes the context ctx.
+Messages that have been submitted for sending may be flushed or delivered,
+depending upon the transport.
Further attempts to use the context after this call returns will result in NNG_ECLOSED.
+Threads waiting for operations on the context when this
+call is executed may also return with an NNG_ECLOSED result.
+ + note +
+Closing the socket associated with ctx using nng_socket_close] also closes this context.
Sending Messages
+int nng_ctx_sendmsg(nng_ctx ctx, nng_msg *msg, int flags);
+void nng_ctx_send(nng_ctx ctx, nng_aio *aio);
+
+These functions (nng_ctx_sendmsg and nng_ctx_send) send
+messages over the socket s. The differences in their behaviors are as follows.
+ + note +
+The semantics of what sending a message means varies from protocol to +protocol, so examination of the protocol documentation is encouraged. +Additionally, some protocols may not support sending at all or may require other pre-conditions first. +(For example, REP sockets cannot normally send data until they have first received a request, +while SUB sockets can only receive data and never send it.)
+nng_ctx_sendmsg
+The nng_ctx_sendmsg function sends the msg over the context ctx.
If this function returns zero, then the socket will dispose of msg when the transmission is complete. +If the function returns a non-zero status, then the call retains the responsibility for disposing of msg.
+The flags can contain the value NNG_FLAG_NONBLOCK, indicating that the function should not wait if the socket
+cannot accept more data for sending. In such a case, it will return NNG_EAGAIN.
nng_ctx_send
+The nng_ctx_send function sends a message asynchronously, using the nng_aio aio, over the context ctx.
+The message to send must have been set on aio using the nng_aio_set_msg function.
If the operation completes successfully, then the context will have disposed of the message.
+However, if it fails, then callback of aio should arrange for a final disposition of the message.
+(The message can be retrieved from aio with nng_aio_get_msg.)
Note that callback associated with aio may be called before the message is finally delivered to the recipient. +For example, the message may be sitting in queue, or located in TCP buffers, or even in flight.
++ + tip +
+This is the preferred function to use for sending data on a context. While it does require a few extra
+steps on the part of the application, the lowest latencies and highest performance will be achieved by using
+this function instead of nng_ctx_sendmsg.
Receiving Messages
+int nng_ctx_recvmsg(nng_ctx ctx, nng_msg **msgp, int flags);
+void nng_ctx_recv(nng_ctx ctx, nng_aio *aio);
+
+These functions (, nng_ctx_recvmsg and nng_ctx_recv) receive
+messages over the context ctx. The differences in their behaviors are as follows.
+ + note +
+The semantics of what receving a message means varies from protocol to +protocol, so examination of the protocol documentation is encouraged. +Additionally, some protocols may not support receiving at all or may require other pre-conditions first. +(For example, REQ sockets cannot normally receive data until they have first sent a request.)
+nng_recvmsg
+The nng_ctx_recvmsg function receives a message and stores a pointer to the nng_msg for that message in msgp.
The flags can contain the value NNG_FLAG_NONBLOCK, indicating that the function should not wait if the socket
+has no messages available to receive. In such a case, it will return NNG_EAGAIN.
nng_recv_aio
+The nng_ctx_send function receives a message asynchronously, using the nng_aio aio, over the context ctx.
+On success, the received message can be retrieved from the aio using the nng_aio_get_msg function.
+ + note +
+It is important that the application retrieves the message, and disposes of it accordingly. +Failure to do so will leak the memory.
++ + tip +
+This is the preferred function to use for receiving data on a context. While it does require a few extra
+steps on the part of the application, the lowest latencies and highest performance will be achieved by using
+this function instead of nng_ctx_recvmsg.
Context Options
+int nng_ctx_get_bool(nng_ctx ctx, const char *opt, bool *valp);
+int nng_ctx_get_int(nng_ctx ctx, const char *opt, int *valp);
+int nng_ctx_get_ms(nng_ctx ctx, const char *opt, nng_duration *valp);
+int nng_ctx_get_size(nng_ctx ctx, const char *opt, size_t *valp);
+int nng_ctx_get_uint64(nng_ctx ctx, const char *opt, uint64_t *valp);
+
+int nng_ctx_set_bool(nng_ctx ctx, const char *opt, int val);
+int nng_ctx_set_int(nng_ctx ctx, const char *opt, int val);
+int nng_ctx_set_ms(nng_ctx ctx, const char *opt, nng_duration val);
+int nng_ctx_set_size(nng_ctx ctx, const char *opt, size_t val);
+int nng_ctx_set_uint64(nng_ctx ctx, const char *opt, uint64_t val);
+
+These functions are used to retrieve or change the value of an option named opt from the context ctx.
+The nng_ctx_get_ functions retrieve the value, and store it in the location valp references.
+The nng_ctx_set_ functions change the value for the ctx, taking it from val.
These functions access an option as a specific type. The protocol will have details about which options +are available for contexts, and which type they may be accessed using.
+Examples
+These examples show building blocks for a concurrent service based on contexts.
+Example 1: Context Echo Server
+The following program fragment demonstrates the use of contexts to implement +a concurrent REP service that simply echos messages back +to the sender.
+struct echo_context {
+ nng_ctx ctx;
+ nng_aio *aio;
+ enum { INIT, RECV, SEND } state;
+};
+
+void
+echo(void *arg)
+{
+ struct echo_context *ec = arg;
+
+ switch (ec->state) {
+ case INIT:
+ ec->state = RECV;
+ nng_ctx_recv(ec->ctx, ec->aio);
+ return;
+ case RECV:
+ if (nng_aio_result(ec->aio) != 0) {
+ // ... handle error
+ }
+ // We reuse the message on the ec->aio
+ ec->state = SEND;
+ nng_ctx_send(ec->ctx, ec->aio);
+ return;
+ case SEND:
+ if (nng_aio_result(ec->aio) != 0) {
+ // ... handle error
+ }
+ ec->state = RECV;
+ nng_ctx_recv(ec->ctx, ec->aio);
+ return;
+ }
+}
+
+Example 2: Starting the Echo Service
+Given the above fragment, the following example shows setting up the service.
+It assumes that the socket has already been
+created and any transports set up as well with functions such as nng_dial or nng_listen.
+#define CONCURRENCY 1024
+
+static struct echo_context ecs[CONCURRENCY];
+
+void
+start_echo_service(nng_socket rep_socket)
+{
+ for (int i = 0; i < CONCURRENCY; i++) {
+ // error checks elided for clarity
+ nng_ctx_open(&ecs[i].ctx, rep_socket);
+ nng_aio_alloc(&ecs[i].aio, echo, ecs+i);
+ ecs[i].state = INIT;
+ echo(ecs+i); // start it running
+ }
+}
+
+
+
+
+
+
+
+