diff options
author | Michał Łyszczek <michal.lyszczek@bofc.pl> | 2018-09-29 16:01:41 +0200 |
---|---|---|
committer | Michał Łyszczek <michal.lyszczek@bofc.pl> | 2018-09-29 16:01:41 +0200 |
commit | 4581704245cbdd5b1e16aac10620f7c16c2a58d2 (patch) | |
tree | 6552087725a6474a8b24a198e9a44089b36596ed | |
parent | 555a8fabdaa3a6c303d9456e8ce9e2966ee2c3cc (diff) | |
download | librb-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.3 | 313 | ||||
-rw-r--r-- | man/rb_posix_recv.3 | 2 | ||||
-rw-r--r-- | man/rb_posix_send.3 | 2 | ||||
-rw-r--r-- | man/rb_posix_write.3 | 311 | ||||
-rw-r--r-- | man/rb_read.3 | 175 | ||||
-rw-r--r-- | man/rb_write.3 | 172 | ||||
-rw-r--r-- | rb.c | 2 | ||||
-rw-r--r-- | tests.c | 130 |
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), @@ -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) @@ -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); |