curl_multi_assign
curl_multi_get_handles
curl_multi_get_offt
+curl_multi_notify_disable
+curl_multi_notify_enable
curl_pushheader_bynum
curl_pushheader_byname
-curl_multi_waitfds
curl_easy_option_by_name
curl_easy_option_by_id
curl_easy_option_next
curl_multi_get_offt.3 \
curl_multi_info_read.3 \
curl_multi_init.3 \
+ curl_multi_notify_disable.3 \
+ curl_multi_notify_enable.3 \
curl_multi_perform.3 \
curl_multi_poll.3 \
curl_multi_remove_handle.3 \
--- /dev/null
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Title: curl_multi_notify_disable
+Section: 3
+Source: libcurl
+See-also:
+ - CURLMOPT_NOTIFYFUNCTION (3)
+ - CURLMOPT_NOTIFYDATA (3)
+ - curl_multi_notify_enable (3)
+Protocol:
+ - All
+Added-in: 8.17.0
+---
+
+# NAME
+
+curl_multi_notify_disable - disable a notification type
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+CURLMcode curl_multi_notify_disable(CURLM *multi_handle,
+ unsigned int notification);
+~~~
+
+# DESCRIPTION
+
+Disables collecting the given notification type in the multi handle. A
+callback function installed via CURLMOPT_NOTIFYFUNCTION(3) is no longer
+called when this notification happens.
+
+Only when a notification callback is installed *and* a notification
+is enabled are these collected and dispatched to the callback.
+
+Several notification types can be enabled at the same time. Disabling
+an already disabled notification is not an error.
+
+A notification can be enabled again via curl_multi_notify_enable(3).
+
+# %PROTOCOLS%
+
+# EXAMPLE
+
+~~~c
+int main(void)
+{
+ int rc;
+ CURLM *multi = curl_multi_init();
+
+ rc = curl_multi_notify_disable(multi, CURLM_NTFY_INFO_READ);
+}
+~~~
+
+# %AVAILABILITY%
+
+# RETURN VALUE
+
+This function returns a CURLMcode indicating success or error.
+
+CURLM_OK (0) means everything was OK, non-zero means an error occurred, see
+libcurl-errors(3).
+
+The return code is for the whole multi stack. Problems still might have
+occurred on individual transfers even when one of these functions return OK.
--- /dev/null
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Title: curl_multi_notify_enable
+Section: 3
+Source: libcurl
+See-also:
+ - CURLMOPT_NOTIFYFUNCTION (3)
+ - CURLMOPT_NOTIFYDATA (3)
+ - curl_multi_notify_disable (3)
+Protocol:
+ - All
+Added-in: 8.17.0
+---
+
+# NAME
+
+curl_multi_notify_enable - enable a notification type
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+CURLMcode curl_multi_notify_enable(CURLM *multi_handle,
+ unsigned int notification);
+~~~
+
+# DESCRIPTION
+
+Enables collecting the given notification type in the multi handle. A
+callback function installed via CURLMOPT_NOTIFYFUNCTION(3) is called
+when this notification happens.
+
+Only when a notification callback is installed *and* a notification
+is enabled are these collected and dispatched to the callback.
+
+Several notification types can be enabled at the same time. Enabling
+an already enabled notification is not an error.
+
+A notification can be disabled again via curl_multi_notify_disable(3).
+
+# %PROTOCOLS%
+
+# EXAMPLE
+
+~~~c
+int main(void)
+{
+ int rc;
+ CURLM *multi = curl_multi_init();
+
+ rc = curl_multi_notify_enable(multi, CURLM_NTFY_INFO_READ);
+}
+~~~
+
+# %AVAILABILITY%
+
+# RETURN VALUE
+
+This function returns a CURLMcode indicating success or error.
+
+CURLM_OK (0) means everything was OK, non-zero means an error occurred, see
+libcurl-errors(3).
+
+The return code is for the whole multi stack. Problems still might have
+occurred on individual transfers even when one of these functions return OK.
Signal that the network has changed. See CURLMOPT_NETWORK_CHANGED(3)
+## CURLMOPT_NOTIFYDATA
+
+Custom pointer passed to the notify callback. See CURLMOPT_NOTIFYDATA(3)
+
+## CURLMOPT_NOTIFYFUNCTION
+
+Callback that receives notifications. See CURLMOPT_NOTIFYFUNCTION(3)
+
## CURLMOPT_PIPELINING
Enable HTTP multiplexing. See CURLMOPT_PIPELINING(3)
--- /dev/null
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Title: CURLMOPT_NOTIFYDATA
+Section: 3
+Source: libcurl
+See-also:
+ - CURLMOPT_NOTIFYFUNCTION (3)
+ - curl_multi_notify_disable (3)
+ - curl_multi_notify_enable (3)
+Protocol:
+ - All
+Added-in: 8.17.0
+---
+
+# NAME
+
+CURLMOPT_NOTIFYDATA - custom pointer passed to the notification callback
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+
+CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NOTIFYDATA, void *pointer);
+~~~
+
+# DESCRIPTION
+
+A data *pointer* to pass to the notification callback set with the
+CURLMOPT_NOTIFYFUNCTION(3) option.
+
+This pointer is not touched by libcurl but is only passed in as the
+notification callback's **clientp** argument.
+
+# DEFAULT
+
+NULL
+
+# %PROTOCOLS%
+
+# EXAMPLE
+
+~~~c
+struct priv {
+ void *ours;
+};
+
+static void ntfy_cb(CURLM *multi, unsigned int notification,
+ CURL *easy, void *ntfyp)
+{
+ struct priv *p = ntfyp;
+ printf("my ptr: %p\n", p->ours);
+ /* ... */
+}
+
+int main(void)
+{
+ struct priv setup;
+ CURLM *multi = curl_multi_init();
+ /* ... use socket callback and custom pointer */
+ curl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, ntfy_cb);
+ curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, &setup);
+ curl_multi_notify_enable(multi, CURLM_NTFY_INFO_READ);
+}
+~~~
+
+# %AVAILABILITY%
+
+# RETURN VALUE
+
+Returns CURLM_OK.
--- /dev/null
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Title: CURLMOPT_NOTIFYFUNCTION
+Section: 3
+Source: libcurl
+See-also:
+ - CURLMOPT_NOTIFYDATA (3)
+ - curl_multi_socket_action (3)
+ - curl_multi_notify_disable (3)
+ - curl_multi_notify_enable (3)
+Protocol:
+ - All
+Added-in: 8.17.0
+---
+
+# NAME
+
+CURLMOPT_NOTIFYFUNCTION - callback receiving notifications
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+
+void ntfy_callback(CURLM *multi, /* multi handle */
+ unsigned int notification, /* notification type */
+ CURL *easy, /* easy handle */
+ void *ntfyp); /* private ntfy pointer */
+
+CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NOTIFYFUNCTION, ntfy_callback);
+~~~
+
+# DESCRIPTION
+
+Pass a pointer to your callback function, which should match the prototype
+shown above.
+
+When the multi handle processes transfers, changes can be observed
+by receiving notifications about them. This can eliminate the need to
+constantly interrogate the multi handle to observe such changes to
+act on them.
+
+Notifications are collected and dispatched to the application's callback
+function at an appropriate time.
+
+The notify callback is different from other callbacks in that it
+can use more libcurl API functions. Apart from curl_multi_perform(3),
+curl_multi_socket(3), curl_multi_socket_action(3), curl_multi_socket_all(3)
+and curl_multi_cleanup(3) it may call all other methods on the
+multi and easy handles. This includes adding and removing easy
+handles to/from the multi handle.
+
+This callback may get invoked at any time when interacting with libcurl.
+This may even happen after all transfers are done and *may also*
+happen *during* a call to curl_multi_cleanup(3) when cached connections
+are shut down.
+
+# CALLBACK ARGUMENTS
+
+*multi* identifies the multi handle that triggered the notification.
+
+**notification** is the type of notification, e.g. what happened. The
+following types are available:
+
+## CURLM_NTFY_INFO_READ
+
+When enabled via curl_multi_notify_enable(3), this informs the application
+that there are new messages to be processed via curl_multi_info_read(3).
+
+This notification happens whenever a message is added to an empty
+message stack in the multi handle and not for subsequent additions. The
+notification callback is then expected to read all available message,
+emptying the stack, so a subsequent addition triggers the notification
+again.
+
+The *easy* handle passed is an internal handle.
+
+## CURLM_NTFY_EASY_DONE
+
+When enabled via curl_multi_notify_enable(3), this notification is triggered
+when a an easy handle has finished. This happens both for
+successful and failed transfers.
+
+The *easy* handle passed is the transfer that is done. This *may* be
+an internal handle when DoH or other features are used.
+
+*easy* identifies the transfer involved. This may be one of the
+application's own easy handle or an internal handle.
+
+**ntfyp** is set with CURLMOPT_NOTIFYDATA(3).
+
+# DEFAULT
+
+NULL (no callback)
+
+# %PROTOCOLS%
+
+# EXAMPLE
+
+~~~c
+struct priv {
+ void *ours;
+};
+
+static void ntfy_cb(CURLM *multi, unsigned int notification,
+ CURL *easy, void *ntfyp)
+{
+ struct priv *p = ntfyp;
+ printf("my ptr: %p\n", p->ours);
+ /* ... */
+}
+
+int main(void)
+{
+ struct priv setup;
+ CURLM *multi = curl_multi_init();
+ /* ... use socket callback and custom pointer */
+ curl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, ntfy_cb);
+ curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, &setup);
+ curl_multi_notify_enable(multi, CURLM_NTFY_INFO_READ);
+}
+~~~
+
+# %AVAILABILITY%
+
+# RETURN VALUE
+
+Returns CURLM_OK.
CURLMOPT_MAX_TOTAL_CONNECTIONS.3 \
CURLMOPT_MAXCONNECTS.3 \
CURLMOPT_NETWORK_CHANGED.3 \
+ CURLMOPT_NOTIFYDATA.3 \
+ CURLMOPT_NOTIFYFUNCTION.3 \
CURLMOPT_PIPELINING.3 \
CURLMOPT_PIPELINING_SERVER_BL.3 \
CURLMOPT_PIPELINING_SITE_BL.3 \
CURLM_CALL_MULTI_PERFORM 7.9.6
CURLM_CALL_MULTI_SOCKET 7.15.5
CURLM_INTERNAL_ERROR 7.9.6
+CURLM_NTFY_EASY_DONE 8.17.0
+CURLM_NTFY_INFO_READ 8.17.0
CURLM_OK 7.9.6
CURLM_OUT_OF_MEMORY 7.9.6
CURLM_RECURSIVE_API_CALL 7.59.0
CURLMOPT_MAX_TOTAL_CONNECTIONS 7.30.0
CURLMOPT_MAXCONNECTS 7.16.3
CURLMOPT_NETWORK_CHANGED 8.16.0
+CURLMOPT_NOTIFYDATA 8.17.0
+CURLMOPT_NOTIFYFUNCTION 8.17.0
CURLMOPT_PIPELINING 7.16.0
CURLMOPT_PIPELINING_SERVER_BL 7.30.0
CURLMOPT_PIPELINING_SITE_BL 7.30.0
/* network has changed, adjust caches/connection reuse */
CURLOPT(CURLMOPT_NETWORK_CHANGED, CURLOPTTYPE_LONG, 17),
+ /* This is the notify callback function pointer */
+ CURLOPT(CURLMOPT_NOTIFYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 18),
+
+ /* This is the argument passed to the notify callback */
+ CURLOPT(CURLMOPT_NOTIFYDATA, CURLOPTTYPE_OBJECTPOINT, 19),
+
CURLMOPT_LASTENTRY /* the last unused */
} CURLMoption;
unsigned int size,
unsigned int *fd_count);
+/*
+ * Notifications dispatched by a multi handle, when enabled.
+ */
+#define CURLM_NTFY_INFO_READ 0
+#define CURLM_NTFY_EASY_DONE 1
+
+/*
+ * Callback to install via CURLMOPT_NOTIFYFUNCTION.
+ */
+typedef void (*curl_notify_callback)(CURLM *multi,
+ unsigned int notification,
+ CURL *easy,
+ void *user_data);
+
+
+CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *multi,
+ unsigned int notification);
+
+CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *multi,
+ unsigned int notification);
+
#ifdef __cplusplus
} /* end of extern "C" */
#endif
if(curlcheck_charpp_option(option)) \
if(!curlcheck_ptrptr(value, char)) \
Wcurl_multi_setopt_err_charpp(); \
+ if((option) == CURLMOPT_NOTIFYFUNCTION) \
+ if(!curlcheck_multintfy_cb(value)) \
+ Wcurl_multi_setopt_err_ntfycb(); \
if((option) == CURLMOPT_PUSHFUNCTION) \
if(!curlcheck_multipush_cb(value)) \
Wcurl_multi_setopt_err_pushcb(); \
/* evaluates to true if the option takes a data argument to pass to a
callback */
#define curlcheck_multicb_data_option(option) \
- ((option) == CURLMOPT_PUSHDATA || \
+ ((option) == CURLMOPT_NOTIFYDATA || \
+ (option) == CURLMOPT_PUSHDATA || \
(option) == CURLMOPT_SOCKETDATA || \
(option) == CURLMOPT_TIMERDATA || \
0)
(curlcheck_NULL(expr) || \
curlcheck_cb_compatible((expr), curl_push_callback))
+/* evaluates to true if expr is of type curl_push_callback */
+#define curlcheck_multintfy_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_notify_callback))
+
/*
* For now, just make sure that the functions are called with three arguments
*/
"curl_multi_setopt expects a 'char **' argument")
CURLWARNING(Wcurl_multi_setopt_err_pushcb,
"curl_multi_setopt expects a curl_push_callback argument")
+CURLWARNING(Wcurl_multi_setopt_err_ntfycb,
+ "curl_multi_setopt expects a curl_notify_callback argument")
CURLWARNING(Wcurl_multi_setopt_err_socketcb,
"curl_multi_setopt expects a curl_socket_callback argument")
CURLWARNING(Wcurl_multi_setopt_err_timercb,
mqtt.c \
multi.c \
multi_ev.c \
+ multi_ntfy.c \
netrc.c \
noproxy.c \
openldap.c \
mqtt.h \
multihandle.h \
multi_ev.h \
+ multi_ntfy.h \
multiif.h \
netrc.h \
noproxy.h \
curl_multi_get_offt
curl_multi_info_read
curl_multi_init
+curl_multi_notify_disable
+curl_multi_notify_enable
curl_multi_perform
curl_multi_poll
curl_multi_remove_handle
#endif
data->mstate = state;
-
- if(state == MSTATE_COMPLETED) {
+ switch(state) {
+ case MSTATE_DONE:
+ CURLM_NTFY(data, CURLM_NTFY_EASY_DONE);
+ break;
+ case MSTATE_COMPLETED:
+ /* we sometimes directly jump to COMPLETED, trigger also a notification
+ * in that case. */
+ if(oldstate < MSTATE_DONE)
+ CURLM_NTFY(data, CURLM_NTFY_EASY_DONE);
/* changing to COMPLETED means it is in process and needs to go */
DEBUGASSERT(Curl_uint_bset_contains(&data->multi->process, data->mid));
Curl_uint_bset_remove(&data->multi->process, data->mid);
/* free the transfer buffer when we have no more active transfers */
multi_xfer_bufs_free(data->multi);
}
+ break;
+ default:
+ break;
}
/* if this state has an init-function, run it */
*/
static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg)
{
+ if(!Curl_llist_count(&multi->msglist))
+ CURLM_NTFY(multi->admin, CURLM_NTFY_INFO_READ);
Curl_llist_append(&multi->msglist, msg, &msg->list);
}
multi->magic = CURL_MULTI_HANDLE;
Curl_dnscache_init(&multi->dnscache, dnssize);
+ Curl_mntfy_init(multi);
Curl_multi_ev_init(multi, ev_hashsize);
Curl_uint_tbl_init(&multi->xfers, NULL);
Curl_uint_bset_init(&multi->process);
multi->max_concurrent_streams = 100;
multi->last_timeout_ms = -1;
- if(Curl_uint_bset_resize(&multi->process, xfer_table_size) ||
+ if(Curl_mntfy_resize(multi) ||
+ Curl_uint_bset_resize(&multi->process, xfer_table_size) ||
Curl_uint_bset_resize(&multi->pending, xfer_table_size) ||
Curl_uint_bset_resize(&multi->dirty, xfer_table_size) ||
Curl_uint_bset_resize(&multi->msgsent, xfer_table_size) ||
multi->admin->multi = NULL;
Curl_close(&multi->admin);
}
+ Curl_mntfy_cleanup(multi);
Curl_uint_bset_destroy(&multi->process);
Curl_uint_bset_destroy(&multi->dirty);
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
+ if(multi->in_ntfy_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
sigpipe_init(&pipe_st);
if(Curl_uint_bset_first(&multi->process, &mid)) {
CURL_TRC_M(multi->admin, "multi_perform(running=%u)",
if(multi_ischanged(m, TRUE))
process_pending_handles(m);
+ if(!returncode)
+ returncode = Curl_mntfy_dispatch_all(multi);
+
/*
* Simply remove all expired timers from the splay since handles are dealt
* with unconditionally by this function and curl_multi_timeout() requires
unsigned int mid;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
+ if(multi->in_ntfy_callback)
+ return CURLM_RECURSIVE_API_CALL;
/* First remove all remaining easy handles,
* close internal ones. admin handle is special */
#endif
multi_xfer_bufs_free(multi);
+ Curl_mntfy_cleanup(multi);
#ifdef DEBUGBUILD
if(Curl_uint_tbl_count(&multi->xfers)) {
multi_xfer_tbl_dump(multi);
if(multi_ischanged(multi, TRUE))
process_pending_handles(multi);
+ if(!result)
+ result = Curl_mntfy_dispatch_all(multi);
+
if(running_handles) {
unsigned int running = Curl_multi_xfers_running(multi);
*running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
}
break;
}
+ case CURLMOPT_NOTIFYFUNCTION:
+ multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback);
+ break;
+ case CURLMOPT_NOTIFYDATA:
+ multi->ntfy.ntfy_cb_data = va_arg(param, void *);
+ break;
default:
res = CURLM_UNKNOWN_OPTION;
break;
struct Curl_multi *multi = m;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
+ if(multi->in_ntfy_callback)
+ return CURLM_RECURSIVE_API_CALL;
return multi_socket(multi, FALSE, s, 0, running_handles);
}
struct Curl_multi *multi = m;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
+ if(multi->in_ntfy_callback)
+ return CURLM_RECURSIVE_API_CALL;
return multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
}
struct Curl_multi *multi = m;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
+ if(multi->in_ntfy_callback)
+ return CURLM_RECURSIVE_API_CALL;
return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
}
Curl_uint_bset_remove(&data->multi->dirty, data->mid);
}
+CURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification)
+{
+ struct Curl_multi *multi = m;
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+ return Curl_mntfy_enable(multi, notification);
+}
+
+CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification)
+{
+ struct Curl_multi *multi = m;
+
+ if(!GOOD_MULTI_HANDLE(multi))
+ return CURLM_BAD_HANDLE;
+ return Curl_mntfy_disable(multi, notification);
+}
+
#ifdef DEBUGBUILD
static void multi_xfer_dump(struct Curl_multi *multi, unsigned int mid,
void *entry)
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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 "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "curl_trc.h"
+#include "multihandle.h"
+#include "multiif.h"
+#include "multi_ntfy.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+struct mntfy_entry {
+ unsigned int mid;
+ unsigned int type;
+};
+
+#define CURL_MNTFY_CHUNK_SIZE 128
+
+struct mntfy_chunk {
+ struct mntfy_chunk *next;
+ size_t r_offset;
+ size_t w_offset;
+ struct mntfy_entry entries[CURL_MNTFY_CHUNK_SIZE];
+};
+
+static struct mntfy_chunk *mnfty_chunk_create(void)
+{
+ return calloc(1, sizeof(struct mntfy_chunk));
+}
+
+static void mnfty_chunk_destroy(struct mntfy_chunk *chunk)
+{
+ free(chunk);
+}
+
+static void mnfty_chunk_reset(struct mntfy_chunk *chunk)
+{
+ memset(chunk, 0, sizeof(*chunk));
+}
+
+static bool mntfy_chunk_append(struct mntfy_chunk *chunk,
+ struct Curl_easy *data,
+ unsigned int type)
+{
+ struct mntfy_entry *e;
+
+ if(chunk->w_offset >= CURL_MNTFY_CHUNK_SIZE)
+ return FALSE;
+ e = &chunk->entries[chunk->w_offset++];
+ e->mid = data->mid;
+ e->type = type;
+ return TRUE;
+}
+
+static struct mntfy_chunk *mntfy_non_full_tail(struct curl_multi_ntfy *mntfy)
+{
+ struct mntfy_chunk *chunk;
+ if(!mntfy->tail) {
+ chunk = mnfty_chunk_create();
+ if(!chunk)
+ return NULL;
+ DEBUGASSERT(!mntfy->head);
+ mntfy->head = mntfy->tail = chunk;
+ return chunk;
+ }
+ else if(mntfy->tail->w_offset < CURL_MNTFY_CHUNK_SIZE)
+ return mntfy->tail;
+ else { /* tail is full. */
+ chunk = mnfty_chunk_create();
+ if(!chunk)
+ return NULL;
+ DEBUGASSERT(mntfy->head);
+ mntfy->tail->next = chunk;
+ mntfy->tail = chunk;
+ return chunk;
+ }
+}
+
+static void mntfy_chunk_dispatch_all(struct Curl_multi *multi,
+ struct mntfy_chunk *chunk)
+{
+ struct mntfy_entry *e;
+ struct Curl_easy *data;
+
+ if(multi->ntfy.ntfy_cb) {
+ while((chunk->r_offset < chunk->w_offset) && !multi->ntfy.failure) {
+ e = &chunk->entries[chunk->r_offset];
+ data = e->mid ? Curl_multi_get_easy(multi, e->mid) : multi->admin;
+ /* only when notification has not been disabled in the meantime */
+ if(data && Curl_uint_bset_contains(&multi->ntfy.enabled, e->type)) {
+ /* this may cause new notifications to be added! */
+ CURL_TRC_M(multi->admin, "[NTFY] dispatch %d to xfer %u",
+ e->type, e->mid);
+ multi->ntfy.ntfy_cb(multi, e->type, data, multi->ntfy.ntfy_cb_data);
+ }
+ /* once dispatched, safe to increment */
+ chunk->r_offset++;
+ }
+ }
+ mnfty_chunk_reset(chunk);
+}
+
+void Curl_mntfy_init(struct Curl_multi *multi)
+{
+ memset(&multi->ntfy, 0, sizeof(multi->ntfy));
+ Curl_uint_bset_init(&multi->ntfy.enabled);
+}
+
+CURLMcode Curl_mntfy_resize(struct Curl_multi *multi)
+{
+ if(Curl_uint_bset_resize(&multi->ntfy.enabled, CURLM_NTFY_EASY_DONE + 1))
+ return CURLM_OUT_OF_MEMORY;
+ return CURLM_OK;
+}
+
+void Curl_mntfy_cleanup(struct Curl_multi *multi)
+{
+ while(multi->ntfy.head) {
+ struct mntfy_chunk *chunk = multi->ntfy.head;
+ multi->ntfy.head = chunk->next;
+ mnfty_chunk_destroy(chunk);
+ }
+ multi->ntfy.tail = NULL;
+ Curl_uint_bset_destroy(&multi->ntfy.enabled);
+}
+
+CURLMcode Curl_mntfy_enable(struct Curl_multi *multi, unsigned int type)
+{
+ if(type > CURLM_NTFY_EASY_DONE)
+ return CURLM_UNKNOWN_OPTION;
+ Curl_uint_bset_add(&multi->ntfy.enabled, type);
+ return CURLM_OK;
+}
+
+CURLMcode Curl_mntfy_disable(struct Curl_multi *multi, unsigned int type)
+{
+ if(type > CURLM_NTFY_EASY_DONE)
+ return CURLM_UNKNOWN_OPTION;
+ Curl_uint_bset_remove(&multi->ntfy.enabled, type);
+ return CURLM_OK;
+}
+
+void Curl_mntfy_add(struct Curl_easy *data, unsigned int type)
+{
+ struct Curl_multi *multi = data ? data->multi : NULL;
+ if(multi && multi->ntfy.ntfy_cb && !multi->ntfy.failure &&
+ Curl_uint_bset_contains(&multi->ntfy.enabled, type)) {
+ /* append to list of outstanding notifications */
+ struct mntfy_chunk *tail = mntfy_non_full_tail(&multi->ntfy);
+ CURL_TRC_M(data, "[NTFY] add %d for xfer %u", type, data->mid);
+ if(tail)
+ mntfy_chunk_append(tail, data, type);
+ else
+ multi->ntfy.failure = CURLM_OUT_OF_MEMORY;
+ }
+}
+
+CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi)
+{
+ DEBUGASSERT(!multi->in_ntfy_callback);
+ multi->in_ntfy_callback = TRUE;
+ while(multi->ntfy.head && !multi->ntfy.failure) {
+ struct mntfy_chunk *chunk = multi->ntfy.head;
+ /* this may cause new notifications to be added! */
+ mntfy_chunk_dispatch_all(multi, chunk);
+ DEBUGASSERT(chunk->r_offset == chunk->w_offset);
+
+ if(chunk == multi->ntfy.tail) /* last one, keep */
+ break;
+ DEBUGASSERT(chunk->next);
+ DEBUGASSERT(multi->ntfy.head != multi->ntfy.tail);
+ multi->ntfy.head = chunk->next;
+ mnfty_chunk_destroy(chunk);
+ }
+ multi->in_ntfy_callback = FALSE;
+
+ if(multi->ntfy.failure) {
+ CURLMcode result = multi->ntfy.failure;
+ multi->ntfy.failure = CURLM_OK; /* reset, once delivered */
+ return result;
+ }
+ return CURLM_OK;
+}
--- /dev/null
+#ifndef HEADER_CURL_MULTI_NTFY_H
+#define HEADER_CURL_MULTI_NTFY_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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 "uint-bset.h"
+
+struct Curl_easy;
+struct Curl_multi;
+
+struct curl_multi_ntfy {
+ curl_notify_callback ntfy_cb;
+ void *ntfy_cb_data;
+ struct uint_bset enabled;
+ CURLMcode failure;
+ struct mntfy_chunk *head;
+ struct mntfy_chunk *tail;
+};
+
+void Curl_mntfy_init(struct Curl_multi *multi);
+CURLMcode Curl_mntfy_resize(struct Curl_multi *multi);
+void Curl_mntfy_cleanup(struct Curl_multi *multi);
+
+CURLMcode Curl_mntfy_enable(struct Curl_multi *multi, unsigned int type);
+CURLMcode Curl_mntfy_disable(struct Curl_multi *multi, unsigned int type);
+
+void Curl_mntfy_add(struct Curl_easy *data, unsigned int type);
+
+#define CURLM_NTFY(d,t) \
+ do { if((d) && (d)->multi && (d)->multi->ntfy.ntfy_cb) \
+ Curl_mntfy_add((d), (t)); } while(0)
+
+CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi);
+
+
+#endif /* HEADER_CURL_MULTI_NTFY_H */
#include "cshutdn.h"
#include "hostip.h"
#include "multi_ev.h"
+#include "multi_ntfy.h"
#include "psl.h"
#include "socketpair.h"
#include "uint-bset.h"
/* multi event related things */
struct curl_multi_ev ev;
+ /* multi notification related things */
+ struct curl_multi_ntfy ntfy;
/* `proto_hash` is a general key-value store for protocol implementations
* with the lifetime of the multi handle. The number of elements kept here
BIT(multiplexing); /* multiplexing wanted */
BIT(recheckstate); /* see Curl_multi_connchanged */
BIT(in_callback); /* true while executing a callback */
+ BIT(in_ntfy_callback); /* true while dispatching notifications */
#ifdef USE_OPENSSL
BIT(ssl_seeded);
#endif
'curl_multi_get_offt' => 'API',
'curl_multi_info_read' => 'API',
'curl_multi_init' => 'API',
+ 'curl_multi_notify_disable' => 'API',
+ 'curl_multi_notify_enable' => 'API',
'curl_multi_perform' => 'API',
'curl_multi_remove_handle' => 'API',
'curl_multi_setopt' => 'API',
struct datauv *uv;
};
-static CURLcode check_finished(struct parastate *s);
-
-static void check_multi_info(struct datauv *uv)
-{
- CURLcode result;
-
- result = check_finished(uv->s);
- if(result && !uv->s->result)
- uv->s->result = result;
-
- if(uv->s->more_transfers) {
- result = add_parallel_transfers(uv->s->multi, uv->s->share,
- &uv->s->more_transfers,
- &uv->s->added_transfers);
- if(result && !uv->s->result)
- uv->s->result = result;
- if(result)
- uv_stop(uv->loop);
- }
-}
+static void mnotify(CURLM *multi, unsigned int notification,
+ CURL *easy, void *user_data);
/* callback from libuv on socket activity */
static void on_uv_socket(uv_poll_t *req, int status, int events)
if(uv && uv->s) {
curl_multi_socket_action(uv->s->multi, CURL_SOCKET_TIMEOUT, 0,
&uv->s->still_running);
- check_multi_info(uv);
}
}
uv_poll_stop(&c->poll_handle);
destroy_context(c);
curl_multi_assign(uv->s->multi, s, NULL);
- /* check if we can do more now */
- check_multi_info(uv);
}
break;
default:
curl_mfprintf(tool_stderr, "parallel_event: uv_run() returned\n");
#endif
- result = check_finished(s);
- if(result && !s->result)
- s->result = result;
-
/* early exit called */
if(s->wrapitup) {
if(s->still_running && !s->wrapitup_processed) {
}
break;
}
-
- if(s->more_transfers) {
- result = add_parallel_transfers(s->multi, s->share, &s->more_transfers,
- &s->added_transfers);
- if(result && !s->result)
- s->result = result;
- }
}
result = s->result;
return result;
}
+static void mnotify(CURLM *multi, unsigned int notification,
+ CURL *easy, void *user_data)
+{
+ struct parastate *s = user_data;
+ CURLcode result;
+
+ (void)multi;
+ (void)easy;
+
+ switch(notification) {
+ case CURLM_NTFY_INFO_READ:
+ result = check_finished(s);
+ /* remember first failure */
+ if(result && !s->result)
+ s->result = result;
+ break;
+ default:
+ break;
+ }
+}
+
static CURLcode parallel_transfers(CURLSH *share)
{
CURLcode result;
if(!s->multi)
return CURLE_OUT_OF_MEMORY;
+ (void)curl_multi_setopt(s->multi, CURLMOPT_NOTIFYFUNCTION, mnotify);
+ (void)curl_multi_setopt(s->multi, CURLMOPT_NOTIFYDATA, s);
+ (void)curl_multi_notify_enable(s->multi, CURLM_NTFY_INFO_READ);
+
result = add_parallel_transfers(s->multi, s->share,
&s->more_transfers, &s->added_transfers);
if(result) {
s->mcode = curl_multi_poll(s->multi, NULL, 0, 1000, NULL);
if(!s->mcode)
s->mcode = curl_multi_perform(s->multi, &s->still_running);
- if(!s->mcode)
- result = check_finished(s);
}
(void)progress_meter(s->multi, &s->start, TRUE);
}
+ /* Result is the first failed transfer - if there was one. */
+ result = s->result;
+
/* Make sure to return some kind of error if there was a multi problem */
if(s->mcode) {
result = (s->mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
curl_pushheader_bynum
curl_pushheader_byname
curl_multi_waitfds
+curl_multi_notify_disable
+curl_multi_notify_enable
curl_easy_option_by_name
curl_easy_option_by_id
curl_easy_option_next
# Verify data after the test has been "shot"
<verify>
<limits>
-Allocations: 13500
+Allocations: 13600
</limits>
</verify>
</testcase>
\r
</protocol>
<limits>
-Allocations: 81
+Allocations: 82
Maximum allocated: 33400
</limits>
</verify>
'-n', f'profile-97 /pid == {self._pid}/ {{ @[ustack()] = count(); }} tick-60s {{ exit(0); }}',
'-o', f'{self._file}'
]
+ if sys.platform.startswith('darwin'):
+ # macOS seems to like this for producing symbols in user stacks
+ args.extend(['-p', f'{self._pid}'])
self._proc = subprocess.Popen(args, text=True, cwd=self._run_dir, shell=False)
assert self._proc
/* the maximum sizes we allow specific structs to grow to */
#define MAX_CURL_EASY 5800
#define MAX_CONNECTDATA 1300
-#define MAX_CURL_MULTI 750
+#define MAX_CURL_MULTI 850
#define MAX_CURL_HTTPPOST 112
#define MAX_CURL_SLIST 16
#define MAX_CURL_KHKEY 24