Coverage Report

Created: 2026-07-30 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/multi_ev.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#include "urldata.h"
27
#include "url.h"
28
#include "cfilters.h"
29
#include "curl_trc.h"
30
#include "multiif.h"
31
#include "multi_ev.h"
32
#include "select.h"
33
#include "uint-bset.h"
34
#include "uint-spbset.h"
35
#include "multihandle.h"
36
37
#ifdef DEBUGBUILD
38
0
#define SH_ENTRY_MAGIC 0x570091d
39
#endif
40
41
/* Information about a socket for which we inform the libcurl application
42
 * what to supervise (CURL_POLL_IN/CURL_POLL_OUT/CURL_POLL_REMOVE)
43
 */
44
struct mev_sh_entry {
45
  struct uint32_spbset xfers; /* bitset of transfers `mid`s on this socket */
46
  struct connectdata *conn; /* connection using this socket or NULL */
47
  void *user_data;      /* libcurl app data via curl_multi_assign() */
48
  unsigned int action;  /* CURL_POLL_IN/CURL_POLL_OUT we last told the
49
                         * libcurl application to watch out for */
50
  unsigned int readers; /* this many transfers want to read */
51
  unsigned int writers; /* this many transfers want to write */
52
#ifdef DEBUGBUILD
53
  unsigned int magic;
54
#endif
55
  BIT(announced);       /* this socket has been passed to the socket
56
                           callback at least once */
57
};
58
59
static size_t mev_sh_entry_hash(void *key, size_t key_length, size_t slots_num)
60
0
{
61
0
  curl_socket_t fd = *((curl_socket_t *)key);
62
0
  (void)key_length;
63
0
  return (fd % (curl_socket_t)slots_num);
64
0
}
65
66
static size_t mev_sh_entry_compare(void *k1, size_t k1_len,
67
                                   void *k2, size_t k2_len)
68
0
{
69
0
  (void)k1_len;
70
0
  (void)k2_len;
71
0
  return (*((curl_socket_t *)k1)) == (*((curl_socket_t *)k2));
72
0
}
73
74
/* sockhash entry destructor callback */
75
static void mev_sh_entry_dtor(void *freethis)
76
0
{
77
0
  struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis;
78
0
  Curl_uint32_spbset_destroy(&entry->xfers);
79
0
#ifdef DEBUGBUILD
80
0
  entry->magic = 0;
81
0
#endif
82
0
  curlx_free(entry);
83
0
}
84
85
/* look up a given socket in the socket hash, skip invalid sockets */
86
static struct mev_sh_entry *mev_sh_entry_get(struct Curl_hash *sh,
87
                                             curl_socket_t s)
88
0
{
89
0
  if(s != CURL_SOCKET_BAD) {
90
    /* only look for proper sockets */
91
0
    return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
92
0
  }
93
0
  return NULL;
94
0
}
95
96
/* make sure this socket is present in the hash for this handle */
97
static struct mev_sh_entry *mev_sh_entry_add(struct Curl_hash *sh,
98
                                             curl_socket_t s)
99
0
{
100
0
  struct mev_sh_entry *there = mev_sh_entry_get(sh, s);
101
0
  struct mev_sh_entry *check;
102
103
0
  if(there) {
104
    /* it is present, return fine */
105
0
    return there;
106
0
  }
107
108
  /* not present, add it */
109
0
  check = curlx_calloc(1, sizeof(struct mev_sh_entry));
110
0
  if(!check)
111
0
    return NULL; /* major failure */
112
113
0
  Curl_uint32_spbset_init(&check->xfers);
114
115
  /* make/add new hash entry */
116
0
  if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
117
0
    mev_sh_entry_dtor(check);
118
0
    return NULL; /* major failure */
119
0
  }
120
0
#ifdef DEBUGBUILD
121
0
  check->magic = SH_ENTRY_MAGIC;
122
0
#endif
123
0
  return check; /* things are good in sockhash land */
124
0
}
125
126
/* delete the given socket entry from the hash */
127
static void mev_sh_entry_kill(struct Curl_multi *multi, curl_socket_t s)
128
0
{
129
0
  Curl_hash_delete(&multi->ev.sh_entries, (char *)&s, sizeof(curl_socket_t));
130
0
}
131
132
static size_t mev_sh_entry_user_count(struct mev_sh_entry *e)
133
0
{
134
0
  return Curl_uint32_spbset_count(&e->xfers) + (e->conn ? 1 : 0);
135
0
}
136
137
static bool mev_sh_entry_xfer_known(struct mev_sh_entry *e,
138
                                    struct Curl_easy *data)
