diff options
author | Michał Łyszczek <michal.lyszczek@bofc.pl> | 2018-09-19 14:25:56 +0200 |
---|---|---|
committer | Michał Łyszczek <michal.lyszczek@bofc.pl> | 2018-09-19 14:25:56 +0200 |
commit | 83e8233dd0b8a5ad4ea7071ea58e85f736454f12 (patch) | |
tree | b5135347bc6f7988c9f3c7e7312081654584ac6a | |
parent | dd816b65c24fd9159f37947fba73209a801b92e1 (diff) | |
download | librb-83e8233dd0b8a5ad4ea7071ea58e85f736454f12.tar.gz librb-83e8233dd0b8a5ad4ea7071ea58e85f736454f12.tar.bz2 librb-83e8233dd0b8a5ad4ea7071ea58e85f736454f12.zip |
fix: deadlock by assuming select() is thread safe
It is not.
-rw-r--r-- | man/rb_overview.7 | 6 | ||||
-rw-r--r-- | man/rb_write.3 | 10 | ||||
-rw-r--r-- | rb.c | 37 |
3 files changed, 8 insertions, 45 deletions
diff --git a/man/rb_overview.7 b/man/rb_overview.7 index 06c5c46..4625f8c 100644 --- a/man/rb_overview.7 +++ b/man/rb_overview.7 @@ -122,8 +122,7 @@ Upon entering any of the .B POSIX function, entering thread will lock read or write mutex, meaning when thread 1 enters any read function, no other thread will be able to read until thread 1 -completly finishes its read (there is on exception to this rule explained -later). +completly finishes its read. Now you can see the problem, thread 1 may go in and perform .BR read () on super slow socket and it will block threads that may also want to read into @@ -133,9 +132,6 @@ object but from different, much faster socket. This situation, of course, can occur only when multiple threads uses same .B rb object. -As stated before, there is one exception to this rule. -Until thread 1 (the one using slow socket) reads anything from the socket it -doesn't block any other threads. .SH EXAMPLE .PP Please note, that example is missing error handling for simplicity. diff --git a/man/rb_write.3 b/man/rb_write.3 index caae337..0912a04 100644 --- a/man/rb_write.3 +++ b/man/rb_write.3 @@ -188,9 +188,13 @@ As usual, error handling ommited for clarity. for (;;) { /* rb_posix_write() will sleep current thread until - * data shows up on data->fd file descriptor. Until - * this thread writes any data to rb, it won't block - * other threads that wants to use rb_write() functions. + * 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, @@ -908,12 +908,6 @@ static long rb_recvt pthread_mutex_unlock(&rb->lock); trace(("i/rb unlock")); - if (r == 0) - { - pthread_mutex_unlock(&rb->rlock); - trace(("i/rlock unlock")); - } - tv.tv_sec = 0; tv.tv_usec = 0; @@ -929,12 +923,6 @@ static long rb_recvt sact = select(fd + 1, NULL, &fds, NULL, NULL); } - if (r == 0) - { - trace(("i/rlock lock")); - pthread_mutex_lock(&rb->rlock); - } - trace(("i/rb lock")); pthread_mutex_lock(&rb->lock); rb_del_blocked_thread(rb); @@ -1412,21 +1400,6 @@ long rb_sendt pthread_mutex_unlock(&rb->lock); trace(("i/rb unlock")); - if (w == 0) - { - /* - * we didn't write anything to rb just yet, so we can unlock - * another wlock, so other thread can write to rb, while we - * wait. If we don't unlock it we risk situation where t1 - * calls this function, then blocks for days on select() - * waiting for some data to arrive, blocking every thread - * that may receive data much faster. - */ - - trace(("i/wlock unlock")); - pthread_mutex_unlock(&rb->wlock); - } - tv.tv_sec = 0; tv.tv_usec = 0; @@ -1450,16 +1423,6 @@ long rb_sendt sact = select(fd + 1, &fds, NULL, NULL, NULL); } - if (w == 0) - { - /* - * ok, blocking is done, relock wlock again - */ - - trace(("i/wlock lock")); - pthread_mutex_lock(&rb->wlock); - } - trace(("i/rb lock")); pthread_mutex_lock(&rb->lock); rb_del_blocked_thread(rb); |