aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichał Łyszczek <michal.lyszczek@bofc.pl>2018-09-29 16:01:41 +0200
committerMichał Łyszczek <michal.lyszczek@bofc.pl>2018-09-29 16:01:41 +0200
commit4581704245cbdd5b1e16aac10620f7c16c2a58d2 (patch)
tree6552087725a6474a8b24a198e9a44089b36596ed
parent555a8fabdaa3a6c303d9456e8ce9e2966ee2c3cc (diff)
downloadlibrb-4581704245cbdd5b1e16aac10620f7c16c2a58d2.tar.gz
librb-4581704245cbdd5b1e16aac10620f7c16c2a58d2.tar.bz2
librb-4581704245cbdd5b1e16aac10620f7c16c2a58d2.zip
fix: rb_posix_* functions should only be used when objsize is 1
It is to prevent data loss in corner cases with partial read() and write() functions
-rw-r--r--man/rb_posix_read.3313
-rw-r--r--man/rb_posix_recv.32
-rw-r--r--man/rb_posix_send.32
-rw-r--r--man/rb_posix_write.3311
-rw-r--r--man/rb_read.3175
-rw-r--r--man/rb_write.3172
-rw-r--r--rb.c2
-rw-r--r--tests.c130
8 files changed, 673 insertions, 434 deletions
diff --git a/man/rb_posix_read.3 b/man/rb_posix_read.3
index 9948d24..5e6de18 100644
--- a/man/rb_posix_read.3
+++ b/man/rb_posix_read.3
@@ -1 +1,312 @@
-.so man3/rb_read.3
+.TH "rb_posix_read" "3" "29 September 2018 (v1.0.0)" "bofc.pl"
+.SH NAME
+.PP
+.BR rb_posix_read ,
+.B rb_posix_recv
+- functions to directly send data from ring buffer to object associated with
+file descriptor.
+.SH SYNOPSIS
+.PP
+.BI "#include <librb.h>"
+.PP
+.BI "long rb_posix_read(struct rb *" rb ", int " fd ", size_t " count ");"
+.br
+.BI "long rb_posix_recv(struct rb *" rb ", int " fd ", size_t " count ", \
+unsigned long " flags ");"
+.PP
+Feature Test Macro:
+.PP
+.BR rb_posix_read (),
+.BR rb_posix_recv ():
+.RS
+ENABLE_POSIX_CALLS
+.RE
+.SH DESCRIPTION
+.PP
+.BR rb_posix_read (3)
+copies at most
+.I count
+.B bytes
+from ring buffer
+.I rb
+to object associated with
+.I fd
+file descriptor.
+.PP
+.BR rb_posix_read (3)
+works in the same way as
+.BR rb_read (3)
+but instead of copying data to
+.I buffer
+it copies data to whatever is pointed by
+.I fd
+file descriptor.
+This may be ordinary file on disk, or pipe, or serial device, or network socket.
+This is convenient function that may be useful when constructing packet frame to
+send. You could first prepare packet byte by byte with
+.BR rb_write (3)
+and then send whole packet with
+.BR rb_posix_read (3)
+function in on single burst.
+Calling multiple
+.BR rb_write (3)
+followed by single
+.BR rb_posix_read (3)
+to send frame, will be way faster than calling multiple
+.BR write ()
+system call functions.
+.PP
+Another change is that while ordinary
+.B rb_read (3)
+works on
+.BR elements ,
+.B rb_posix_read (3)
+works solely on
+.B bytes
+and thus
+.I rb
+object
+.B must
+be created with
+.I object_size
+set to 1 or these functions will return
+.B EINVAL
+error.
+This is due to the fact, that
+.B rb
+uses
+.BR write ()
+under the hood which works on bytes.
+Reason for such limitation is that
+.BR write ()
+may return after writing two and a half of object size and second half of
+third object would be lost causing mayheem in communication stream.
+Fixing that would require blocking calling thread and
+.B rb
+cannot make that such decision for the user.
+So if you want to send 5 frames of data in one call, you need to do
+.BR "rb_posix_read(rb, fd, 5 * sizeof(data_frame))" .
+.PP
+.BR rb_posix_recv (3)
+work the same as
+.BR rb_posix_read (3)
+but it also accepts
+.IR flags .
+Possible
+.I flags
+are:
+.TP
+.B MSG_DONTWAIT
+Only works in multi threaded environment, on single threaded mode this is
+default.
+When passed and
+.I rb
+contains less
+.B bytes
+than passed in
+.IR count ,
+function will copy all bytes from
+.I rb
+into
+.I fd
+and will return immediately.
+This means, function will never block, no matter what.
+Not recommended when multiple threads calls
+.BR rb_read (3)
+with this flag - may lead to interlaced reads
+.TP
+.B MSG_PEEK
+Reads from
+.I rb
+into
+.I fd
+as normal, but doesn't remove data from
+.IR rb ,
+so consecutive calls to this function will return same data (provided
+that nobody called
+.BR rb_posix_recv (3)
+without this flag).
+When this is called in threaded environment enabled, functions will act as if
+.B MSG_DONTWAIT
+flag was also passed.
+It is guaranteed that calling function with this flag, will not alter internal
+state of
+.IR rb object .
+.SH EXAMPLE
+.PP
+Reading frames by multiple parsers.
+Error handling ommited for clarity.
+.I rb
+object is assumed to be created with
+.B object_size
+equals to 1.
+.EX
+.PP
+ void *parser(void *arg)
+ {
+ struct rb *rb = arg;
+
+ for (;;)
+ {
+ struct frame f;
+
+ /* when rb was created with O_MULTITHREAD, this function
+ * is guaranteed to store exactly sizeof(f) data, and
+ * this data will be continous, that is no other parser
+ * thread will wake up, and take any byte in the middle
+ * of our read (that is if rb_read doesn't return -1)
+ */
+
+ if (rb_read(rb, &f, sizeof(f)) == -1)
+ {
+ if (errno == ECANCELED)
+ {
+ /* rb_stop() has been called, rb object should
+ * not be used any more
+ */
+
+ break;
+ }
+
+ /* some other error */
+ }
+
+ /* f object will be valid here, some frame processing
+ * stuff can be made here
+ */
+ }
+
+ /* perform cleanup */
+ return NULL;
+ }
+
+ void loop(struct rb *rb)
+ {
+ int fd, i;
+ pthread_t parsers[4];
+
+ fd = init_client_socket();
+
+ for(i = 0; i != 4; ++i)
+ {
+ /* create parser threads, they will block in rb_read()
+ * as we didn't put any data to rb yet
+ */
+
+ pthread_create(&parsers[i], NULL, parse, rb);
+ }
+
+ for (;;)
+ {
+ /* read data from fd socket and write them to rb
+ * for further processing, we set count to max
+ * value for performance as we don't need it to
+ * return (unless error occurs)
+ *
+ * NOTE: rb will now be locked from writing, only
+ * this thread will be able to write to rb, don't
+ * try to call another rb_write() function, it will
+ * be blocked until this function returns - and it
+ * may take a while to read LONG_MAX bytes.
+ *
+ * LONG_MAX is passed as count because we want this
+ * function to be called as rarely as possible, and
+ * still we don't want to overflow internal integer.
+ *
+ * You could also pass '-1' here, it would be casted
+ * to unsigned type and the it would be reduced to
+ * LONG_MAX anyway.
+ */
+
+ if (rb_posix_write(rb, fd, LONG_MAX) == -1)
+ {
+ if (errno == ECANCELED)
+ {
+ /* rb_stop() has been called, we shouldn't
+ * use rb object anymore
+ */
+
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i != 4; ++i)
+ {
+ /* join all parsers, so rb_destory() can be safely
+ * called
+ */
+
+ pthread_join(parsers[i], NULL);
+ }
+ }
+.EE
+.SH RETURN VALUES
+.PP
+On successfull read, function will return number of
+.B bytes
+it read from
+.I rb
+object and stored in
+.I fd
+file descriptor.
+Returned value can be less than
+.I count
+if
+.I rb
+doesn't contain enough data and function operates in non blocking mode.
+On errors function returns -1, in such case,
+.I rb
+is left intact.
+.SH ERRORS
+.TP
+.B EINVAL
+Any of the passed pointers is NULL.
+.B EINVAL
+.BR rb_posix_read (3)
+was called when
+.I rb
+object was created with
+.B object_size
+different than 1.
+.TP
+.B EAGAIN
+This error will be set, when
+.I rb
+is operating in non blocking mode, and there is no data to be read from
+.I rb
+immediately.
+.TP
+.B ECANCELED
+.BR rb_stop (3)
+was called, and operation was cancelled, because
+.I rb
+object is about to be destroyed.
+You should not access
+.I rb
+object after you receive this error.
+Otherwise you will probably get deadlock or application will crash.
+Returned only if threads are enabled.
+.TP
+.B ENOSYS
+Function is not implemented (was not compiled in).
+.SH SEE ALSO
+.PP
+.BR rb_overview (7),
+.BR rb_new (3),
+.BR rb_init (3),
+.BR rb_destroy (3),
+.BR rb_cleanup (3),
+.BR rb_discard (3),
+.BR rb_stop (3),
+.BR rb_stop_signal (3),
+.BR rb_write (3),
+.BR rb_send (3),
+.BR rb_posix_write (3),
+.BR rb_posix_send (3),
+.BR rb_clear (3),
+.BR rb_count (3),
+.BR rb_space (3),
+.BR rb_header_size (3),
+.BR rb_array_size (3),
+.BR rb_version (3)
diff --git a/man/rb_posix_recv.3 b/man/rb_posix_recv.3
index 9948d24..d9369e1 100644
--- a/man/rb_posix_recv.3
+++ b/man/rb_posix_recv.3
@@ -1 +1 @@
-.so man3/rb_read.3
+.so man3/rb_posix_read.3
diff --git a/man/rb_posix_send.3 b/man/rb_posix_send.3
index d97fa6d..dbe7258 100644
--- a/man/rb_posix_send.3
+++ b/man/rb_posix_send.3
@@ -1 +1 @@
-.so man3/rb_write.3
+.so man3/rb_posix_write.3
diff --git a/man/rb_posix_write.3 b/man/rb_posix_write.3
index d97fa6d..4a826b2 100644
--- a/man/rb_posix_write.3
+++ b/man/rb_posix_write.3
@@ -1 +1,310 @@
-.so man3/rb_write.3
+.TH "rb_posix_write" "3" " 9 February 2018 (v1.0.0)" "bofc.pl"
+.SH NAME
+.PP
+.BR rb_posix_write ,
+.B rb_posix_send
+- functions to put data on ring buffer directly from object associated with
+file descriptor
+.SH SYNOPSIS
+.PP
+.BI "#include <librb.h>"
+.PP
+.BI "long rb_posix_write(struct rb *" rb ", int " fd ", \
+size_t " count ");"
+.br
+.BI "long rb_posix_send(struct rb *" rb ", int " fd ", \
+size_t " count ", unsigned long " flags ");"
+.PP
+Feature Test Macro:
+.PP
+.BR rb_posix_write (),
+.BR rb_posix_send ():
+.RS
+ENABLE_POSIX_CALLS
+.RE
+.SH DESCRIPTION
+.PP
+.BR rb_posix_write (3)
+copies at most
+.I count
+.B bytes
+from
+.I fd
+to
+.IR rb .
+.PP
+.BR rb_posix_write (3)
+works in the same way as
+.BR rb_write (3)
+but instead of copying data from
+.I buffer
+to
+.I rb
+object, it will copy data from whatever is associated with
+.I fd
+file descriptor to
+.I rb
+object.
+This may be ordinary file on disk, or pipe, or seria ldevice, or network socket.
+This is convenient function that may be useful when you want to read same frame
+from multiple sources - like allowing to control embedded device via serial,
+network and fifo simultaneously.
+For example you can create 3 threads that will read data to
+.I rb
+and then one thread that will parse it.
+This way you can get rid of polling each interface for data, which will speed
+up execution - check example below.
+.PP
+Another change is that while ordinary
+.BR rb_write (3)
+works on
+.BR elements ,
+.BR rb_posix_write (3)
+works solely on
+.B bytes
+and thus
+.I rb
+object
+.B must
+be created with
+.I object_size
+set to 1 or these function will return
+.B EINVAL
+error.
+This is due to the fact that
+.B rb
+uses
+.BR read ()
+under the hood which works on bytes.
+Reason for such limitaion is that
+.BR read ()
+may return after reading two and a half of object size and you would end up
+having 2 halfs of one frame stored as 2 separated frames.
+Fixing that would rquire blocking calling thread and
+.B rb
+cannot make such decision for the user.
+So if you want to read 5 frames of data in one call into
+.I rb
+you need to do
+.BR "rb_posix_write(rb, fd, 5 * sizeof(data_frame))".
+.PP
+It is valid for
+.BR rb_posix_write (3)
+to make early return with return value less than
+.I count
+bytes.
+This will happen when internal
+.BR write ()
+returns zero, which may mean we read end of the file, or remote computer closed
+network connection.
+.PP
+.BR rb_posix_send (3)
+work the same way as
+.BR rb_posix_write (3)
+but also accept following
+.IR flags :
+.TP
+.B MSG_DONTWAIT
+Only works in multi threaded environment, on single threaded mode this is
+default.
+When passed and
+.I rb
+contains less free
+.B bytes
+than passed in
+.IR count ,
+function will copy all
+.B bytes
+from
+.I fd
+into
+.I rb
+and will return immediately.
+Not recommended when multiple concurent threads calls
+.BR rb_posix_write (3)
+with this flag - may lead to interlaced reads.
+.SH EXAMPLE
+.PP
+Reading frames from multiple sockets and parse them in one thread.
+As usual, error handling ommited for clarity.
+It is assumend that
+.I rb
+was created with
+.B object_size
+equal to 1.
+.EX
+.PP
+ struct tdata
+ {
+ struct rb *rb;
+ int fd;
+ };
+
+ void *receiver(void *arg)
+ {
+ struct tdata *data = arg;
+
+ for (;;)
+ {
+ /* rb_posix_write() will sleep current thread until
+ * data shows up on data->fd file descriptor.
+ *
+ * NOTE: you should be carefull here, as calling this
+ * function on slow data->fd will block other threads.
+ * rb will try to call read() on data->fd and read()
+ * will block until any data shows on data->fd, and
+ * if data->fd is slow, this may take a long time.
+ */
+
+ if (rb_posix_write(data->rb, data->fd,
+ sizeof(struct data_frame) == -1)
+ {
+ if (errno == ECANCELED)
+ {
+ /* rb_stop() has been called, rb shouldn't
+ * be used any more
+ */
+ break;
+ }
+
+ /* some other error */
+ }
+ }
+ }
+
+ void loop(struct rb *rb)
+ {
+ struct tdata tdata[3];
+ pthread_t receivers[3];
+ int i;
+
+ tdata[0].rb = rb;
+ tdata[0].fd = get_network_socket();
+
+ tdata[1].rb = rb;
+ tdata[1].fd = get_serial_socket();
+
+ tdata[2].rb = rb;
+ tdata[2].fd = get_fifo_socket();
+
+ for (i = 0; i != 3; ++i)
+ {
+ /* create multiple threads that will read data from
+ * sockets and put that data into rb. Pretty nice
+ * way to eliminate polling, as these threads will
+ * wake only when there is data available
+ /
+
+ pthread_create(&receivers[i], NULL, receiver, &tdata[i]);
+ }
+
+ for (;;)
+ {
+ /* receivers are started and will populate our rb with
+ * frames over time, rb guarantees all frames here will
+ * be valid (if they come valid from socket that is)
+ */
+
+ struct data_frame f;
+
+ if (rb_read(rb, &f, sizeof(f)) == -1)
+ {
+ if (errno == ECANCELED)
+ {
+ /* rb_stop() has been called, we shouldn't
+ * use rb object anymore
+ */
+
+ break;
+ }
+ }
+
+ process_frame(&f);
+ }
+
+ for (i = 0; i != 3; ++i)
+ {
+ /* join all threads using rb object, so rb_destroy()
+ * can be called safely
+ */
+
+ pthread_join(receivers[i], NULL);
+ }
+ }
+
+.EE
+.SH RETURN VALUES
+.PP
+On successfull write, function will return number of
+.B bytes
+it stored in
+.IR rb .
+Returned value can be less than
+.I count
+if
+.I rb
+doesn't contain enough free space and function operates in non blocking mode.
+It is also possible that internal
+.BR read ()
+returned 0 (end of file or remote client closed network socket), in which case
+function will also return.
+It's also possible for these functions to return 0 in case when
+.BR read ()
+returned 0 and not data has been commited to
+.IR rb .
+On errors function returns -1, in such case,
+.I rb
+is left intact.
+.SH ERRORS
+.TP
+.B EINVAL
+Any of the passed pointers is NULL
+.TP
+.B EINVAL
+.I rb
+object was created with
+.B object_size
+different than 1.
+.TP
+.B EAGAIN
+This error will be set, when
+.I rb
+is operating in non blocking mode, and there is no place in
+.I rb
+to write data from
+.I fd
+immediately.
+.TP
+.B ECANCELED
+.BR rb_stop (3)
+was called, and operation was cancelled, because
+.I rb
+object is abou to be destroyed.
+You should not access
+.I rb
+object after you receive this error.
+Otherwise you will probably get deadlock or application will crash.
+Returned only if threads are enabled.
+.TP
+.B ENOSYS
+Function is not implemented (was not compiled in).
+.SH SEE ALSO
+.PP
+.BR rb_overview (7),
+.BR rb_new (3),
+.BR rb_init (3),
+.BR rb_destroy (3),
+.BR rb_cleanup (3),
+.BR rb_discard (3),
+.BR rb_stop (3),
+.BR rb_stop_signal (3),
+.BR rb_read (3),
+.BR rb_recv (3),
+.BR rb_posix_read (3),
+.BR rb_posix_recv (3),
+.BR rb_clear (3),
+.BR rb_count (3),
+.BR rb_space (3),
+.BR rb_header_size (3),
+.BR rb_array_size (3),
+.BR rb_version (3)
diff --git a/man/rb_read.3 b/man/rb_read.3
index 98df72e..c7a0fdf 100644
--- a/man/rb_read.3
+++ b/man/rb_read.3
@@ -2,9 +2,7 @@
.SH NAME
.PP
.BR rb_read ,
-.BR rb_recv ,
-.BR rb_posix_read ,
-.B rb_posix_recv
+.B rb_recv
- functions to retrieve data from ring buffer
.SH SYNOPSIS
.PP
@@ -14,19 +12,7 @@
.br
.BI "long rb_recv(struct rb *" rb ", void *" buffer ", size_t " count ", \
unsigned long " flags ");"
-.br
-.BI "long rb_posix_read(struct rb *" rb ", int " fd ", size_t " count ");"
-.br
-.BI "long rb_posix_recv(struct rb *" rb ", int " fd ", size_t " count ", \
-unsigned long " flags ");"
.PP
-Feature Test Macro:
-.PP
-.BR rb_posix_read (),
-.BR rb_posix_recv ():
-.RS
-ENABLE_POSIX_CALLS
-.RE
.SH DESCRIPTION
.PP
.BR rb_read (3)
@@ -70,7 +56,9 @@ it will be set to
.B LONG_MAX
internaly anyway.
Also make sure that formula
-.IR count * rb->object_size
+.I count
+*
+.I rb->object_size
does not exceed
.B size_t
type or you will overflow internal integer and that will result in Undefined
@@ -90,7 +78,7 @@ as there are inside
.I rb
and will return.
If
-.BR read (3)
+.BR rb_read (3)
is called when buffer is empty,
.B EAGAIN
error will be returned.
@@ -115,67 +103,38 @@ to write data to buffer and total
have been copied into
.IR buffer .
.PP
-.BR rb_posix_read (3)
-works in the same way as
-.BR rb_read (3)
-but instead of copying data to
-.I buffer
-it copies data to whatever is pointed by
-.I fd
-file descriptor.
-This may be ordinary file on disk, or pipe, or serial device, or network socket.
-This is convenient function that may be useful when constructing packet frame to
-send. You could first prepare packet byte by byte with
-.BR rb_write (3)
-and then send whole packet with
-.BR rb_posix_read (3)
-function in on single burst.
-Calling multiple
-.BR rb_write (3)
-followed by single
-.BR rb_posix_read (3)
-to send frame, will be way faster than calling multiple
-.BR write ()
-system call functions.
-.PP
When multiple threads access single
.I rb
object in blocking mode, it is guaranteed that each thread will write from
.I rb
to
.I buffer
-or
-.I fd
continous data.
Consider
.I rb
-object with 10 message in the buffer each containing 20 bytes frame.
+object with 10 message in the
+.I rb
+each containing 20 bytes frame.
Now 4 threads simultaneously calls
-.BR rb_posix_read (3)
-to write data from
+.BR rb_read (3)
+to read data from
.I rb
to
-.I buffer
-or
-.IR fd.
+.IR buffer .
In such scenario
.B rb
guarantees all frames will be read from
.I rb
-in order without interlace even when socket associated with
-.I fd
-is full and doesn't accept
-.B write()
-at the moment.
+in order without interlace even when
+.I rb
+is currently empty and waits for
+.BR rb_write (3)
+to feed it some data.
.PP
.BR rb_recv (3)
-and
-.BR rb_posix_recv (3)
work the same as
.BR rb_read (3)
-and
-.BR rb_posix_read (3)
-but they also accepts
+but it also accepts
.IR flags .
Possible
.I flags
@@ -217,102 +176,6 @@ flag was also passed.
It is guaranteed that calling function with this flag, will not alter internal
state of
.I rb object.
-.SH EXAMPLE
-.PP
-Reading frames by multiple parsers. Error handling ommited for clarity.
-.EX
-.PP
- void *parser(void *arg)
- {
- struct rb *rb = arg;
-
- for (;;)
- {
- struct frame f;
-
- /* when rb was created with O_MULTITHREAD, this function
- * is guaranteed to store exactly sizeof(f) data, and
- * this data will be continous, that is no other parser
- * thread will wake up, and take any byte in the middle
- * (that is if rb_read doesn't return -1)
- */
-
- if (rb_read(rb, &f, sizeof(f)) == -1)
- {
- if (errno == ECANCELED)
- {
- /* rb_stop() has been called, rb object should
- * not be used any more
- */
-
- break;
- }
- }
-
- /* f object will be valid here, some frame processing
- * stuff can be made here
- */
- }
-
- /* perform cleanup */
- return NULL;
- }
-
- void loop(struct rb *rb)
- {
- int fd, i;
- pthread_t parsers[4];
-
- fd = init_client_socket();
-
- for(i = 0; i != 4; ++i)
- {
- pthread_create(&parsers[i], NULL, parse, rb);
- }
-
- for (;;)
- {
- /* read data from fd socket and write them to rb
- * for further processing, we set count to max
- * value for performance as we don't need it to
- * return (unless error occurs)
- *
- * NOTE: rb will now be locked from writing, only
- * this thread will be able to write to rb, don't
- * try to call another rb_write() function, it will
- * be blocked until this function returns - and it
- * may take a while to read LONG_MAX / OBJECT_SIZE.
- *
- * LONG_MAX / OBJECT_SIZE is passed as count because
- * we want this function to be called as rarely as
- * possible, and still we don't want to overflow
- * internal integer. OBJECT_SIZE is size of a single
- * rb object set during creation of rb
- */
-
- if (rb_posix_write(rb, fd, LONG_MAX / OBJECT_SIZE) == -1)
- {
- if (errno == ECANCELED)
- {
- /* rb_stop() has been called, we shouldn't
- * use rb object anymore
- */
-
- break;
- }
- }
- }
-
- for (i = 0; i != 4; ++i)
- {
- /* join all parsers, so rb_destory() can be safely
- * called
- */
-
- pthread_join(parsers[i], NULL);
- }
- }
-.EE
.SH RETURN VALUES
.PP
On successfull read, function will return number of
@@ -324,7 +187,6 @@ Returned value can be less than
if
.I rb
doesn't contain enough data and function operates in non blocking mode.
-In such case it is also ok for function to return 0 - meaning buffer is empty.
On errors function returns -1, in such case,
.I rb
buffer is left intact.
@@ -350,9 +212,6 @@ You should not access
object after you receive this error.
Otherwise you will probably get deadlock or application will crash.
Returned only if threads are enabled.
-.TP
-.B ENOSYS
-Function is not implemented (was not compiled in).
.SH SEE ALSO
.PP
.BR rb_overview (7),
diff --git a/man/rb_write.3 b/man/rb_write.3
index 2c87ca4..50741e2 100644
--- a/man/rb_write.3
+++ b/man/rb_write.3
@@ -2,9 +2,7 @@
.SH NAME
.PP
.BR rb_write ,
-.BR rb_send ,
-.BR rb_posix_write ,
-.B rb_posix_send
+.B rb_send
- functions to put data on ring buffer
.SH SYNOPSIS
.PP
@@ -15,20 +13,6 @@ size_t " count ");"
.br
.BI "long rb_send(struct rb *" rb ", const void *" buffer ", \
size_t " count ", unsigned long " flags ");"
-.br
-.BI "long rb_posix_write(struct rb *" rb ", int " fd ", \
-size_t " count ");"
-.br
-.BI "long rb_posix_send(struct rb *" rb ", int " fd ", \
-size_t " count ", unsigned long " flags ");"
-.PP
-Feature Test Macro:
-.PP
-.BR rb_posix_write (),
-.BR rb_posix_send ():
-.RS
-ENABLE_POSIX_CALLS
-.RE
.SH DESCRIPTION
.PP
.BR rb_write (3)
@@ -73,7 +57,9 @@ it will be set to
.B LONG_MAX
internaly anyway.
Also make sure that formula
-.IR count * rb->object_size
+.I count
+*
+.I rb->object_size
does not exceed
.B size_t
type or you will overflow internal integer and that will result in Undefined
@@ -95,7 +81,7 @@ as there is space for inside
.B rb
and will immediately return.
If
-.BR write (3)
+.BR rb_write (3)
is called when buffer is full,
.B EAGAIN
error will be returned.
@@ -116,39 +102,6 @@ use no CPU - until someone else calls
.BR rb_read (3)
to read data from buffer and makes some space.
.PP
-.BR rb_posix_write (3)
-works in the same way as
-.BR rb_write (3)
-but instead of copying data from
-.I buffer
-to
-.I rb
-object, it will copy data from whatever is associated with
-.I fd
-file descriptor
-.I rb
-object.
-This may be ordinary file on disk, or pipe, or seria ldevice, or network socket.
-This is convenient function that may be useful when you want to read same frame
-from multiple sources - like allowing to control embedded device via serial,
-network and fifo simultaneously.
-You can create 3 threads that will read data
-to
-.I rb
-and then one thread that will parse it.
-This way you can get rid of polling each interface for data, which will speed
-up execution - check example below.
-.PP
-It is valid for
-.BR rb_posix_write (3)
-to make early return with return value less than
-.I count
-elements.
-This will happen when internal
-.BR write ()
-returns zero, which may mean we read end of the file, or remote computer closed
-network connection.
-.PP
When multiple threads access single
.I rb
object, in blocking way, it is guaranteed that each thread will write continous
@@ -172,13 +125,9 @@ first thread will put second half of its frame - now frames are corrupted and
not really usable.
.PP
.BR rb_send (3)
-and
-.BR rb_posix_send (3)
work the same way as
.BR rb_write (3)
-and
-.BR rb_posix_write (3)
-but also accept following
+but also accepts following
.IR flags :
.TP
.B MSG_DONTWAIT
@@ -200,109 +149,6 @@ and will return immediately.
Not recommended when multiple concurent threads calls
.BR rb_write (3)
with this flag - may lead to interlaced reads.
-.SH EXAMPLE
-.PP
-Reading frames from multiple sockets and parse them in one thread.
-As usual, error handling ommited for clarity.
-.EX
-.PP
- struct tdata
- {
- struct rb *rb;
- int fd;
- };
-
- void *receiver(void *arg)
- {
- struct tdata *data = arg;
-
- for (;;)
- {
- /* rb_posix_write() will sleep current thread until
- * data shows up on data->fd file descriptor.
- *
- * NOTE: you should be carefull here, as calling this
- * function on slow data->fd will block other threads.
- * rb will try to call read() on data->fd and read()
- * will block until any data shows on data->fd, and
- * if data->fd is slow, this may take a long time.
- */
-
- if (rb_posix_write(data->rb, data->fd,
- sizeof(struct data_frame) == -1)
- {
- if (errno == ECANCELED)
- {
- /* rb_stop() has been called, rb shouldn't
- * be used any more
- */
- break;
- }
- }
- }
- }
-
- void loop(struct rb *rb)
- {
- struct tdata tdata[3];
- pthread_t receivers[3];
- int i;
-
- tdata[0].rb = rb;
- tdata[0].fd = get_network_socket();
-
- tdata[1].rb = rb;
- tdata[1].fd = get_serial_socket();
-
- tdata[2].rb = rb;
- tdata[2].fd = get_fifo_socket();
-
- for (i = 0; i != 3; ++i)
- {
- /* create multiple threads that will read data from
- * sockets and put that data into rb. Pretty nice
- * way to eliminate polling, as these threads will
- * wake only when there is data available
- /
-
- pthread_create(&receivers[i], NULL, receiver, &tdata[i]);
- }
-
- for (;;)
- {
- /* receivers are started and will populate our rb with
- * frames over time, rb guarantees all frames here will
- * be valid (if they come valid from socket that is)
- */
-
- struct data_frame f;
-
- if (rb_read(rb, &f, sizeof(f)) == -1)
- {
- if (errno == ECANCELED)
- {
- /* rb_stop() has been called, we shouldn't
- * use rb object anymore
- */
-
- break;
- }
- }
-
- process_frame(&f);
- }
-
- for (i = 0; i != 3; ++i)
- {
- /* join all threads using rb object, so rb_destroy()
- * can be called safely
- */
-
- pthread_join(receivers[i], NULL);
- }
- }
-
-.EE
.SH RETURN VALUES
.PP
On successfull write, function will return number of
@@ -314,9 +160,6 @@ Returned value can be less than
if
.I rb
doesn't contain enough free space and function operates in non blocking mode.
-In such case it is also ok for function to return 0 - meaning
-.I rb
-is full.
On errors function returns -1, in such case,
.I rb
buffer is left intact.
@@ -344,9 +187,6 @@ You should not access
object after you receive this error.
Otherwise you will probably get deadlock or application will crash.
Returned only if threads are enabled.
-.TP
-.B ENOSYS
-Function is not implemented (was not compiled in).
.SH SEE ALSO
.PP
.BR rb_overview (7),
diff --git a/rb.c b/rb.c
index aa0ef4c..758b119 100644
--- a/rb.c
+++ b/rb.c
@@ -2142,6 +2142,7 @@ long rb_posix_recv
VALID(EINVAL, rb);
VALID(EINVAL, rb->buffer);
+ VALID(EINVAL, rb->object_size == 1);
VALID(EINVAL, fd >= 0);
if (count > (size_t)LONG_MAX)
@@ -2323,6 +2324,7 @@ long rb_posix_send
VALID(EINVAL, rb);
VALID(EINVAL, rb->buffer);
+ VALID(EINVAL, rb->object_size == 1);
VALID(EINVAL, fd >= 0);
if (count > (size_t)LONG_MAX)
diff --git a/tests.c b/tests.c
index b1d9a0e..1ece1d7 100644
--- a/tests.c
+++ b/tests.c
@@ -68,7 +68,7 @@ static int multi;
static int t_num_producers;
static int t_num_consumers;
-static int data[255];
+static unsigned char data[250];
static unsigned int multi_index;
static volatile unsigned int multi_index_count;
@@ -197,7 +197,7 @@ static void *multi_pipe_producer(void *arg)
return NULL;
}
- write(fd, &index, sizeof(index));
+ write(fd, &index, 1);
}
}
@@ -214,6 +214,7 @@ static void *multi_consumer(void *arg)
if (fd == -1)
{
+ index = 0;
if (rb_read(rb, &index, 1) == -1)
{
/*
@@ -311,7 +312,8 @@ static void *multi_pipe_consumer(void *arg)
}
- if (read(fd, &index, sizeof(index)) == -1)
+ index = 0;
+ if (read(fd, &index, 1) == -1)
{
perror("read()");
close(fd);
@@ -436,7 +438,7 @@ static void multi_file_consumer_producer(void)
unlink("./rb-test-fifo");
mkfifo("./rb-test-fifo", 0777);
- rb = rb_new(8, sizeof(unsigned int), O_MULTITHREAD);
+ rb = rb_new(8, 1, O_MULTITHREAD);
pthread_mutex_init(&multi_mutex, NULL);
pthread_mutex_init(&multi_mutex_count, NULL);
@@ -1231,67 +1233,13 @@ static void fd_write_single(void)
close(fd);
}
-static void fd_write_single_multibyte(void)
-{
- int fd;
- int i;
- int pos;
- int data[128];
- int rdata[128];
- struct rb *rb;
-
- /*
- * prepare file with some content to read into rb
- */
-
- if ((fd = open("./rb-test-file", O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0)
- {
- perror("posix_read_single; prepare; open");
- mt_assert(0);
- }
-
- for (i = 0; i != 128; ++i)
- {
- data[i] = rand();
- }
-
- write(fd, data, sizeof(data));
- lseek(fd, 0, SEEK_SET);
-
- rb = rb_new(128, sizeof(int), 0);
-
- mt_fail(rb_posix_write(rb, fd, 100) == 100);
- mt_fail(rb_read(rb, rdata, 100) == 100);
-
- for (i = 0; i != 100; ++i)
- {
- mt_fail(data[i] == rdata[i]);
- }
-
- mt_fail(rb_posix_write(rb, fd, 100) == 28);
- mt_fail(rb_read(rb, rdata, 100) == 28);
- for (i = 0; i != 28; ++i)
- {
- mt_fail(data[i + 100] == rdata[i]);
- }
-
- /*
- * we read all data from file, read() on our fd will return 0 (end of file),
- * so rb will return 0 as well
- */
- mt_fail(rb_posix_write(rb, fd, 10) == 0);
-
- rb_destroy(rb);
- close(fd);
-}
-
static void fd_write_single_overlap(void)
{
int fd;
int i;
int pos;
- int data[128];
- int rdata[128];
+ unsigned char data[128];
+ unsigned char rdata[128];
struct rb *rb;
/*
@@ -1312,7 +1260,7 @@ static void fd_write_single_overlap(void)
write(fd, data, sizeof(data));
lseek(fd, 0, SEEK_SET);
- rb = rb_new(16, sizeof(int), 0);
+ rb = rb_new(16, 1, 0);
/*
* cause overlap condition
@@ -1444,45 +1392,15 @@ static void fd_read_single(void)
}
-static void fd_read_single_multibyte(void)
+static void fd_single_multibyte(void)
{
struct rb *rb;
int fd;
- int i;
- int data[128];
- int rdata[128];
-
-
- if ((fd = open("./rb-test-file", O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0)
- {
- perror("prepare; open");
- mt_assert(0);
- }
-
- for (i = 0; i != 128; ++i)
- {
- data[i] = rand();
- }
+ fd = 1;
rb = rb_new(128, sizeof(int), 0);
-
- mt_fail(rb_write(rb, data, 100) == 100);
- mt_fail(rb_posix_read(rb, fd, 100) == 100);
- mt_fail(rb_write(rb, data + 100, 28) == 28);
- mt_fail(rb_posix_read(rb, fd, 28) == 28);
-
- lseek(fd, 0, SEEK_SET);
- mt_fail(read(fd, rdata, 128 * sizeof(int)) == 128 * sizeof(int));
-
- for (i = 0; i != 128; ++i)
- {
- mt_fail(rdata[i] == data[i]);
- }
- mt_fail(read(fd, rdata, 1) == 0);
-
- rb_destroy(rb);
- close(fd);
-
+ mt_ferr(rb_posix_write(rb, fd, 100), EINVAL);
+ mt_ferr(rb_posix_read(rb, fd, 100), EINVAL);
}
static void fd_read_single_overlap(void)
@@ -1804,26 +1722,27 @@ int main(void)
sprintf(name, "singl_thread with buffer %3d, %3d, %3d, %3d",
t_rblen, t_readlen, t_writelen, t_objsize);
mt_run_named(single_thread, name);
+ }
+
+ t_objsize = 1;
#if ENABLE_POSIX_CALLS
- t_multi_test_type = TEST_FD_FILE;
+ t_multi_test_type = TEST_FD_FILE;
# if ENABLE_THREADS
- sprintf(name, "multi_thread with file %3d, %3d, %3d, %3d",
- t_rblen, t_readlen, t_writelen, t_objsize);
- mt_run_named(multi_thread, name);
+ sprintf(name, "multi_thread with file %3d, %3d, %3d, %3d",
+ t_rblen, t_readlen, t_writelen, t_objsize);
+ mt_run_named(multi_thread, name);
# endif
- sprintf(name, "singl_thread with file %3d, %3d, %3d, %3d",
- t_rblen, t_readlen, t_writelen, t_objsize);
- mt_run_named(single_thread, name);
+ sprintf(name, "singl_thread with file %3d, %3d, %3d, %3d",
+ t_rblen, t_readlen, t_writelen, t_objsize);
+ mt_run_named(single_thread, name);
#endif
-
- }
}
}
}
@@ -1846,11 +1765,10 @@ int main(void)
#if ENABLE_POSIX_CALLS
mt_run(fd_write_single);
- mt_run(fd_write_single_multibyte);
mt_run(fd_write_single_overlap);
mt_run(fd_write_single_partial);
mt_run(fd_read_single);
- mt_run(fd_read_single_multibyte);
+ mt_run(fd_single_multibyte);
mt_run(fd_read_single_overlap);
mt_run(fd_read_single_partial);
mt_run(fd_read_single_peek);