139
0
{
140
0
  return Curl_uint32_spbset_contains(&e->xfers, data->mid);
141
0
}
142
143
static bool mev_sh_entry_conn_known(struct mev_sh_entry *e,
144
                                    struct connectdata *conn)
145
0
{
146
0
  return (e->conn == conn);
147
0
}
148
149
static bool mev_sh_entry_xfer_add(struct mev_sh_entry *e,
150
                                  struct Curl_easy *data)
151
0
{
152
   /* detect weird values */
153
0
  DEBUGASSERT(mev_sh_entry_user_count(e) < 100000);
154
0
  return Curl_uint32_spbset_add(&e->xfers, data->mid);
155
0
}
156
157
static bool mev_sh_entry_conn_add(struct mev_sh_entry *e,
158
                                  struct connectdata *conn)
159
0
{
160
   /* detect weird values */
161
0
  DEBUGASSERT(mev_sh_entry_user_count(e) < 100000);
162
0
  DEBUGASSERT(!e->conn);
163
0
  if(e->conn)
164
0
    return FALSE;
165
0
  e->conn = conn;
166
0
  return TRUE;
167
0
}
168
169
static bool mev_sh_entry_xfer_remove(struct mev_sh_entry *e,
170
                                     struct Curl_easy *data)
171
0
{
172
0
  bool present = Curl_uint32_spbset_contains(&e->xfers, data->mid);
173
0
  if(present)
174
0
    Curl_uint32_spbset_remove(&e->xfers, data->mid);
175
0
  return present;
176
0
}
177
178
static bool mev_sh_entry_conn_remove(struct mev_sh_entry *e,
179
                                     struct connectdata *conn)
180
0
{
181
0
  DEBUGASSERT(e->conn == conn);
182
0
  if(e->conn == conn) {
183
0
    e->conn = NULL;
184
0
    return TRUE;
185
0
  }
186
0
  return FALSE;
187
0
}
188
189
/* Purge any information about socket `s`.
190
 * Let the socket callback know as well when necessary */
191
static CURLMcode mev_forget_socket(struct Curl_multi *multi,
192
                                   struct Curl_easy *data,
193
                                   curl_socket_t s,
194
                                   const char *cause)
195
0
{
196
0
  struct mev_sh_entry *entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
197
0
  int rc = 0;
198
199
0
  if(!entry) /* we never knew or already forgot about this socket */
200
0
    return CURLM_OK;
201
202
  /* We managed this socket before, tell the socket callback to forget it. */
203
0
  if(entry->announced && multi->socket_cb) {
204
0
    struct Curl_mapi_guard guard;
205
206
0
    NOVERBOSE((void)cause);
207
0
    CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s);
208
0
    CURL_CBAPI_MULTI_START(&guard, multi, multi_socket_cb);
209
0
    rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
210
0
                          multi->socket_userp, entry->user_data);
211
0
    CURL_CBAPI_END(&guard);
212
0
    entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
213
0
    if(entry)
214
0
      entry->announced = FALSE;
215
0
  }
216
217
0
  mev_sh_entry_kill(multi, s);
218
0
  if(rc == -1) {
219
0
    multi->dead = TRUE;
220
0
    return CURLM_ABORTED_BY_CALLBACK;
221
0
  }
222
0
  return CURLM_OK;
223
0
}
224
225
static CURLMcode mev_sh_entry_update(struct Curl_multi *multi,
226
                                     struct Curl_easy *data,
227
                                     struct mev_sh_entry *entry,
228
                                     curl_socket_t s,
229
                                     unsigned char last_action,
230
                                     unsigned char cur_action)
