lib: keepon improving

Improve the name, type and handling of `data->req.keepon`:

- Rename `keepon` to `io_flags`
- make `io_flags` and `uint8_t` and reposition in struct
- Rename `KEEP_*` defines to `REQ_IO_*`, move to request.h
- Replace all direct bit tests to `CURL_REQ_WANT_*` use
- Replace all direct bit manipulations with new macros

Closes #20905
This commit is contained in:
Stefan Eissing 2026-03-12 11:18:32 +01:00 committed by Daniel Stenberg
parent 9325eb5fc4
commit f50446f6da
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
13 changed files with 78 additions and 79 deletions

View File

@ -477,7 +477,7 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session,
* window and *assume* that we treat this like a WINDOW_UPDATE. Some
* servers send an explicit WINDOW_UPDATE, but not all seem to do that.
* To be safe, we UNHOLD a stream in order not to stall. */
if(CURL_WANT_SEND(data)) {
if(CURL_REQ_WANT_SEND(data)) {
drain_tunnel(cf, data, &ctx->tunnel);
}
break;
@ -512,7 +512,7 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session,
}
break;
case NGHTTP2_WINDOW_UPDATE:
if(CURL_WANT_SEND(data)) {
if(CURL_REQ_WANT_SEND(data)) {
drain_tunnel(cf, data, &ctx->tunnel);
}
break;

View File

@ -1617,7 +1617,7 @@ CURLcode Curl_http_perform_pollset(struct Curl_easy *data,
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
if(CURL_WANT_RECV(data)) {
if(CURL_REQ_WANT_RECV(data)) {
result = Curl_pollset_add_in(data, ps, conn->sock[FIRSTSOCKET]);
}
@ -2677,7 +2677,7 @@ static CURLcode http_firstwrite(struct Curl_easy *data)
if(conn->bits.close) {
/* Abort after the headers if "follow Location" is set
and we are set to close anyway. */
k->keepon &= ~KEEP_RECV;
CURL_REQ_CLEAR_RECV(data);
k->done = TRUE;
return CURLE_OK;
}
@ -2696,7 +2696,7 @@ static CURLcode http_firstwrite(struct Curl_easy *data)
infof(data, "The entire document is already downloaded");
streamclose(conn, "already downloaded");
/* Abort download */
k->keepon &= ~KEEP_RECV;
CURL_REQ_CLEAR_RECV(data);
k->done = TRUE;
return CURLE_OK;
}
@ -4139,7 +4139,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) {
/* We rewind before next send, continue sending now */
infof(data, "Keep sending data to get tossed away");
k->keepon |= KEEP_SEND;
CURL_REQ_SET_SEND(data);
}
}

View File

