-
Notifications
You must be signed in to change notification settings - Fork 87
Avoid buffered I/O #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
fiber/fiber.c
Outdated
@@ -119,13 +119,24 @@ int fiber_wait_all() | |||
return FIBER_NOERROR; | |||
} | |||
|
|||
/* A simple solution to avoid unexpected output by the buffered I/O printf is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You shall explain why this wrapper is "safe."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I am thinking to change the name for the macro. I use the word "safe" to represent that the output can show as we may want. But as far as I know, printf
is said to be thread-safe in the Linux manual. That means it won't corrupt anything when using in the multithreading. However, some interleaving that doesn't change the outcome is still allowed to happen, which can produce the mixing output between threads.
So I am not sure if the name print_safe
will mislead someone to the concept that printf
is not thread-safe. Do I misunderstand something or any advice for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was meant to be "safe." Instead, its intention is to eliminate the impact of buffered I/O, which causes the unexpected sequence of strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the printf
provided by glibc is a buffered output function, the proposed change does "direct" format string manipulation. Therefore, you can rename printf_safe
to printf_unbuffered
and then use #define printf printf_unbuffered
for build-time substitution.
Reference: CS:APP Chapter 10
c949298
to
521032e
Compare
fiber/fiber.c
Outdated
/* A simple solution to avoid unexpected output by the buffered I/O 'printf' is | ||
* using low-level I/O interface 'write'. It works because 'printf' will wait to | ||
* write STDOUT until the buffer is full, or on some other conditions. Using | ||
* write, however, can write STDOUT immediately. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change "write STDOUT" to "write to STDOUT"
When I try to go deep on my solution for the interleaved output, I think I may have some wrong thoughts when I design this macro. I first used this as the solution because I think Although for my result, the output contents do show as expected, but I'm not sure if this really solves the problem. I may need time to clarify my own question. |
fd082b4
to
4f4031a
Compare
A simple solution to avoid unexpected output by the buffered I/O 'printf' is using low-level I/O interface 'write'. It works because 'printf' will wait to write to STDOUT until the buffer is full, or on some other conditions. Using write, however, can write to STDOUT immediately. Here is a naive implementation for the idea with some limitation and weakness that need improvement: 1. It will fail if the formatted string with length >64 2. The function 'write' can write less than n bytes. It will need further handling if happens.
Currently, the reader_func is counting the number of grace periods based on the value of dut, which is updated by mthpc_rcu_replace_pointer. However, the value of dut actually represents the time we update the value, not the number of grace periods. Also, the original method might result in incorrect counting if someone tried to update the gp_idx while others who saw the same dut value with prev_count still depend on the old gp_idx to increase the counter. To fix the problem, instead of relying on the dut value to increase the gp_idx, we manually increase gp_idx on write side. Then, we can easily determine the gp on read side. For dut value, we simply check the old count value is not greater than the newest one. Additionally, since synchronize_rcu is quite slow, readers generally will pass through the critical section during the first grace period. To generate more realistic output, we add a delay on read side before entering the critical section. Before: 100 reader(s), 5 update run(s), 6 grace period(s) [grace period #0] 100 reader(s) [grace period sysprog21#1] 0 reader(s) [grace period sysprog21#2] 0 reader(s) [grace period sysprog21#3] 0 reader(s) [grace period sysprog21#4] 0 reader(s) [grace period sysprog21#5] 0 reader(s) After, we added a delay: 100 reader(s), 5 update run(s), 6 grace period(s) [grace period #0] 76 reader(s) [grace period sysprog21#1] 0 reader(s) [grace period sysprog21#2] 1 reader(s) [grace period sysprog21#3] 0 reader(s) [grace period sysprog21#4] 3 reader(s) [grace period sysprog21#5] 20 reader(s)
Currently, the reader_func is counting the number of grace periods based on the value of dut, which is updated by rcu_assign_pointer. However, the value of dut actually represents the time we update the value, not the number of grace periods. Also, the original method might result in incorrect counting if someone tried to update the gp_idx while others who saw the same dut value with prev_count still depend on the old gp_idx to increase the counter. To fix the problem, instead of relying on the dut value to increase the gp_idx, we manually increase gp_idx on write side. Then, we can easily determine the gp on read side. For dut value, we simply check the old count value is not greater than the newest one. Additionally, since synchronize_rcu is quite slow, readers generally will pass through the critical section during the first grace period. To generate more realistic output, we add a delay on read side before entering the critical section. Before: 100 reader(s), 5 update run(s), 6 grace period(s) [grace period #0] 100 reader(s) [grace period sysprog21#1] 0 reader(s) [grace period sysprog21#2] 0 reader(s) [grace period sysprog21#3] 0 reader(s) [grace period sysprog21#4] 0 reader(s) [grace period sysprog21#5] 0 reader(s) After, we added a delay: 100 reader(s), 5 update run(s), 6 grace period(s) [grace period #0] 76 reader(s) [grace period sysprog21#1] 0 reader(s) [grace period sysprog21#2] 1 reader(s) [grace period sysprog21#3] 0 reader(s) [grace period sysprog21#4] 3 reader(s) [grace period sysprog21#5] 20 reader(s)
Currently, the reader_func is counting the number of grace periods based on the value of dut, which is updated by rcu_assign_pointer. However, the value of dut actually represents the time we update the value, not the number of grace periods. Also, the original method might result in incorrect counting if someone tried to update the gp_idx while others who saw the same dut value with prev_count still depend on the old gp_idx to increase the counter. To fix the problem, instead of relying on the dut value to increase the gp_idx, we manually increase gp_idx on write side. Then, we can easily determine the gp on read side. For dut value, we simply check the old count value is not greater than the newest one. Additionally, since synchronize_rcu is quite slow, readers generally will pass through the critical section during the first grace period. To generate more realistic output, we add a delay on read side before entering the critical section. Before: 100 reader(s), 5 update run(s), 6 grace period(s) [grace period #0] 100 reader(s) [grace period sysprog21#1] 0 reader(s) [grace period sysprog21#2] 0 reader(s) [grace period sysprog21#3] 0 reader(s) [grace period sysprog21#4] 0 reader(s) [grace period sysprog21#5] 0 reader(s) After, we added a delay: 100 reader(s), 5 update run(s), 6 grace period(s) [grace period #0] 76 reader(s) [grace period sysprog21#1] 0 reader(s) [grace period sysprog21#2] 1 reader(s) [grace period sysprog21#3] 0 reader(s) [grace period sysprog21#4] 3 reader(s) [grace period sysprog21#5] 20 reader(s)
Here's a macro solution to avoid unexpected output by the buffered I/O
printf
. It could have some limitations and need improvement, but it achieves the purpose of output in text lines as we may want.