231
0
{
232
0
  int rc, comboaction;
233
234
  /* we should only be called when the callback exists */
235
0
  DEBUGASSERT(multi->socket_cb);
236
0
  DEBUGASSERT(entry->magic == SH_ENTRY_MAGIC);
237
0
  if(!multi->socket_cb)
238
0
    return CURLM_OK;
239
240
  /* Transfer `data` goes from `last_action` to `cur_action` on socket `s`
241
   * with `multi->ev.sh_entries` entry `entry`. Update `entry` and trigger
242
   * `multi->socket_cb` on change, if the callback is set. */
243
0
  if(last_action == cur_action)  /* nothing from `data` changed */
244
0
    return CURLM_OK;
245
246
0
  if(last_action & CURL_POLL_IN) {
247
0
    DEBUGASSERT(entry->readers);
248
0
    if(!(cur_action & CURL_POLL_IN))
249
0
      entry->readers--;
250
0
  }
251
0
  else if(cur_action & CURL_POLL_IN)
252
0
    entry->readers++;
253
254
0
  if(last_action & CURL_POLL_OUT) {
255
0
    DEBUGASSERT(entry->writers);
256
0
    if(!(cur_action & CURL_POLL_OUT))
257
0
      entry->writers--;
258
0
  }
259
0
  else if(cur_action & CURL_POLL_OUT)
260
0
    entry->writers++;
261
262
0
  DEBUGASSERT(entry->readers <= mev_sh_entry_user_count(entry));
263
0
  DEBUGASSERT(entry->writers <= mev_sh_entry_user_count(entry));
264
0
  DEBUGASSERT(entry->writers + entry->readers);
265
266
0
  CURL_TRC_M(data, "ev update fd=%" FMT_SOCKET_T ", action '%s%s' -> '%s%s'"
267
0
             " (%u/%u r/w)", s,
268
0
             (last_action & CURL_POLL_IN) ? "IN" : "",
269
0
             (last_action & CURL_POLL_OUT) ? "OUT" : "",
270
0
             (cur_action & CURL_POLL_IN) ? "IN" : "",
271
0
             (cur_action & CURL_POLL_OUT) ? "OUT" : "",
272
0
             entry->readers, entry->writers);
273
274
0
  comboaction = (entry->writers ? CURL_POLL_OUT : 0) |
275
0
                (entry->readers ? CURL_POLL_IN : 0);
276
0
  if(((int)entry->action == comboaction)) /* nothing for socket changed */
277
0
    return CURLM_OK;
278
279
0
  CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)",
280
0
             s, (comboaction & CURL_POLL_IN) ? "IN" : "",
281
0
             (comboaction & CURL_POLL_OUT) ? "OUT" : "");
282
0
  {
283
0
    struct Curl_mapi_guard guard;
284
0
    CURL_CBAPI_MULTI_START(&guard, multi, multi_socket_cb);
285
0
    rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
286
0
                          entry->user_data);
287
0
    CURL_CBAPI_MULTI_END(&guard);
288
0
  }
289
0
  if(rc == -1) {
290
0
    multi->dead = TRUE;
291
0
    return CURLM_ABORTED_BY_CALLBACK;
292
0
  }
293
  /* curl_easy_pause() is documented as callable from any callback; it
294
   * re-enters mev_assess() which may free this 'entry'. Re-fetch. */
295
0
  entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
296
0
  if(entry) {
297
0
    DEBUGASSERT(entry->magic == SH_ENTRY_MAGIC);
298
0
    entry->announced = TRUE;
299
0
    entry->action = (unsigned int)comboaction;
300
0
  }
301
0
  return CURLM_OK;
302
0
}
303
304
static CURLMcode mev_pollset_diff(struct Curl_multi *multi,
305
                                  struct Curl_easy *data,
306
                                  struct connectdata *conn,
307
                                  struct easy_pollset *ps,
308
                                  struct easy_pollset *prev_ps)
