diff --git a/doc/Nodes.md b/doc/Nodes.md index 9b59dfbf7..12da03b73 100644 --- a/doc/Nodes.md +++ b/doc/Nodes.md @@ -27,4 +27,7 @@ Every node is an instance of a node-type. VILLASnode currently supports the foll - NI LabView RT-targets #### @subpage cbuilder -- RTDS CBuilder Control System components \ No newline at end of file +- RTDS CBuilder Control System components + +#### @subpage shmem +- POSIX shared memory interface diff --git a/doc/nodes/Shmem.md b/doc/nodes/Shmem.md new file mode 100644 index 000000000..0faea2ec5 --- /dev/null +++ b/doc/nodes/Shmem.md @@ -0,0 +1,55 @@ +# Shared memory {#shmem} + +The `shmem` node-type can be used to quickly exchange samples with a process on the same host using a POSIX shared memory object. + +## Configuration + +The only required configuration option is the `name` option; all others are optional with reasonable defaults. + +#### `name` (string) + +Name of the POSIX shared memory object. Must start with a forward slash (`/`). +The same name should be passed to the external program somehow in its +configuration or command-line arguments. + +#### `queuelen` (int) + +Length of the input and output queues in elements. Defaults to `DEFAULT_SHMEM_QUEUELEN`, +a compile-time constant. + +#### `samplelen` (int) + +Maximum number of data elements in a single `struct sample` for the samples handled +by this node. Defaults to `DEFAULT_SHMEM_SAMPLELEN`, a compile-time constant. + +#### `polling` (boolean) + +If set to false, POSIX condition variables are used to signal writes between processes. +If set to true, no CV's are used, meaning that blocking writes have to be +implemented using polling, leading to performance improvements at a cost of +unnecessary CPU usage. Defaults to false. + +#### `exec` (array of strings) + +Optional name and command-line arguments (as passed to `execve`) of a command +to be executed during node startup. This can be used to start the external +program directly from VILLASNode. If unset, no command is executed. + +## API for external programs + +The actual sharing of data is implemented by putting two shared `struct queue`s +(one per direction) and an associated `struct pool` in the shared memory region. +Samples can be exchanged by simply writing to and reading from these queues. + +External programs that want to use this interface can link against +`libvillas-ext.so`. This library provides a subset of the functions from +`libvillas.so` that can be used to access and modify the shared data structures. + +The interface for external programs is very simple: after opening the shared +memory object with `shmem_shared_open` (passing the object name from the +configuration file), samples can be read from and written to VILLASNode using +`shmem_shared_read` and `shmem_shared_write`, respectively. Samples written to +the node must be allocated by `sample_alloc` from the shared pool; samples read +from the node should be freed with `sample_put` after they have been processed. +See the [example client](test-shmem_8c_source.html) and the [API +documentation](group__shmem.html) for more details. diff --git a/include/villas/nodes/shmem.h b/include/villas/nodes/shmem.h index dfa4d9692..ddfa278f4 100644 --- a/include/villas/nodes/shmem.h +++ b/include/villas/nodes/shmem.h @@ -7,7 +7,7 @@ /** * @ingroup node - * @addtogroup shmem Shared memory interface + * @addtogroup shmem_node Shared memory node type * @{ */ @@ -22,6 +22,9 @@ #define DEFAULT_SHMEM_QUEUELEN 512 #define DEFAULT_SHMEM_SAMPLELEN DEFAULT_SAMPLELEN +/** Node-type for shared memory communication. + * @see node_type + */ struct shmem { const char* name; /**< Name of the shm object. */ int samplelen; /**< Number of data entries for each sample. */ @@ -47,4 +50,4 @@ int shmem_read(struct node *n, struct sample *smps[], unsigned cnt); int shmem_write(struct node *n, struct sample *smps[], unsigned cnt); -/** @} */ \ No newline at end of file +/** @} */ diff --git a/include/villas/shmem.h b/include/villas/shmem.h index ce88a9fa8..271654495 100644 --- a/include/villas/shmem.h +++ b/include/villas/shmem.h @@ -5,6 +5,12 @@ * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC *********************************************************************************/ +/** Interface to the shared memory node for external programs. + * + * @addtogroup shmem Shared memory interface + * @{ + */ + #pragma once #include "pool.h" @@ -37,28 +43,34 @@ struct shmem_shared { /** Open the shared memory object and retrieve / initialize the shared data structures. * @param[in] name Name of the POSIX shared memory object. * @param[inout] base_ptr The base address of the shared memory region is written to this pointer. - * @retval A valid shmem_shared* on success, or NULL with errno indicating the error on failure. + * @retval NULL An error occurred; errno is set appropiately. */ struct shmem_shared * shmem_shared_open(const char* name, void **base_ptr); /** Close and destroy the shared memory object and related structures. - * @param[shm] The shared memory structure. - * @param[base] The base address as returned by shmem_shared_open. + * @param shm The shared memory structure. + * @param base The base address as returned by shmem_shared_open. + * @retval 0 Closing successfull. + * @retval <0 An error occurred; errno is set appropiately. */ int shmem_shared_close(struct shmem_shared *shm, void *base); /** Read samples from VILLASNode. - * @param[shm] The shared memory structure. - * @param[smps] An array where the pointers to the samples will be written. The samples + * @param shm The shared memory structure. + * @param smps An array where the pointers to the samples will be written. The samples * must be freed with sample_put after use. - * @param[cnt] Number of samples to be read. + * @param cnt Number of samples to be read. + * @retval >=0 Number of samples that were read. Can be less than cnt (including 0) in case not enough samples were available. + * @retval -1 VILLASNode exited; no samples can be read anymore. */ int shmem_shared_read(struct shmem_shared *shm, struct sample *smps[], unsigned cnt); /** Write samples to VILLASNode. - * @param[shm] The shared memory structure. - * @param[smps] The samples to be written. Must be allocated from shm->pool. - * @param[cnt] Number of samples to write. + * @param shm The shared memory structure. + * @param smps The samples to be written. Must be allocated from shm->pool. + * @param cnt Number of samples to write. + * @retval >=0 Number of samples that were successfully written. Can be less than cnt (including 0) in case of a full queue. + * @retval -1 VILLASNode exited; no samples can be written anymore. */ int shmem_shared_write(struct shmem_shared *shm, struct sample *smps[], unsigned cnt); @@ -66,3 +78,5 @@ int shmem_shared_write(struct shmem_shared *shm, struct sample *smps[], unsigned * the input/output queues (in elements) and the given number of data elements * per struct sample. */ size_t shmem_total_size(int insize, int outsize, int sample_size); + +/** @} */ diff --git a/lib/nodes/shmem.c b/lib/nodes/shmem.c index e6ff1d916..4b4f9cb3c 100644 --- a/lib/nodes/shmem.c +++ b/lib/nodes/shmem.c @@ -1,5 +1,6 @@ /** Node-type for shared memory communication. * + * @file * @author Georg Martin Reinke * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC *********************************************************************************/ @@ -159,7 +160,7 @@ int shmem_read(struct node *n, struct sample *smps[], unsigned cnt) int shmem_write(struct node *n, struct sample *smps[], unsigned cnt) { struct shmem *shm = n->_vd; - struct sample *shared_smps[cnt]; /**< Samples need to be copied to the shared pool first */ + struct sample *shared_smps[cnt]; /* Samples need to be copied to the shared pool first */ int avail, pushed, len; avail = sample_alloc(&shm->shared->pool, shared_smps, cnt);