Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions sdk/core/azure-core/src/http/curl/curl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,9 @@ CURLcode CurlConnection::SendBuffer(
// expected to return CURLE_AGAIN (since socket is ready), so, a chuck of data will be uploaded
// and result will be CURLE_OK which breaks the loop. Also, getting other than CURLE_OK or
// CURLE_AGAIN throws.
// NOTE: curl_easy_send() may return CURLE_OK with sentBytesPerRequest == 0 on SSL sockets
// when the socket is not ready for writing. In this case, we add a small delay to prevent
// busy-wait loops and allow the scheduler to release the current quantum.
context.ThrowIfCancelled();
for (CURLcode sendResult = CURLE_AGAIN; sendResult == CURLE_AGAIN;)
{
Expand All @@ -637,6 +640,13 @@ CURLcode CurlConnection::SendBuffer(
switch (sendResult)
{
case CURLE_OK: {
if (sentBytesPerRequest == 0)
{
// When curl_easy_send returns CURLE_OK but sends 0 bytes (which can happen on SSL
// sockets), add a small delay to prevent rapid retries that keep the CPU busy
// without making progress. This allows the scheduler to run other threads.
std::this_thread::sleep_for(std::chrono::milliseconds(10));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::this_thread::yield will give up the time slice to other like or higher priority threads. Does curl actually need the thread to suspend (sleep) in case?

Copy link

@agabhin agabhin Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation of this_thread::yield: https://en.cppreference.com/w/cpp/thread/yield.html

Provides a hint to the implementation to reschedule the execution of threads, allowing other threads to run.

Note that it is only a hint.

Also, note Linux CFS scheduler behavior https://www.kernel.org/doc/html/latest/scheduler/sched-design-CFS.html

CFS’s task picking logic is based on this p->se.vruntime value and it is thus very simple: it always tries to run the task with the smallest p->se.vruntime value (i.e., the task which executed least so far). CFS always tries to split up CPU time between runnable tasks as close to “ideal multitasking hardware” as possible.

This means - since Linux CFS (Completely Fair Scheduler) tries to be "fair," if your thread has yielded a lot recently, yield might effectively be a "no-op" (do nothing) if the scheduler decides you still haven't used your "fair share" of time compared to others, or if no other thread of the same priority is waiting.

  • this_thread::yield will maybe yield the cpu but keep the thread in Running state and let OS sched decide what to do
  • this_thread::sleep_for will move it to Blocked state which is much better in this scenario and implicitly includes yielding the cpu.

Conclusion

  • Using this_thread::yield in this context is risky and might not solve the cpu spin problem. Standard sleep is safer and better.

-- Abhinav

}
sentBytesTotal += sentBytesPerRequest;
break;
}
Expand Down