309
0
{
310
0
  struct mev_sh_entry *entry;
311
0
  curl_socket_t s;
312
0
  unsigned int i, j;
313
0
  CURLMcode mresult;
314
315
  /* The transfer `data` reports in `ps` the sockets it is interested
316
   * in and which combination of CURL_POLL_IN/CURL_POLL_OUT it wants
317
   * to have monitored for events.
318
   * There can be more than 1 transfer interested in the same socket
319
   * and 1 transfer might be interested in more than 1 socket.
320
   * `prev_ps` is the pollset copy from the previous call here. On
321
   * the 1st call it will be empty.
322
   */
323
0
  DEBUGASSERT(ps);
324
0
  DEBUGASSERT(prev_ps);
325
326
  /* Handle changes to sockets the transfer is interested in. */
327
0
  for(i = 0; i < ps->n; i++) {
328
0
    unsigned char last_action;
329
0
    bool first_time = FALSE; /* data/conn appears first time on socket */
330
331
0
    s = ps->sockets[i];
332
    /* Have we handled this socket before? */
333
0
    entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
334
0
    if(!entry) {
335
      /* new socket, add new entry */
336
0
      first_time = TRUE;
337
0
      entry = mev_sh_entry_add(&multi->ev.sh_entries, s);
338
0
      if(!entry) /* fatal */
339
0
        return CURLM_OUT_OF_MEMORY;
340
0
      CURL_TRC_M(data, "ev new entry fd=%" FMT_SOCKET_T, s);
341
0
    }
342
0
    else if(conn) {
343
0
      first_time = !mev_sh_entry_conn_known(entry, conn);
344
0
    }
345
0
    else {
346
0
      first_time = !mev_sh_entry_xfer_known(entry, data);
347
0
    }
348
349
    /* What was the previous action the transfer had regarding this socket?
350
     * If the transfer is new to the socket, disregard the information
351
     * in `last_poll`, because the socket might have been destroyed and
352
     * reopened. We would have cleared the sh_entry for that, but the socket
353
     * might still be mentioned in the hashed pollsets. */
354
0
    last_action = 0;
355
0
    if(first_time) {
356
0
      if(conn) {
357
0
        if(!mev_sh_entry_conn_add(entry, conn))
358
0
          return CURLM_OUT_OF_MEMORY;
359
0
      }
360
0
      else {
361
0
        if(!mev_sh_entry_xfer_add(entry, data))
362
0
          return CURLM_OUT_OF_MEMORY;
363
0
      }
364
0
      CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", added %s #%" FMT_OFF_T
365
0
                 ", total=%u/%d (xfer/conn)", s,
366
0
                 conn ? "connection" : "transfer",
367
0
                 conn ? conn->connection_id : data->mid,
368
0
                 Curl_uint32_spbset_count(&entry->xfers),
369
0
                 entry->conn ? 1 : 0);
370
0
    }
371
0
    else {
372
0
      for(j = 0; j < prev_ps->n; j++) {
373
0
        if(s == prev_ps->sockets[j]) {
374
0
          last_action = prev_ps->actions[j];
375
0
          break;
376
0
        }
377
0
      }
378
0
    }
379
    /* track readers/writers changes and report to socket callback */
380
0
    mresult = mev_sh_entry_update(multi, data, entry, s,
381
0
                                  last_action, ps->actions[i]);
382
0
    if(mresult)
383
0
      return mresult;
384
0
  }
385
386
  /* Handle changes to sockets the transfer is NO LONGER interested in. */
387
0
  for(i = 0; i < prev_ps->n; i++) {
388
0
    bool stillused = FALSE;
389
390
0
    s = prev_ps->sockets[i];
391
0
    for(j = 0; j < ps->n; j++) {
392
0
      if(s == ps->sockets[j]) {
393
        /* socket is still supervised */
394
0
        stillused = TRUE;
395
0
        break;
396
0
      }
397
0
    }
398
0
    if(stillused)
399
0
      continue;
400
401
0
    entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
402
    /* if entry does not exist, we were either never told about it or
403
     * have already cleaned up this socket via Curl_multi_ev_socket_done().
404
     * In other words: this is perfectly normal */
405
0
    if(!entry)
406
0
      continue;
407
408
0
    if(conn && !mev_sh_entry_conn_remove(entry, conn)) {
409
      /* `conn` says in `prev_ps` that it had been using a socket,
410
       * but `conn` has not been registered for it.
411
       * This should not happen if our book-keeping is correct? */
412
0
      CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", conn lost "
413
0
                 "interest but is not registered", s);
414
0
      DEBUGASSERT(NULL);
415
0
      continue;
416
0
    }
417
418
0
    if(!conn && !mev_sh_entry_xfer_remove(entry, data)) {
419
      /* `data` says in `prev_ps` that it had been using a socket,
420
       * but `data` has not been registered for it.
421
       * This should not happen if our book-keeping is correct? */
422
0
      CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", transfer lost "
423
0
                 "interest but is not registered", s);
