18namespace proto_fuzzer {
23short PollEventsForInterest(
int interest) {
25 if ((interest & CURL_POLL_IN) != 0) {
28 if ((interest & CURL_POLL_OUT) != 0) {
35int CurlEventsForPollResult(
short events) {
37 if ((events & (POLLIN | POLLHUP)) != 0) {
39 result |= CURL_CSELECT_IN;
41 if ((events & POLLOUT) != 0) {
42 result |= CURL_CSELECT_OUT;
44 if ((events & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
45 result |= CURL_CSELECT_ERR;
55 : multi_(nullptr), timeout_ms_(-1), timer_pending_(false), watch_exhausted_(false), generation_(0) {}
69 if (multi_ ==
nullptr) {
72 return curl_multi_setopt(multi_, CURLMOPT_SOCKETFUNCTION, &MultiSocketDriver::SocketCallback) == CURLM_OK &&
73 curl_multi_setopt(multi_, CURLMOPT_SOCKETDATA,
this) == CURLM_OK &&
74 curl_multi_setopt(multi_, CURLMOPT_TIMERFUNCTION, &MultiSocketDriver::TimerCallback) == CURLM_OK &&
75 curl_multi_setopt(multi_, CURLMOPT_TIMERDATA,
this) == CURLM_OK;
85 if (multi_ ==
nullptr) {
86 return CURLM_BAD_HANDLE;
88 timer_pending_ =
false;
89 return curl_multi_socket_action(multi_, CURL_SOCKET_TIMEOUT, 0, running_handles);
103 if (multi_ ==
nullptr) {
104 result.
code = CURLM_BAD_HANDLE;
108 std::array<struct pollfd, kMaxWatches> poll_fds{};
109 std::size_t poll_count = 0;
110 for (
const Watch& watch : watches_) {
114 poll_fds[poll_count].fd = watch.socket;
115 poll_fds[poll_count].events = PollEventsForInterest(watch.interest);
119 const std::uint64_t generation_before = generation_;
120 const int running_before = running_handles ==
nullptr ? 0 : *running_handles;
121 bool action_dispatched =
false;
122 if (poll_count != 0 && ::poll(poll_fds.data(),
static_cast<nfds_t
>(poll_count), 0) > 0) {
123 for (std::size_t index = 0; index < poll_count; ++index) {
124 if (running_handles !=
nullptr && *running_handles == 0) {
130 Watch* current = FindWatch(poll_fds[index].fd);
131 if (current ==
nullptr) {
134 const int events = CurlEventsForPollResult(poll_fds[index].revents);
138 result.
code = curl_multi_socket_action(multi_, poll_fds[index].fd, events, running_handles);
139 action_dispatched =
true;
147 if (result.
code == CURLM_OK && timer_pending_ && timeout_ms_ == 0) {
150 timer_pending_ =
false;
151 result.
code = curl_multi_socket_action(multi_, CURL_SOCKET_TIMEOUT, 0, running_handles);
152 action_dispatched =
true;
155 const int running_after = running_handles ==
nullptr ? 0 : *running_handles;
160 result.
made_progress = action_dispatched || generation_ != generation_before || running_after != running_before;
169 if (multi_ ==
nullptr) {
172 long timeout_ms = -1;
173 (void)curl_multi_timeout(multi_, &timeout_ms);
174 (void)curl_multi_wakeup(multi_);
179int MultiSocketDriver::SocketCallback(CURL* , curl_socket_t socket,
int what,
void* user_data,
181 return static_cast<MultiSocketDriver*
>(user_data)->UpdateSocket(socket, what, socket_data);
187int MultiSocketDriver::TimerCallback(CURLM* ,
long timeout_ms,
void* user_data) {
195int MultiSocketDriver::UpdateSocket(curl_socket_t socket,
int what,
void* socket_data) {
196 Watch* watch = FindWatch(socket);
197 if (what == CURL_POLL_REMOVE) {
198 if (watch !=
nullptr) {
199 (void)curl_multi_assign(multi_, socket,
nullptr);
200 watch->active =
false;
201 watch->socket = CURL_SOCKET_BAD;
202 watch->interest = CURL_POLL_NONE;
208 if (watch ==
nullptr) {
209 watch = FindFreeWatch();
210 if (watch ==
nullptr) {
211 watch_exhausted_ =
true;
214 watch->socket = socket;
215 watch->active =
true;
216 (void)curl_multi_assign(multi_, socket, watch);
218 }
else if (socket_data !=
nullptr && socket_data != watch) {
221 (void)curl_multi_assign(multi_, socket, watch);
224 if (watch->interest != what) {
225 watch->interest = what;
235int MultiSocketDriver::UpdateTimer(
long timeout_ms) {
236 if (!timer_pending_ || timeout_ms_ != timeout_ms) {
239 timeout_ms_ = timeout_ms;
240 timer_pending_ = timeout_ms >= 0;
246MultiSocketDriver::Watch* MultiSocketDriver::FindWatch(curl_socket_t socket) {
247 for (Watch& watch : watches_) {
248 if (watch.active && watch.socket == socket) {
258MultiSocketDriver::Watch* MultiSocketDriver::FindFreeWatch() {
259 for (Watch& watch : watches_) {
Owns the socket and timer callback state required by libcurl's event-driven multi API.
CURLMcode Start(int *running_handles)
Start libcurl's socket state machine through the documented timeout pseudo-socket without waiting for...
bool Install(CURLM *multi)
Install socket/timer callbacks on a caller-owned multi before adding an easy handle.
MultiSocketDriver()
Construct detached callback state.
void ProbeControlApis()
Exercise the wakeup and timeout-query APIs from a valid live-multi state.
~MultiSocketDriver()
The owner deliberately destroys this after curl_multi_cleanup, so there is no callback deregistration...
DriveResult DriveReady(int *running_handles)
Run one bounded, non-blocking event-loop turn: poll the callback-provided watch set with a zero timeo...
Bounded event-loop state for driving curl_multi_socket_action.
Result from one non-blocking event-loop turn.
bool made_progress
True when an action ran or callback/handle state changed.
CURLMcode code
libcurl's result for the last socket action in the turn.