scheduler remove lock for scheduled|resumed tasks#444
Conversation
* Uses atomic waiter list to enqueue schedule_operations. * List is reversed prior to execution so they are in order. * Normally co_await'ed tasks do not allocate * std::coroutine_handle<>'s that are directly resumed require a schedule_operation to be allocated and deleted upon completion. * Consider adding a pool of pre-allocated schedule_operations to reuse for direct coroutine handle resumes.
c37e7f9 to
4356df0
Compare
4356df0 to
4b9c114
Compare
| else | ||
| { | ||
| m_thread_pool->resume(m_handles_to_resume); | ||
| m_size.fetch_sub(m_handles_to_resume.size(), std::memory_order::release); |
There was a problem hiding this comment.
I simplified the m_size being decremented only here by the process calls for inline and thread pool, it was difficult to merge the two patterns until I realized the other decrements should have just been here all along.
| const constexpr std::size_t connections_per_client = 10; | ||
| // const constexpr std::size_t connections_per_client = 2; | ||
| const constexpr std::size_t messages_per_connection = 2; | ||
| const constexpr std::size_t messages_per_connection = 10'000; |
There was a problem hiding this comment.
I updated this test to actually have some throughput for benchmarking, and removed most of the REQUIRE_THREAD_SAFE since they are a lock and affect benching
| { | ||
| if constexpr (std::is_void_v<return_type>) | ||
| { | ||
| co_await schedule(std::move(task)); |
There was a problem hiding this comment.
This was a bug, added a test to fix it as well.
| { | ||
| delete op; | ||
| } | ||
| } |
There was a problem hiding this comment.
What if a partial read happens? write is guaranteed to be atomic (if the size of the data written is <= PIPE_BUF), but read is not
There was a problem hiding this comment.
yeah good point, the check needs to be perfectly divided by for the sizeof, in testing this doesn't seem to happen, but that doesn't mean it won't. Caching a partial read sucks too. 😬
There was a problem hiding this comment.
Yeah maybe this change isn't really worth it, there are some nice cleanups around these data structures but that is a real problem.
There was a problem hiding this comment.
You can do something similar to read_exact in TCP, but read_aligned
auto pipe_t::read_aligned(void* buffer, std::size_t n, std::size_t align) -> long
{
if (n == 0 || align == 0) return 0;
std::size_t total_read = 0;
char* ptr = static_cast<char*>(buffer);
while (total_read < n) {
ssize_t result = ::read(read_fd(), ptr + total_read, n - total_read);
if (result < 0) {
if (errno == EINTR) continue;
return -1;
}
// EOF
if (result == 0) break;
total_read += result;
if (total_read % align == 0) {
return static_cast<ssize_t>(total_read);
}
}
if (total_read % align != 0) {
// If we hit EOF but aren't aligned, we have data corruption.
std::cerr << "cry in cerr\n";
return -1;
}
return static_cast<long>(total_read);
}But it looks a bit too much
There was a problem hiding this comment.
Although it won't save us if an error happens midpointer ._.
There was a problem hiding this comment.
Also, I think, this can completely break if we have multiple threads
Thread A calls read and the kernel gives it 12 bytes
Thread B calls read and the kernel gives it the remaining 4 bytes plus whatever comes next
Now they have 1,5 and 0,5 pointers and corrupted data
There was a problem hiding this comment.
Yeah all good points, for the scheduler the event loop is currently single threaded, but it would limit us to always be singly threaded on it. I'll update this PR to revert passing the schedule_operation* through the pipe, I think there are some other nice changes to keep here though.
There was a problem hiding this comment.
Ok I've reverted to using an atomic waiter list (still dropping the lock). In theory I believe this should work, but its possibly got an ABA problem?
* Code review noted that while writes to the pipe are atomic, reads are not, so you are not guaranteed to get a full pointer read. In testing this didn't happen but in reality it is a failure point. This adds too much complexity. * Attempting to revert to using the atomic waiter list, this possibly has the ABA problem and needs to be worked through.
|
I think this works to get rid of one lock but I think getting rid of the timer lock could help a lot too. Probably need to create the poll info on schedule op and have the process event thread use the poll map exclusively. This could introduce some latency to the timers though. |
|
I'm gonna give this a merge to move forward, if we start seeing failures we can revert. |
Uh oh!
There was an error while loading. Please reload this page.