424
0
      DEBUGASSERT(NULL);
425
0
      continue;
426
0
    }
427
428
0
    if(mev_sh_entry_user_count(entry)) {
429
      /* track readers/writers changes and report to socket callback */
430
0
      mresult = mev_sh_entry_update(multi, data, entry, s,
431
0
                                    prev_ps->actions[i], 0);
432
0
      if(mresult)
433
0
        return mresult;
434
0
      CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", removed transfer, "
435
0
                 "total=%u/%d (xfer/conn)", s,
436
0
                 Curl_uint32_spbset_count(&entry->xfers),
437
0
                 entry->conn ? 1 : 0);
438
0
    }
439
0
    else {
440
0
      mresult = mev_forget_socket(multi, data, s, "last user gone");
441
0
      if(mresult)
442
0
        return mresult;
443
0
    }
444
0
  } /* for loop over num */
445
446
  /* Remember for next time */
447
0
  Curl_pollset_move(prev_ps, ps);
448
0
  return CURLM_OK;
449
0
}
450
451
static void mev_pollset_dtor(void *key, size_t klen, void *entry)
452
0
{
453
0
  struct easy_pollset *ps = entry;
454
0
  (void)key;
455
0
  (void)klen;
456
0
  if(ps) {
457
0
    Curl_pollset_cleanup(ps);
458
0
    curlx_free(ps);
459
0
  }
460
0
}
461
462
static struct easy_pollset *mev_add_new_conn_pollset(struct connectdata *conn)
463
0
{
464
0
  struct easy_pollset *ps;
465
466
0
  ps = Curl_pollset_create();
467
0
  if(!ps)
468
0
    return NULL;
469
0
  if(Curl_conn_meta_set(conn, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor))
470
0
    return NULL;
471
0
  return ps;
472
0
}
473
474
static struct easy_pollset *mev_add_new_xfer_pollset(struct Curl_easy *data)
475
0
{
476
0
  struct easy_pollset *ps;
477
478
0
  ps = Curl_pollset_create();
479
0
  if(!ps)
480
0
    return NULL;
481
0
  if(Curl_meta_set(data, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor))
482
0
    return NULL;
483
0
  return ps;
484
0
}
485
486
static struct easy_pollset *mev_get_last_pollset(struct Curl_easy *data,
487
                                                 struct connectdata *conn)
488
0
{
489
0
  if(data) {
490
0
    if(conn)
491
0
      return Curl_conn_meta_get(conn, CURL_META_MEV_POLLSET);
492
0
    return Curl_meta_get(data, CURL_META_MEV_POLLSET);
493
0
  }
494
0
  return NULL;
495
0
}
496
497
static CURLMcode mev_assess(struct Curl_multi *multi,
498
                            struct Curl_easy *data,
499
                            struct connectdata *conn)
500
0
{
501
0
  struct easy_pollset ps, *last_ps;
502
0
  CURLMcode mresult = CURLM_OK;
503
504
0
  if(!multi || !multi->socket_cb)
505
0
    return CURLM_OK;
506
507
0
  Curl_pollset_init(&ps);
508
0
  if(conn) {
509
0
    CURLcode result = Curl_conn_adjust_pollset(data, conn, &ps);
510
0
    if(result) {
511
0
      mresult = (result == CURLE_OUT_OF_MEMORY) ?
512
0
        CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR;
513
0
      goto out;
514
0
    }
515
0
  }
516
0
  else
517
0
    Curl_multi_pollset(data, &ps);
518
0
  last_ps = mev_get_last_pollset(data, conn);
519
520
0
  if(!last_ps && ps.n) {
521
0
    if(conn)
522
0
      last_ps = mev_add_new_conn_pollset(conn);
523
0
    else
524
0
      last_ps = mev_add_new_xfer_pollset(data);
525
0
    if(!last_ps) {
526
0
      mresult = CURLM_OUT_OF_MEMORY;
527
0
      goto out;
528
0
    }
529
0
  }
530
531
0
  if(last_ps)
532
0
    mresult = mev_pollset_diff(multi, data, conn, &ps, last_ps);
533
0
  else
534
0
    DEBUGASSERT(!ps.n);
535
0
out:
536
0
  Curl_pollset_cleanup(&ps);
537
0
  return mresult;
538
0
}
539
540
CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi,
541
                                    struct Curl_easy *data)