@ -1010,7 +1010,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
Curl_multi_mark_dirty(data);
break;
case NGHTTP2_WINDOW_UPDATE:
if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
if(CURL_REQ_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
/* need more data, force processing of transfer */
Curl_multi_mark_dirty(data);
}
@ -1187,7 +1187,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
* window and *assume* that we treat this like a WINDOW_UPDATE. Some
* servers send an explicit WINDOW_UPDATE, but not all seem to do that.
* To be safe, we UNHOLD a stream in order not to stall. */
if(CURL_WANT_SEND(data))
if(CURL_REQ_WANT_SEND(data))
Curl_multi_mark_dirty(data);
}
break;
@ -1986,7 +1986,7 @@ out:
/* pending data to send, need to be called again. Ideally, we
* monitor the socket for POLLOUT, but when not SENDING
* any more, we force processing of the transfer. */
if(!CURL_WANT_SEND(data))
if(!CURL_REQ_WANT_SEND(data))
Curl_multi_mark_dirty(data);
}
else if(r2) {

View File

@ -1065,7 +1065,7 @@ static CURLcode mstate_perform_pollset(struct Curl_easy *data,
if(conn->scheme->run->perform_pollset)
result = conn->scheme->run->perform_pollset(data, ps);
else {
/* Default is to obey the data->req.keepon flags for send/recv */
/* Default is to obey the request flags for send/recv */
if(Curl_req_want_recv(data) && CONN_SOCK_IDX_VALID(conn->recv_idx)) {
result = Curl_pollset_add_in(data, ps, conn->sock[conn->recv_idx]);
}
@ -1645,7 +1645,6 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
mresult = curl_multi_add_handle(multi, data);
if(!mresult) {
struct SingleRequest *k = &data->req;
CURLcode result;
/* pass in NULL for 'conn' here since we do not want to init the
@ -1659,7 +1658,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
/* take this handle to the perform state right away */
multistate(data, MSTATE_PERFORMING);
Curl_attach_connection(data, conn);
k->keepon |= KEEP_RECV; /* setup to receive! */
CURL_REQ_SET_RECV(data);
}
return mresult;
}

View File

@ -1075,7 +1075,6 @@ static CURLcode pop3_write(struct Curl_easy *data, const char *str,
{
/* This code could be made into a special function in the handler struct */
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
struct connectdata *conn = data->conn;
struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
bool strip_dot = FALSE;
@ -1185,7 +1184,7 @@ static CURLcode pop3_write(struct Curl_easy *data, const char *str,
message as per RFC-1939, sect. 3 */
result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2);
k->keepon &= ~KEEP_RECV;
CURL_REQ_CLEAR_RECV(data);
pop3c->eob = 0;
return result;

View File

@ -132,7 +132,7 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
req->headerline = 0;
req->offset = 0;
req->httpcode = 0;
req->keepon = 0;
req->io_flags = 0;
req->upgr101 = UPGR101_NONE;
req->sendbuf_hds_len = 0;
req->timeofdoc = 0;
@ -256,7 +256,7 @@ static CURLcode req_set_upload_done(struct Curl_easy *data)
{
DEBUGASSERT(!data->req.upload_done);
data->req.upload_done = TRUE;
data->req.keepon &= ~KEEP_SEND; /* we are done sending */
CURL_REQ_CLEAR_SEND(data);
Curl_pgrsTime(data, TIMER_POSTRANSFER);
Curl_creader_done(data, data->req.upload_aborted);
@ -419,22 +419,22 @@ bool Curl_req_sendbuf_empty(struct Curl_easy *data)
bool Curl_req_want_send(struct Curl_easy *data)
{
/* Not done and upload not blocked and either one of
* - KEEP_SEND
* - REQ_IO_SEND
* - request has buffered data to send
* - connection has pending data to send */
return !data->req.done &&
!Curl_rlimit_is_blocked(&data->progress.ul.rlimit) &&
((data->req.keepon & KEEP_SEND) ||
(CURL_REQ_WANT_SEND(data) ||
!Curl_req_sendbuf_empty(data) ||
Curl_xfer_needs_flush(data));
}
bool Curl_req_want_recv(struct Curl_easy *data)
{
/* Not done and download not blocked and KEEP_RECV */
/* Not done and download not blocked and want RECV */
return !data->req.done &&
!Curl_rlimit_is_blocked(&data->progress.dl.rlimit) &&
(data->req.keepon & KEEP_RECV);
CURL_REQ_WANT_RECV(data);
}
bool Curl_req_done_sending(struct Curl_easy *data)
@ -470,7 +470,7 @@ CURLcode Curl_req_abort_sending(struct Curl_easy *data)
if(!data->req.upload_done) {
Curl_bufq_reset(&data->req.sendbuf);
data->req.upload_aborted = TRUE;
data->req.keepon &= ~KEEP_SEND;
CURL_REQ_CLEAR_SEND(data);
return req_set_upload_done(data);
}
return CURLE_OK;
@ -482,8 +482,8 @@ CURLcode Curl_req_stop_send_recv(struct Curl_easy *data)
* We might still be paused on receive client writes though, so
* keep those bits around. */
CURLcode result = CURLE_OK;
if(data->req.keepon & KEEP_SEND)
if(CURL_REQ_WANT_SEND(data))
result = Curl_req_abort_sending(data);
data->req.keepon &= ~(KEEP_RECV | KEEP_SEND);
CURL_REQ_CLEAR_IO(data);
return result;
}

View File

@ -31,6 +31,24 @@
/* forward declarations */
struct UserDefined;
/* Bits on the io_flags member of SingleRequest */
#define REQ_IO_RECV (1 << 0) /* there is or may be data to read */
#define REQ_IO_SEND (1 << 1) /* there is or may be data to write */
/* Low level request receive/send io_flags checks. */
#define CURL_REQ_WANT_SEND(d) ((d)->req.io_flags & REQ_IO_SEND)
#define CURL_REQ_WANT_RECV(d) ((d)->req.io_flags & REQ_IO_RECV)
#define CURL_REQ_WANT_IO(d) ((d)->req.io_flags & (REQ_IO_RECV|REQ_IO_SEND))
/* Low level request receive/send io_flags manipulations. */
#define CURL_REQ_SET_SEND(d) ((d)->req.io_flags |= REQ_IO_SEND)
#define CURL_REQ_SET_RECV(d) ((d)->req.io_flags |= REQ_IO_RECV)
#define CURL_REQ_CLEAR_SEND(d) \
((d)->req.io_flags &= (uint8_t)~REQ_IO_SEND)
#define CURL_REQ_CLEAR_RECV(d) \
((d)->req.io_flags &= (uint8_t)~REQ_IO_RECV)
#define CURL_REQ_CLEAR_IO(d) \
((d)->req.io_flags &= (uint8_t)~(REQ_IO_RECV|REQ_IO_SEND))
enum expect100 {
EXP100_SEND_DATA, /* enough waiting, send the body now */
EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */
@ -59,6 +77,8 @@ struct SingleRequest {
-1 means unlimited */
curl_off_t bytecount; /* total number of bytes read */
curl_off_t writebytecount; /* number of bytes written */
curl_off_t offset; /* possible resume offset read from the
Content-Range: header */
struct curltime start; /* transfer started at this time */
unsigned int headerbytecount; /* received server headers (not CONNECT
@ -72,11 +92,8 @@ struct SingleRequest {
in a CURLE_GOT_NOTHING error code */
int headerline; /* counts header lines to better track the
first one */
curl_off_t offset; /* possible resume offset read from the
Content-Range: header */
int httpcode; /* error code from the 'HTTP/1.? XXX' or
'RTSP/1.? XXX' line */
int keepon;
unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */
unsigned char httpversion; /* Version in response (09, 10, 11, etc.) */
enum upgrade101 upgr101; /* 101 upgrade state */
@ -94,6 +111,7 @@ struct SingleRequest {
header data */
char *newurl; /* Set to the new URL to use when a redirect or a retry is
wanted */
uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */
#ifndef CURL_DISABLE_COOKIES
unsigned char setcookies;

View File

@ -303,15 +303,15 @@ static CURLcode sendrecv_dl(struct Curl_easy *data,
* we should read the EOS. Which may arrive as meta data after
* the bytes. Not taking it in might lead to RST of streams. */
if((!is_multiplex && data->req.download_done) || is_eos) {
data->req.keepon &= ~KEEP_RECV;
CURL_REQ_CLEAR_RECV(data);
}
/* if we stopped receiving, leave the loop */
if(!(k->keepon & KEEP_RECV))
if(!CURL_REQ_WANT_RECV(data))
break;
} while(maxloops--);
if(!is_eos && !rate_limited && CURL_WANT_RECV(data) &&
if(!is_eos && !rate_limited && CURL_REQ_WANT_RECV(data) &&
(!rcvd_eagain || data_pending(data, rcvd_eagain))) {
/* Did not read until EAGAIN/EOS or there is still data pending
* in buffers. Mark as read-again via simulated SELECT results. */
@ -319,7 +319,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data,
CURL_TRC_M(data, "sendrecv_dl() no EAGAIN/pending data, mark as dirty");
}
if(((k->keepon & (KEEP_RECV | KEEP_SEND)) == KEEP_SEND) &&
if(!CURL_REQ_WANT_RECV(data) && CURL_REQ_WANT_SEND(data) &&
(conn->bits.close || is_multiplex)) {
/* When we have read the entire thing and the close bit is set, the server
may now close the connection. If there is now any kind of sending going
@ -340,9 +340,7 @@ out:
*/
static CURLcode sendrecv_ul(struct Curl_easy *data)
{
/* We should not get here when the sending is already done. It
* probably means that someone set `data-req.keepon |= KEEP_SEND`
* when it should not. */
/* We should not get here when the sending is already done. */
DEBUGASSERT(!Curl_req_done_sending(data));
if(!Curl_req_done_sending(data))
@ -366,7 +364,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data)
/* We go ahead and do a read if we have a readable socket or if the stream
was rewound (in which case we have data in a buffer) */
if(k->keepon & KEEP_RECV) {
if(CURL_REQ_WANT_RECV(data)) {
result = sendrecv_dl(data, k);
if(result || data->req.done)
goto out;
@ -383,7 +381,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data)
if(result)
goto out;
if(k->keepon) {
if(CURL_REQ_WANT_IO(data)) {
if(Curl_timeleft_ms(data) < 0) {
if(k->size != -1) {
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
@ -419,7 +417,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data)
}
/* If there is nothing more to send/recv, the request is done */
if((k->keepon & (KEEP_RECV | KEEP_SEND)) == 0)
if(!CURL_REQ_WANT_IO(data))
data->req.done = TRUE;
result = Curl_pgrsUpdate(data);
@ -710,14 +708,11 @@ static void xfer_setup(
/* we want header and/or body, if neither then do not do this! */
if(conn->scheme->run->write_resp_hd || !data->req.no_body) {
if(conn->recv_idx != -1)
k->keepon |= KEEP_RECV;
CURL_REQ_SET_RECV(data);
if(conn->send_idx != -1)
k->keepon |= KEEP_SEND;
CURL_REQ_SET_SEND(data);
}
CURL_TRC_M(data, "xfer_setup: recv_idx=%d, send_idx=%d",
conn->recv_idx, conn->send_idx);
}
@ -868,8 +863,8 @@ CURLcode Curl_xfer_send_close(struct Curl_easy *data)
bool Curl_xfer_is_blocked(struct Curl_easy *data)
{
bool want_send = (data->req.keepon & KEEP_SEND);
bool want_recv = (data->req.keepon & KEEP_RECV);
bool want_send = CURL_REQ_WANT_SEND(data);
bool want_recv = CURL_REQ_WANT_RECV(data);
if(!want_send)
return want_recv && Curl_xfer_recv_is_paused(data);
else if(!want_recv)

View File

@ -388,19 +388,6 @@ struct hostname {
const char *dispname; /* name to display, as 'name' might be encoded */
};
/*
* Flags on the keepon member of the Curl_transfer_keeper
*/
#define KEEP_NONE 0
#define KEEP_RECV (1 << 0) /* there is or may be data to read */
#define KEEP_SEND (1 << 1) /* there is or may be data to write */
/* transfer wants to send */
#define CURL_WANT_SEND(data) ((data)->req.keepon & KEEP_SEND)
/* transfer wants to receive */
#define CURL_WANT_RECV(data) ((data)->req.keepon & KEEP_RECV)
#define FIRSTSOCKET 0
#define SECONDARYSOCKET 1

View File

@ -696,8 +696,8 @@ static void myssh_block2waitfor(struct connectdata *conn,
int dir = ssh_get_poll_flags(sshc->ssh_session);
/* translate the libssh define bits into our own bit defines */
sshc->waitfor =
((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) |
((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0);
((dir & SSH_READ_PENDING) ? REQ_IO_RECV : 0) |
((dir & SSH_WRITE_PENDING) ? REQ_IO_SEND : 0);
}
else
sshc->waitfor = 0;
@ -1084,7 +1084,7 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data,
/* upload data */
Curl_xfer_setup_send(data, FIRSTSOCKET);
/* not set by Curl_xfer_setup to preserve keepon bits */
/* not set by Curl_xfer_setup to preserve io_flags */
data->conn->recv_idx = FIRSTSOCKET;
/* since we do not really wait for anything at this point, we want the
@ -1205,7 +1205,7 @@ static int myssh_in_SFTP_DOWNLOAD_STAT(struct Curl_easy *data,
}
Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size);
/* not set by Curl_xfer_setup to preserve keepon bits */
/* not set by Curl_xfer_setup to preserve io_flags */
data->conn->send_idx = 0;
sshc->sftp_recv_state = 0;
@ -1736,7 +1736,7 @@ static int myssh_SSH_SCP_DOWNLOAD(struct Curl_easy *data,
data->req.maxdownload = bytecount;
Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount);
/* not set by Curl_xfer_setup to preserve keepon bits */
/* not set by Curl_xfer_setup to preserve io_flags */
data->conn->send_idx = 0;
myssh_to(data, sshc, SSH_STOP);
@ -2159,7 +2159,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
/* upload data */
Curl_xfer_setup_send(data, FIRSTSOCKET);
/* not set by Curl_xfer_setup to preserve keepon bits */
/* not set by Curl_xfer_setup to preserve io_flags */
data->conn->recv_idx = FIRSTSOCKET;
myssh_to(data, sshc, SSH_STOP);
@ -2268,12 +2268,12 @@ static CURLcode myssh_pollset(struct Curl_easy *data,
if(!sshc || (sock == CURL_SOCKET_BAD))
return CURLE_FAILED_INIT;
waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon;
waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags;
if(waitfor) {
int flags = 0;
if(waitfor & KEEP_RECV)
if(waitfor & REQ_IO_RECV)
flags |= CURL_POLL_IN;
if(waitfor & KEEP_SEND)
if(waitfor & REQ_IO_SEND)
flags |= CURL_POLL_OUT;
DEBUGASSERT(flags);
CURL_TRC_SSH(data, "pollset, flags=%x", flags);
@ -2596,7 +2596,7 @@ static CURLcode myssh_done(struct Curl_easy *data,
if(Curl_pgrsDone(data))
return CURLE_ABORTED_BY_CALLBACK;
data->req.keepon = 0; /* clear all bits */
CURL_REQ_CLEAR_IO(data);
return result;
}

View File

@ -1054,7 +1054,7 @@ static CURLcode sftp_upload_init(struct Curl_easy *data,
/* upload data */
Curl_xfer_setup_send(data, FIRSTSOCKET);
/* not set by Curl_xfer_setup to preserve keepon bits */
/* not set by Curl_xfer_setup to preserve io_flags */
data->conn->recv_idx = FIRSTSOCKET;
/* since we do not really wait for anything at this point, we want the
@ -1358,7 +1358,7 @@ static CURLcode sftp_download_stat(struct Curl_easy *data,
}
Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size);
/* not set by Curl_xfer_setup to preserve keepon bits */
/* not set by Curl_xfer_setup to preserve io_flags */
data->conn->send_idx = 0;
myssh_to(data, sshc, SSH_STOP);
@ -2265,7 +2265,7 @@ static CURLcode ssh_state_scp_download_init(struct Curl_easy *data,
data->req.maxdownload = (curl_off_t)sb.st_size;
Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount);
/* not set by Curl_xfer_setup to preserve keepon bits */
/* not set by Curl_xfer_setup to preserve io_flags */
data->conn->send_idx = 0;
myssh_to(data, sshc, SSH_STOP);
@ -2411,7 +2411,7 @@ static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data,
Curl_pgrsSetUploadSize(data, data->state.infilesize);
Curl_xfer_setup_send(data, FIRSTSOCKET);
/* not set by Curl_xfer_setup to preserve keepon bits */
/* not set by Curl_xfer_setup to preserve io_flags */
data->conn->recv_idx = FIRSTSOCKET;
myssh_to(data, sshc, SSH_STOP);
@ -3023,12 +3023,12 @@ static CURLcode ssh_pollset(struct Curl_easy *data,
if(!sshc || (sock == CURL_SOCKET_BAD))
return CURLE_FAILED_INIT;
waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon;
waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags;
if(waitfor) {
int flags = 0;
if(waitfor & KEEP_RECV)
if(waitfor & REQ_IO_RECV)
flags |= CURL_POLL_IN;
if(waitfor & KEEP_SEND)
if(waitfor & REQ_IO_SEND)
flags |= CURL_POLL_OUT;
DEBUGASSERT(flags);
CURL_TRC_SSH(data, "pollset, flags=%x", flags);
@ -3057,8 +3057,9 @@ static void ssh_block2waitfor(struct Curl_easy *data,
dir = libssh2_session_block_directions(sshc->ssh_session);
if(dir) {
/* translate the libssh2 define bits into our own bit defines */
sshc->waitfor = ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? KEEP_RECV : 0) |
((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? KEEP_SEND : 0);
sshc->waitfor =
((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? REQ_IO_RECV : 0) |
((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? REQ_IO_SEND : 0);
}
}
if(!dir)
@ -3519,7 +3520,7 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
if(Curl_pgrsDone(data))
return CURLE_ABORTED_BY_CALLBACK;
data->req.keepon = 0; /* clear all bits */
CURL_REQ_CLEAR_IO(data);
return result;
}

View File

@ -162,7 +162,7 @@ struct ssh_conn {
int secondCreateDirs; /* counter use by the code to see if the
second attempt has been made to change
to/create a directory */
int waitfor; /* KEEP_RECV/KEEP_SEND bits overriding
int waitfor; /* REQ_IO_RECV/REQ_IO_SEND bits overriding
pollset given flags */
char *slash_pos; /* used by the SFTP_CREATE_DIRS state */

View File

@ -1399,7 +1399,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
if(result)
goto out;
DEBUGASSERT(nread == nwritten);
k->keepon &= ~KEEP_RECV; /* read no more content */
CURL_REQ_CLEAR_RECV(data); /* read no more content */
}
else { /* !connect_only */
if(data->set.method == HTTPREQ_PUT) {
@ -1423,7 +1423,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
/* start over with sending */
data->req.eos_read = FALSE;
data->req.upload_done = FALSE;
k->keepon |= KEEP_SEND;
CURL_REQ_SET_SEND(data);
}
/* Then pass any additional data to the writers */