1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef CORE_TRANSPORT_H
#define CORE_TRANSPORT_H
// Transport implementation details. Transports must implement the
// interfaces in this file.
struct nni_tran {
// tran_version is the version of the transport ops that this
// transport implements. We only bother to version the main
// ops vector.
uint32_t tran_version;
// tran_scheme is the transport scheme, such as "tcp" or "inproc".
const char *tran_scheme;
// tran_ep links our endpoint-specific operations.
const nni_tran_ep_ops *tran_ep;
// tran_pipe links our pipe-specific operations.
const nni_tran_pipe_ops *tran_pipe;
// tran_init, if not NULL, is called once during library
// initialization.
int (*tran_init)(void);
// tran_fini, if not NULL, is called during library deinitialization.
// It should release any global resources, close any open files, etc.
void (*tran_fini)(void);
};
// We quite intentionally use a signature where the upper word is nonzero,
// which ensures that if we get garbage we will reject it. This is more
// likely to mismatch than all zero bytes would. The actual version is
// stored in the lower word; this is not semver -- the numbers are just
// increasing - we doubt it will increase more than a handful of times
// during the life of the project. If we add a new version, please keep
// the old version around -- it may be possible to automatically convert
// older versions in the future.
#define NNI_TRANSPORT_V0 0x54520000
#define NNI_TRANSPORT_V1 0x54520001
#define NNI_TRANSPORT_VERSION NNI_TRANSPORT_V1
// Endpoint option handlers.
struct nni_tran_ep_option {
// eo_name is the name of the option.
const char *eo_name;
// eo_type is the type of the option.
int eo_type;
// eo_getopt retrieves the value of the option.
int (*eo_getopt)(void *, void *, size_t *, int);
// eo_set sets the value of the option. If the first argument
// (the endpoint) is NULL, then no actual set operation should be
// performed, but the option should be sanity tested for presence
// and size. (This permits the core to validate that an option
// is reasonable and be set even before endpoints are created.)
int (*eo_setopt)(void *, const void *, size_t, int);
};
// Endpoint operations are called by the socket in a protocol-independent
// fashion. The socket makes individual calls, which are expected to block
// if appropriate (except for destroy), or run asynchronously if an aio
// is provided. Endpoints are unable to call back into the socket, to prevent
// recusive entry and deadlock.
//
// For a given endpoint, the framework holds a lock so that each entry
// point is run exclusively of the others. (Transports must still guard
// against any asynchronous operations they manage themselves, though.)
struct nni_tran_ep_ops {
// ep_init creates a vanilla endpoint. The value created is
// used for the first argument for all other endpoint functions.
int (*ep_init)(void **, nni_url *, nni_sock *, int);
// ep_fini frees the resources associated with the endpoint.
// The endpoint will already have been closed.
void (*ep_fini)(void *);
// ep_connect establishes a connection. It can return errors
// NNG_EACCESS, NNG_ECONNREFUSED, NNG_EBADADDR, NNG_ECONNFAILED,
// NNG_ETIMEDOUT, and NNG_EPROTO.
void (*ep_connect)(void *, nni_aio *);
// ep_bind just does the bind() and listen() work,
// reserving the address but not creating any connections.
// It should return NNG_EADDRINUSE if the address is already
// taken. It can also return NNG_EBADADDR for an unsuitable
// address, or NNG_EACCESS for permission problems.
int (*ep_bind)(void *);
// ep_accept accepts an inbound connection.
void (*ep_accept)(void *, nni_aio *);
// ep_close stops the endpoint from operating altogether. It does
// not affect pipes that have already been created. It is nonblocking.
void (*ep_close)(void *);
// ep_stop stops the endpoint, and *waits* for any outstanding
// aio operations to complete.
void (*ep_stop)(void *);
// ep_options is an array of endpoint options. The final element must
// have a NULL name. If this member is NULL, then no transport specific
// options are available.
nni_tran_ep_option *ep_options;
};
// Pipe option handlers. We only have get for pipes; once a pipe is created
// no options may be set on it.
struct nni_tran_pipe_option {
// po_name is the name of the option.
const char *po_name;
// po_type is the type of the option.
int po_type;
// po_getopt retrieves the value of the option.
int (*po_getopt)(void *, void *, size_t *, int);
};
// Pipe operations are entry points called by the socket. These may be called
// with socket locks held, so it is forbidden for the transport to call
// back into the socket at this point. (Which is one reason pointers back
// to socket or even enclosing pipe state, are not provided.)
struct nni_tran_pipe_ops {
// p_fini destroys the pipe. This should clean up all local
// resources, including closing files and freeing memory, used by
// the pipe. After this call returns, the system will not make
// further calls on the same pipe.
void (*p_fini)(void *);
// p_start starts the pipe running. This gives the transport a
// chance to hook into any transport specific negotiation phase.
// The pipe will not have its p_send or p_recv calls started, and
// will not be access by the "socket" until the pipe has indicated
// its readiness by finishing the aio.
void (*p_start)(void *, nni_aio *);
// p_stop stops the pipe, waiting for any callbacks that are
// outstanding to complete. This is done before tearing down
// resources with p_fini.
void (*p_stop)(void *);
// p_aio_send queues the message for transmit. If this fails, then
// the caller may try again with the same message (or free it). If
// the call succeeds, then the transport has taken ownership of the
// message, and the caller may not use it again. The transport will
// have the responsibility to free the message (nng_msg_free()) when
// it is finished with it.
void (*p_send)(void *, nni_aio *);
// p_recv schedules a message receive. This will be performed even for
// cases where no data is expected, to allow detection of a remote
// disconnect.
void (*p_recv)(void *, nni_aio *);
// p_close closes the pipe. Further recv or send operations should
// return back NNG_ECLOSED.
void (*p_close)(void *);
// p_peer returns the peer protocol. This may arrive in whatever
// transport specific manner is appropriate.
uint16_t (*p_peer)(void *);
// p_options is an array of pipe options. The final element must have
// a NULL name. If this member is NULL, then no transport specific
// options are available.
nni_tran_pipe_option *p_options;
};
// These APIs are used by the framework internally, and not for use by
// transport implementations.
extern nni_tran *nni_tran_find(nni_url *);
extern int nni_tran_chkopt(const char *, const void *, size_t, int);
extern int nni_tran_sys_init(void);
extern void nni_tran_sys_fini(void);
extern int nni_tran_register(const nni_tran *);
#endif // CORE_TRANSPORT_H
|