542
0
{
543
0
  return mev_assess(multi, data, NULL);
544
0
}
545
546
CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
547
                                    struct Curl_easy *data,
548
                                    struct connectdata *conn)
549
0
{
550
0
  return mev_assess(multi, data, conn);
551
0
}
552
553
CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
554
                                         struct uint32_bset *set)
555
0
{
556
0
  uint32_t mid;
557
0
  CURLMcode mresult = CURLM_OK;
558
559
0
  if(multi && multi->socket_cb && Curl_uint32_bset_first(set, &mid)) {
560
0
    do {
561
0
      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
562
0
      if(data) {
563
0
        mresult = Curl_multi_ev_assess_xfer(multi, data);
564
0
      }
565
0
    } while(!mresult && Curl_uint32_bset_next(set, mid, &mid));
566
0
  }
567
0
  return mresult;
568
0
}
569
570
CURLMcode Curl_multi_ev_assign(struct Curl_multi *multi,
571
                               curl_socket_t s,
572
                               void *user_data)
573
0
{
574
0
  struct mev_sh_entry *e = mev_sh_entry_get(&multi->ev.sh_entries, s);
575
0
  if(!e)
576
0
    return CURLM_BAD_SOCKET;
577
0
  e->user_data = user_data;
578
0
  return CURLM_OK;
579
0
}
580
581
void Curl_multi_ev_dirty_xfers(struct Curl_multi *multi,
582
                               curl_socket_t s)
583
0
{
584
0
  struct mev_sh_entry *entry;
585
586
0
  DEBUGASSERT(s != CURL_SOCKET_TIMEOUT);
587
0
  entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
588
589
  /* Unmatched socket, we cannot act on it but we ignore this fact. In
590
     real-world tests it has been proved that libevent can in fact give
591
     the application actions even though the socket was previously
592
     asked to get removed, so thus we better survive stray socket actions
593
     and move on. */
594
0
  if(entry) {
595
0
    struct Curl_easy *data;
596
0
    uint32_t mid;
597
598
0
    if(Curl_uint32_spbset_first(&entry->xfers, &mid)) {
599
0
      do {
600
0
        data = Curl_multi_get_easy(multi, mid);
601
0
        if(data) {
602
0
          Curl_multi_mark_dirty(data);
603
0
        }
604
0
        else {
605
0
          CURL_TRC_M(multi->admin, "socket transfer %u no longer found", mid);
606
0
          Curl_uint32_spbset_remove(&entry->xfers, mid);
607
0
        }
608
0
      } while(Curl_uint32_spbset_next(&entry->xfers, mid, &mid));
609
0
    }
610
611
0
    if(entry->conn)
612
0
      Curl_multi_mark_dirty(multi->admin);
613
0
  }
614
0
}
615
616
void Curl_multi_ev_socket_done(struct Curl_multi *multi,
617
                               struct Curl_easy *data, curl_socket_t s)
618
0
{
619
0
  mev_forget_socket(multi, data, s, "socket done");
620
0
}
621
622
void Curl_multi_ev_xfer_done(struct Curl_multi *multi,
623
                             struct Curl_easy *data)
624
0
{
625
0
  DEBUGASSERT(!data->conn); /* transfer should have been detached */
626
0
  (void)mev_assess(multi, data, NULL);
627
0
  Curl_meta_remove(data, CURL_META_MEV_POLLSET);
628
0
}
629
630
void Curl_multi_ev_conn_done(struct Curl_multi *multi,
631
                             struct Curl_easy *data,
632
                             struct connectdata *conn)
633
0
{
634
0
  (void)mev_assess(multi, data, conn);
635
0
  Curl_conn_meta_remove(conn, CURL_META_MEV_POLLSET);
636
0
}
637
638
void Curl_multi_ev_init(struct Curl_multi *multi, size_t hashsize)
639
0
{
640
0
  Curl_hash_init(&multi->ev.sh_entries, hashsize, mev_sh_entry_hash,
641
0
                 mev_sh_entry_compare, mev_sh_entry_dtor);
642
0
}
643
644
void Curl_multi_ev_cleanup(struct Curl_multi *multi)
645
0
{
646
0
  Curl_hash_destroy(&multi->ev.sh_entries);
647
0
}