When building a solution, the NK compiler uses the EDL, CDL and IDL descriptions to generate a set of special methods and types that simplify the creation, forwarding, receipt and processing of IPC messages.
As an example, we will examine the Server
process class that provides the FS
endpoint, which contains a single Open()
method:
Server.edl
entity Server
/* OpsComp is the named instance of the Operations component */
components {
OpsComp: Operations
}
Operations.cdl
component Operations
/* FS is the local name of the endpoint implementing the Filesystem interface */
endpoints {
FS: Filesystem
}
Filesystem.idl
package Filesystem
interface {
Open(in string<256> name, out UInt32 h);
}
These descriptions will be used to generate the files named Server.edl.h
, Operations.cdl.h
, and Filesystem.idl.h
, which contain the following methods and types:
Methods and types that are common to the client and server
In our example, one abstract interface (Filesystem
) will be generated:
typedef struct Filesystem {
const struct Filesystem_ops *ops;
} Filesystem;
typedef nk_err_t
Filesystem_Open_fn(struct Filesystem *, const
struct Filesystem_Open_req *,
const struct nk_arena *,
struct Filesystem_Open_res *,
struct nk_arena *);
typedef struct Filesystem_ops {
Filesystem_Open_fn *Open;
} Filesystem_ops;
When calling an interface method, the corresponding values of the RIID and MID are automatically inserted into the request.
In our example, a single Filesystem_Open
interface method will be generated:
nk_err_t Filesystem_Open(struct Filesystem *self,
struct Filesystem_Open_req *req,
const
struct nk_arena *req_arena,
struct Filesystem_Open_res *res,
struct nk_arena *res_arena)
Methods and types used only on the client
A proxy object is used as an argument in an interface method. In our example, a single Filesystem_proxy
proxy object type will be generated:
typedef struct Filesystem_proxy {
struct Filesystem base;
struct nk_transport *transport;
nk_iid_t iid;
} Filesystem_proxy;
In our example, the single initializing function Filesystem_proxy_init
will be generated:
void Filesystem_proxy_init(struct Filesystem_proxy *self,
struct nk_transport *transport,
nk_iid_t iid)
In our example, two such types will be generated: Filesystem_Open_req
(for a request) and Filesystem_Open_res
(for a response).
typedef struct __nk_packed Filesystem_Open_req {
__nk_alignas(8)
struct nk_message base_;
__nk_alignas(4) nk_ptr_t name;
} Filesystem_Open_req;
typedef struct Filesystem_Open_res {
union {
struct {
__nk_alignas(8)
struct nk_message base_;
__nk_alignas(4) nk_uint32_t h;
};
struct {
__nk_alignas(8)
struct nk_message base_;
__nk_alignas(4) nk_uint32_t h;
} res_;
struct Filesystem_Open_err err_;
};
} Filesystem_Open_res;
Methods and types used only on the server
If there are embedded components, this type also contains their instances, and the initializing function takes their corresponding initialized structures. Therefore, if embedded components are present, their initialization must begin with the most deeply embedded component.
In our example, the Operations_component
structure and Operations_component_init
function will be generated:
typedef struct Operations_component {
struct Filesystem *FS;
};
void Operations_component_init(struct Operations_component *self,
struct Filesystem *FS)
In our example, the Server_entity
structure and Server_entity_init
function will be generated:
#define Server_entity Server_component
typedef struct Server_component {
struct : Operations_component *OpsComp;
} Server_component;
void Server_entity_init(struct Server_entity *self,
struct Operations_component *OpsComp)
In our example, two such types will be generated: Filesystem_req
(for a request) and Filesystem_res
(for a response).
typedef union Filesystem_req {
struct nk_message base_;
struct Filesystem_Open_req Open;
};
typedef union Filesystem_res {
struct nk_message base_;
struct Filesystem_Open_res Open;
};
If embedded components are present, these types also contain structures of the constant part of a message for any method of any endpoint included in all embedded components.
In our example, two such types will be generated: Operations_component_req
(for a request) and Operations_component_res
(for a response).
typedef union Operations_component_req {
struct nk_message base_;
Filesystem_req FS;
} Operations_component_req;
typedef union Operations_component_res {
struct nk_message base_;
Filesystem_res FS;
} Operations_component_res;
If embedded components are present, these types also contain structures of the constant part of a message for any method of any endpoint included in all embedded components.
In our example, two such types will be generated: Server_entity_req
(for a request) and Server_entity_res
(for a response).
#define Server_entity_req Server_component_req
typedef union Server_component_req {
struct nk_message base_;
Filesystem_req OpsComp_FS;
} Server_component_req;
#define Server_entity_res Server_component_res
typedef union Server_component_res {
struct nk_message base_;
Filesystem_res OpsComp_FS;
} Server_component_res;
Dispatchers analyze the received query (the RIID and MID values), call the implementation of the corresponding method, and then save the response in the buffer. In our example, three dispatchers will be generated: Filesystem_interface_dispatch
, Operations_component_dispatch
, and Server_entity_dispatch
.
The process class dispatcher handles the request and calls the methods implemented by this class. If the request contains an incorrect RIID (for example, an RIID for a different endpoint that this process class does not have) or an incorrect MID, the dispatcher returns NK_EOK
or NK_ENOENT
.
nk_err_t Server_entity_dispatch(struct Server_entity *self,
const
struct nk_message *req,
const
struct nk_arena *req_arena,
struct nk_message *res,
struct nk_arena *res_arena)
In special cases, you can use dispatchers of the interface and the component. They take an additional argument: interface implementation ID (nk_iid_t
). The request will be handled only if the passed argument and RIID from the request match, and if the MID is correct. Otherwise, the dispatchers return NK_EOK
or NK_ENOENT
.
nk_err_t Operations_component_dispatch(struct Operations_component *self,
nk_iid_t iidOffset,
const
struct nk_message *req,
const
struct nk_arena *req_arena,
struct nk_message *res,
struct nk_arena *res_arena)
nk_err_t Filesystem_interface_dispatch(struct Filesystem *impl,
nk_iid_t iid,
const
struct nk_message *req,
const
struct nk_arena *req_arena,
struct nk_message *res,
struct nk_arena *res_arena)