multi: reduce struct size with small cleanups
No binary (build failed)
—
(log yok)
| @@ -51,7 +51,7 @@ This iteration has the following properties: | |
| ### Memory | |
| For storing 1000 entries, the table would allocate one block of 8KB on | |
| - | a 64-bit system, plus the 2 pointers and 3 `uint32_t` in its base `struct |
| + | a 64-bit system, plus the pointer and 3 `uint32_t` in its base `struct |
| uint32_tbl`. A resize allocates a completely new pointer array, copy | |
| the existing entries and free the previous one. | |
| @@ -29,6 +29,13 @@ | |
| #include "multiif.h" | |
| #include "vtls/vtls_scache.h" | |
| + | typedef char mapi_fn_ids_fit_uint8[ |
| + | (CURL_MAPI_FN_LAST <= CURL_CBAPI_FN_START) ? 1 : -1]; |
| + | typedef char cbapi_fn_ids_fit_uint8[ |
| + | ((CURL_CBAPI_FN_LAST - 1) <= UINT8_MAX) ? 1 : -1]; |
| + | typedef char mapi_depth_fits_uint8[ |
| + | (CURL_MAPI_MAX_RECURSION <= UINT8_MAX) ? 1 : -1]; |
| + | |
| struct Curl_eapi_fn_props { | |
| Curl_eapi_fn fn; | |
| uint8_t data_is_killed; /* easy handle is killed in call */ | |
| @@ -135,7 +142,7 @@ static bool eapi_in_event_cb(struct Curl_easy *data) | |
| size_t i; | |
| for(i = 0; i < multi->callstack.count; ++i) { | |
| if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) { | |
| - | uint16_t fn = multi->callstack.calls[i]; |
| + | uint8_t fn = multi->callstack.calls[i]; |
| if((fn < CURL_CBAPI_FN_LAST) && | |
| cbapi_fn_props[fn - CURL_CBAPI_FN_START].is_event_cb) | |
| return TRUE; | |
| @@ -376,7 +383,7 @@ bool Curl_mapi_enter(struct Curl_mapi_guard *guard, | |
| mresult = CURLM_RECURSIVE_API_CALL; | |
| goto out; | |
| } | |
| - | multi->callstack.calls[multi->callstack.count] = (uint16_t)fn; |
| + | multi->callstack.calls[multi->callstack.count] = (uint8_t)fn; |
| ++multi->callstack.count; | |
| guard->depth = multi->callstack.count; | |
| guard->multi = fn_props->multi_is_killed ? NULL : multi; | |
| @@ -400,7 +407,7 @@ void Curl_mapi_leave(struct Curl_mapi_guard *guard) | |
| DEBUGASSERT(0); /* someone forgot to clean up */ | |
| } | |
| /* reset to depth the guard was entered in */ | |
| - | guard->multi->callstack.count = (uint16_t)(guard->depth - 1); |
| + | guard->multi->callstack.count = (uint8_t)(guard->depth - 1); |
| } | |
| } | |
| } | |
| @@ -439,7 +446,7 @@ void Curl_cbapi_enter(struct Curl_mapi_guard *guard, | |
| /* if multi callstack already at max depth, leave */ | |
| if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) | |
| return; | |
| - | multi->callstack.calls[multi->callstack.count] = (uint16_t)fn; |
| + | multi->callstack.calls[multi->callstack.count] = (uint8_t)fn; |
| ++multi->callstack.count; | |
| guard->depth = multi->callstack.count; | |
| guard->multi = multi; | |
| @@ -99,7 +99,7 @@ typedef enum { | |
| CURL_MAPI_FN_LAST | |
| } Curl_mapi_fn; | |
| - | #define CURL_CBAPI_FN_START (16 * 1024) |
| + | #define CURL_CBAPI_FN_START 128 |
| /* the callback functions */ | |
| typedef enum { | |
| @@ -169,13 +169,13 @@ CURLHcode Curl_eapi_hcode(CURLcode result); | |
| #define CURL_MAPI_MAX_RECURSION 15 | |
| struct Curl_mapi_stack { | |
| - | uint16_t count; |
| - | uint16_t calls[CURL_MAPI_MAX_RECURSION]; |
| + | uint8_t count; |
| + | uint8_t calls[CURL_MAPI_MAX_RECURSION]; |
| }; | |
| struct Curl_mapi_guard { | |
| struct Curl_multi *multi; /* != NULL if handle stays */ | |
| - | uint16_t depth; /* > 0 if this guard was entered */ |
| + | uint8_t depth; /* > 0 if this guard was entered */ |
| }; | |
| bool Curl_mapi_enter(struct Curl_mapi_guard *guard, | |
| @@ -254,7 +254,7 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, | |
| Curl_dnscache_init(&multi->dnscache, dnssize); | |
| Curl_mntfy_init(multi); | |
| Curl_multi_ev_init(multi, ev_hashsize); | |
| - | Curl_uint32_tbl_init(&multi->xfers, NULL); |
| + | Curl_uint32_tbl_init(&multi->xfers); |
| Curl_uint32_bset_init(&multi->process); | |
| Curl_uint32_bset_init(&multi->dirty); | |
| Curl_uint32_bset_init(&multi->pending); | |
| @@ -264,7 +264,6 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, | |
| multi->multiplexing = TRUE; | |
| multi->max_concurrent_streams = 100; | |
| - | multi->last_timeout_ms = -1; |
| #ifdef ENABLE_WAKEUP | |
| multi->wakeup_pair[0] = CURL_SOCKET_BAD; | |
| multi->wakeup_pair[1] = CURL_SOCKET_BAD; | |
| @@ -3575,7 +3574,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) | |
| return CURLM_OK; | |
| multi_timeout(multi, &timeouts_offset_us, &timeout_ms); | |
| - | if(timeout_ms < 0 && multi->last_timeout_ms < 0) { |
| + | if(timeout_ms < 0 && !multi->last_timeout_set) { |
| /* nothing to do */ | |
| } | |
| else if(timeout_ms < 0) { | |
| @@ -3584,7 +3583,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) | |
| timeout_ms = -1; /* normalize */ | |
| set_value = TRUE; | |
| } | |
| - | else if(multi->last_timeout_ms < 0) { |
| + | else if(!multi->last_timeout_set) { |
| CURL_TRC_M(multi->admin, "[TIMER] set %dms, none before", timeout_ms); | |
| set_value = TRUE; | |
| } | |
| @@ -3606,7 +3605,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) | |
| struct Curl_mapi_guard guard; | |
| multi->last_expire_offset_us = timeouts_offset_us; | |
| - | multi->last_timeout_ms = timeout_ms; |
| + | multi->last_timeout_set = timeout_ms >= 0; |
| CURL_CBAPI_MULTI_START(&guard, multi, multi_timer_cb); | |
| rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); | |
| CURL_CBAPI_MULTI_END(&guard); | |
| @@ -158,7 +158,6 @@ struct Curl_multi { | |
| /* timer callback and user data pointer for the *socket() API */ | |
| curl_multi_timer_callback timer_cb; | |
| void *timer_userp; | |
| - | timediff_t last_timeout_ms; /* the last timeout value set via timer_cb */ |
| #ifdef USE_WINSOCK | |
| WSAEVENT wsa_event; /* Winsock event used for waits */ | |
| @@ -191,6 +190,7 @@ struct Curl_multi { | |
| #endif | |
| BIT(dead); /* a callback returned error, everything needs to crash and | |
| burn */ | |
| + | BIT(last_timeout_set); /* last timer callback value was nonnegative */ |
| BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */ | |
| BIT(xfer_ulbuf_borrowed); /* xfer_ulbuf is currently being borrowed */ | |
| BIT(xfer_sockbuf_borrowed); /* xfer_sockbuf is currently being borrowed */ | |
| @@ -89,7 +89,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, | |
| * @param pbuf on return, the buffer to use or NULL on error | |
| * @param pbuflen on return, the size of *pbuf or 0 on error | |
| * @return CURLE_OK when buffer is available and is returned. | |
| - | * CURLE_OUT_OF_MEMORy on failure to allocate the buffer, |
| + | * CURLE_OUT_OF_MEMORY on failure to allocate the buffer, |
| * CURLE_FAILED_INIT if the easy handle is without multi. | |
| * CURLE_AGAIN if the buffer is borrowed already. | |
| */ | |
| @@ -113,7 +113,7 @@ void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf); | |
| * @param pbuf on return, the buffer to use or NULL on error | |
| * @param pbuflen on return, the size of *pbuf or 0 on error | |
| * @return CURLE_OK when buffer is available and is returned. | |
| - | * CURLE_OUT_OF_MEMORy on failure to allocate the buffer, |
| + | * CURLE_OUT_OF_MEMORY on failure to allocate the buffer, |
| * CURLE_FAILED_INIT if the easy handle is without multi. | |
| * CURLE_AGAIN if the buffer is borrowed already. | |
| */ | |
| @@ -138,8 +138,7 @@ void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf); | |
| * @param blen requested length of the buffer | |
| * @param pbuf on return, the buffer to use or NULL on error | |
| * @return CURLE_OK when buffer is available and is returned. | |
| - | * CURLE_OUT_OF_MEMORy on failure to allocate the buffer, |
| - | * CURLE_FAILED_INIT if the easy handle is without multi. |
| + | * CURLE_OUT_OF_MEMORY on failure to allocate the buffer, |
| * CURLE_AGAIN if the buffer is borrowed already. | |
| */ | |
| CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, | |
| @@ -29,11 +29,9 @@ | |
| #define CURL_UINT32_TBL_MAGIC 0x62757473 | |
| #endif | |
| - | void Curl_uint32_tbl_init(struct uint32_tbl *tbl, |
| - | Curl_uint32_tbl_entry_dtor *entry_dtor) |
| + | void Curl_uint32_tbl_init(struct uint32_tbl *tbl) |
| { | |
| memset(tbl, 0, sizeof(*tbl)); | |
| - | tbl->entry_dtor = entry_dtor; |
| tbl->last_key_added = UINT32_MAX; | |
| #ifdef DEBUGBUILD | |
| tbl->init = CURL_UINT32_TBL_MAGIC; | |
| @@ -49,8 +47,6 @@ static void uint32_tbl_clear_rows(struct uint32_tbl *tbl, | |
| end = CURLMIN(upto_excluding, tbl->nrows); | |
| for(i = from; i < end; ++i) { | |
| if(tbl->rows[i]) { | |
| - | if(tbl->entry_dtor) |
| - | tbl->entry_dtor(i, tbl->rows[i]); |
| tbl->rows[i] = NULL; | |
| tbl->nentries--; | |
| } | |
| @@ -25,12 +25,8 @@ | |
| ***************************************************************************/ | |
| #include "curl_setup.h" | |
| - | /* Destructor for a single table entry */ |
| - | typedef void Curl_uint32_tbl_entry_dtor(uint32_t key, void *entry); |
| - | |
| struct uint32_tbl { | |
| void **rows; /* array of void* holding entries */ | |
| - | Curl_uint32_tbl_entry_dtor *entry_dtor; |
| uint32_t nrows; /* length of `rows` array */ | |
| uint32_t nentries; /* entries in table */ | |
| uint32_t last_key_added; /* UINT_MAX or last key added */ | |
| @@ -39,17 +35,14 @@ struct uint32_tbl { | |
| #endif | |
| }; | |
| - | /* Initialize the table with 0 capacity. |
| - | * The optional `entry_dtor` is called when a table entry is removed, |
| - | * Passing NULL means no action is taken on removal. */ |
| - | void Curl_uint32_tbl_init(struct uint32_tbl *tbl, |
| - | Curl_uint32_tbl_entry_dtor *entry_dtor); |
| + | /* Initialize the table with 0 capacity. */ |
| + | void Curl_uint32_tbl_init(struct uint32_tbl *tbl); |
| /* Resize the table to change capacity `nmax`. When `nmax` is reduced, | |
| * all present entries with key equal or larger to `nmax` are removed. */ | |
| CURLcode Curl_uint32_tbl_resize(struct uint32_tbl *tbl, uint32_t nrows); | |
| - | /* Destroy the table, freeing all entries. */ |
| + | /* Destroy the table, freeing its storage. */ |
| void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl); | |
| /* Get the table capacity. */ | |
| @@ -2081,6 +2081,7 @@ TESTCASES = \ | |
| test3227 \ | |
| test3228 \ | |
| test3229 \ | |
| + | test3230 \ |
| \ | |
| test3300 \ | |
| test3301 \ | |
| @@ -0,0 +1,19 @@ | |
| + | <?xml version="1.0" encoding="US-ASCII"?> |
| + | <testcase> |
| + | <info> |
| + | <keywords> |
| + | unittest |
| + | multi |
| + | </keywords> |
| + | </info> |
| + | |
| + | # Client-side |
| + | <client> |
| + | <features> |
| + | unittest |
| + | </features> |
| + | <name> |
| + | multi timer unit tests |
| + | </name> |
| + | </client> |
| + | </testcase> |
| @@ -102,6 +102,7 @@ TESTS_C = \ | |
| unit3216.c \ | |
| unit3219.c \ | |
| unit3227.c \ | |
| + | unit3230.c \ |
| unit3300.c \ | |
| unit3301.c \ | |
| unit3302.c \ | |
| @@ -30,7 +30,7 @@ | |
| static CURLcode t3212_setup(struct uint32_tbl *tbl) | |
| { | |
| - | Curl_uint32_tbl_init(tbl, NULL); |
| + | Curl_uint32_tbl_init(tbl); |
| return Curl_uint32_tbl_resize(tbl, TBL_SIZE); | |
| } | |
| @@ -0,0 +1,140 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, et al. |
| + | * |
| + | * This software is licensed as described in the file COPYING, which |
| + | * you should have received as part of this distribution. The terms |
| + | * are also available at https://curl.se/docs/copyright.html. |
| + | * |
| + | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + | * copies of the Software, and permit persons to whom the Software is |
| + | * furnished to do so, under the terms of the COPYING file. |
| + | * |
| + | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + | * KIND, either express or implied. |
| + | * |
| + | * SPDX-License-Identifier: curl |
| + | * |
| + | ***************************************************************************/ |
| + | #include "unitcheck.h" |
| + | #include "urldata.h" |
| + | #include "multiif.h" |
| + | |
| + | #define T3230_TIMER_CALLS 6 |
| + | |
| + | struct t3230_timer_ctx { |
| + | long timeout_ms[T3230_TIMER_CALLS]; |
| + | size_t count; |
| + | }; |
| + | |
| + | static int t3230_timer_cb(CURLM *multi, long timeout_ms, void *userp) |
| + | { |
| + | struct t3230_timer_ctx *ctx = userp; |
| + | (void)multi; |
| + | |
| + | if(ctx->count < CURL_ARRAYSIZE(ctx->timeout_ms)) |
| + | ctx->timeout_ms[ctx->count] = timeout_ms; |
| + | ++ctx->count; |
| + | return 0; |
| + | } |
| + | |
| + | static CURLcode t3230_setup(void) |
| + | { |
| + | CURLcode result = CURLE_OK; |
| + | global_init(CURL_GLOBAL_ALL); |
| + | return result; |
| + | } |
| + | |
| + | static void t3230_test_timer_updates(void) |
| + | { |
| + | CURLM *handle = curl_multi_init(); |
| + | struct Curl_multi *multi = handle; |
| + | struct t3230_timer_ctx ctx; |
| + | struct curltime now; |
| + | timediff_t expire_offset_us; |
| + | CURLMcode mresult; |
| + | int running; |
| + | |
| + | memset(&ctx, 0, sizeof(ctx)); |
| + | if(!multi) { |
| + | fail("multi handle creation failed"); |
| + | return; |
| + | } |
| + | |
| + | mresult = curl_multi_setopt(handle, CURLMOPT_TIMERDATA, &ctx); |
| + | fail_unless(mresult == CURLM_OK, "setting timer data failed"); |
| + | mresult = curl_multi_setopt(handle, CURLMOPT_TIMERFUNCTION, |
| + | t3230_timer_cb); |
| + | fail_unless(mresult == CURLM_OK, "setting timer callback failed"); |
| + | |
| + | fail_unless(Curl_update_timer(multi) == CURLM_OK, |
| + | "initial timer update failed"); |
| + | fail_unless(!ctx.count, "callback invoked without a timeout"); |
| + | fail_unless(!multi->last_timeout_set, "initial timer marked as set"); |
| + | |
| + | now = curlx_now(); |
| + | Curl_expire_set(multi->admin, EXPIRE_TIMEOUT, 600000, &now); |
| + | fail_unless(Curl_update_timer(multi) == CURLM_OK, |
| + | "setting timer failed"); |
| + | fail_unless(ctx.count == 1, "setting timer did not invoke callback"); |
| + | fail_unless(multi->last_timeout_set, "timer not marked as set"); |
| + | |
| + | Curl_expire_set(multi->admin, EXPIRE_TIMEOUT, 1200000, &now); |
| + | fail_unless(Curl_update_timer(multi) == CURLM_OK, |
| + | "replacing timer failed"); |
| + | fail_unless(ctx.count == 2, "replacing timer did not invoke callback"); |
| + | |
| + | fail_unless(Curl_update_timer(multi) == CURLM_OK, |
| + | "unchanged timer update failed"); |
| + | fail_unless(ctx.count == 2, "unchanged timer invoked callback"); |
| + | |
| + | expire_offset_us = multi->last_expire_offset_us; |
| + | mresult = curl_multi_socket_action(handle, CURL_SOCKET_TIMEOUT, 0, &running); |
| + | fail_unless(mresult == CURLM_OK, "forced timer refresh failed"); |
| + | fail_unless(ctx.count == 3, "timer refresh did not invoke callback"); |
| + | fail_unless(multi->last_expire_offset_us == expire_offset_us, |
| + | "timer refresh changed expiry offset"); |
| + | fail_unless(multi->last_timeout_set, "refreshed timer not marked as set"); |
| + | |
| + | Curl_expire_clear_all(multi->admin); |
| + | fail_unless(Curl_update_timer(multi) == CURLM_OK, |
| + | "timer clear update failed"); |
| + | fail_unless(ctx.count == 4, "clearing timer did not invoke callback"); |
| + | fail_unless(!multi->last_timeout_set, "cleared timer marked as set"); |
| + | fail_unless(Curl_update_timer(multi) == CURLM_OK, |
| + | "repeated timer clear update failed"); |
| + | |
| + | Curl_multi_mark_dirty(multi->admin); |
| + | fail_unless(Curl_update_timer(multi) == CURLM_OK, |
| + | "zero timer update failed"); |
| + | fail_unless(multi->last_timeout_set, "zero timer not marked as set"); |
| + | Curl_multi_clear_dirty(multi->admin); |
| + | fail_unless(Curl_update_timer(multi) == CURLM_OK, |
| + | "clearing zero timer failed"); |
| + | fail_unless(!multi->last_timeout_set, "zero timer clear left timer set"); |
| + | |
| + | fail_unless(ctx.count == T3230_TIMER_CALLS, "wrong timer callback count"); |
| + | fail_unless(ctx.timeout_ms[0] >= 0, "wrong initial timeout"); |
| + | fail_unless(ctx.timeout_ms[1] > ctx.timeout_ms[0], |
| + | "wrong replacement timeout"); |
| + | fail_unless(ctx.timeout_ms[2] >= 0, "wrong refreshed timeout"); |
| + | fail_unless(ctx.timeout_ms[3] == -1, "wrong cleared timeout"); |
| + | fail_unless(ctx.timeout_ms[4] == 0, "wrong zero timeout"); |
| + | fail_unless(ctx.timeout_ms[5] == -1, "wrong zero timeout clear"); |
| + | |
| + | curl_multi_cleanup(handle); |
| + | } |
| + | |
| + | static CURLcode test_unit3230(const char *arg) |
| + | { |
| + | UNITTEST_BEGIN(t3230_setup()) |
| + | |
| + | t3230_test_timer_updates(); |
| + | |
| + | UNITTEST_END(curl_global_cleanup()) |
| + | } |