--------------------- PatchSet 4876 Date: 2002/09/05 10:54:26 Author: rbcollins Branch: rfc2616 Tag: (none) Log: client Stream update Members: src/clientStream.c:1.1.4.1->1.1.4.2 Index: squid/src/clientStream.c =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Attic/clientStream.c,v retrieving revision 1.1.4.1 retrieving revision 1.1.4.2 diff -u -r1.1.4.1 -r1.1.4.2 --- squid/src/clientStream.c 5 Sep 2002 10:51:09 -0000 1.1.4.1 +++ squid/src/clientStream.c 5 Sep 2002 10:54:26 -0000 1.1.4.2 @@ -1,6 +1,6 @@ /* - * $Id: clientStream.c,v 1.1.4.1 2002/09/05 10:51:09 rbcollins Exp $ + * $Id: clientStream.c,v 1.1.4.2 2002/09/05 10:54:26 rbcollins Exp $ * * DEBUG: section 87 Client-side Strean routines. * AUTHOR: Robert Collins @@ -33,51 +33,209 @@ * */ +/* a client Stream is a uni directional pipe, with the usual non-blocking + * asynchronous approach present elsewhere in squid. + * + * Each pipe node has a data push function, and a data request function. + * This limits flexability - the data flow is no longer assembled at each + * step. + * + * An alternative approach is to pass each node in the pipe the call- + * back to use on each IO call. This allows the callbacks to be changed + * very easily by a participating node, but requires more maintenance + * in each node (store the call back to the msot recent IO request in + * the nodes context.) Such an approach also prevents dynamically + * changing the pipeline from outside without an additional interface + * method to extract the callback and context from the next node. + * + * One important characteristic of the stream is that the readfunc + * on the terminating node, and the callback on the first node + * will be NULL, and never used. + */ + #include "squid.h" CBDATA_TYPE(clientStreamNode); +/* TODO: rather than each node undeleting the next, have a clientStreamDelete that walks the list + */ + +/* clientStream quick notes: + * + * Each node including the HEAD of the clientStream has a cbdataReference + * held by the stream. Freeing the stream then + * removes that reference, and cbdataFrees every node. + * Any node with other References, and all nodes downstream will only + * free when those references are released. + * Stream nodes MAY hold references to the data + * member of the node. + * + * Specifically - on creation no reference is made. + * If you pass a data variable to a node, give it an + * initial reference. + * If the data member is non-null on FREE, cbdataFree + * WILL be called. + * This you must never call cbdataFree on your own context + * without explicitly setting the stream node data member + * to NULL and cbdataReferenceDone'ing it. + * + * No data member may hold a reference to it's stream node. + * The stream guarantees that DETACH will be called before + * freeing the node, alowing data members to cleanup. + * + * If a node's data holds a reference to something + * that needs to free the stream a circular reference + * list will occur. This results no data being freed + * until that reference is removed. + * One way to accomplish this is to explicitly remove the + * data from your own node before freeing the stream. + * (i.e. + * mycontext = this->data; + * cbdataReferenceDone (mycontext); + * clientStreamFreeLinst (this->head); + * cbdataFree (mycontext); + * return; + */ /* Local functions */ static FREE clientStreamFree; clientStreamNode * -clientStreamNew (CSR *func, void *data) +clientStreamNew (CSR *readfunc, CSCB *callback, CSD *detach, CSS *status, void *data) { clientStreamNode *temp; CBDATA_INIT_TYPE_FREECB(clientStreamNode, clientStreamFree); temp = cbdataAlloc (clientStreamNode); - temp->func = func; + temp->readfunc = readfunc; + temp->callback = callback; + temp->detach = detach; + temp->status = status; temp->data = data; return temp; } +/* Initialise a client Stream. + * list is the stream + * func is the read function for the head + * callback is the callback for the tail + * tailbuf and taillen are the initial buffer and length for the tail. + */ +void +clientStreamInit (dlink_list *list, CSR *func, CSD *rdetach, CSS* readstatus, void *readdata, + CSCB*callback, CSD* cdetach, void *callbackdata, char *tailbuf, size_t taillen) +{ + clientStreamNode *temp = clientStreamNew(func, NULL, rdetach, readstatus, readdata); + dlinkAdd (temp, &temp->node, list); + cbdataReference (temp); + temp->head = list; + clientStreamInsertHead (list, NULL, callback, cdetach, NULL, callbackdata); + temp = list->tail->data; + temp->readbuf = tailbuf; + temp->readlen = taillen; +} + +/* Doesn't actually insert at head. Instead it inserts one *after* + * head. This is because HEAD is a special node, as is tail + * This function is not suitable for inserting the real HEAD. + * TODO: should we always initalise the buffers and length, to + * allow safe insertion of elements in the downstream cycle? + */ void -clientStreamInsertHead (clientStreamNode **head, CSR *func, void *data) +clientStreamInsertHead (dlink_list *list, CSR *func, CSCB *callback, CSD *detach, CSS *status, void *data) { clientStreamNode *temp; /* test preconditions */ - assert (head != NULL); - temp = clientStreamNew(func, data); - temp->next = *head; - *head = cbdataReference (temp); + assert (list != NULL); + assert (list->head); + temp = clientStreamNew(func, callback, detach, status, data); + temp->head = list; + debug (87,3)("clientStreamInsertHead: Inserted node %p with data %p after head\n", temp, data); + dlinkAddAfter (temp, &temp->node, list->head, list); + cbdataReference (temp); } +/* Callback the next node the in chain with it's requested data + */ +void +clientStreamCallback (clientStreamNode *this, clientHttpRequest *http, HttpReply *rep, const char *body_data, ssize_t body_size) +{ + /* No asserts for speed. This could even be a #define if needed */ + clientStreamNode *next = this->node.next->data; + debug (87,3)("clientStreamCallback: Calling %p with cbdata %p from node %p\n",next->callback,next->data,this); + next->callback (next, http, rep, body_data, body_size); +} + +/* Call the previous node in the chain to read some data */ +void +clientStreamRead (clientStreamNode *this, clientHttpRequest *http, off_t readoff, size_t readlen, char *readbuf) +{ + /* no asserts for speed. This could even be a #define if needed */ + /* place the parameters on the 'stack' */ + clientStreamNode *prev = this->node.prev->data; + debug (87,3)("clientStreamRead: Calling %p with cbdata %p from node %p\n",prev->readfunc,prev->data,this); + this->readoff = readoff; + this->readlen = readlen; + this->readbuf = readbuf; + prev->readfunc (prev,http); +} + +/* Detach from the stream - only allowed for terminal members */ +void +clientStreamDetach (clientStreamNode *this, clientHttpRequest *http) +{ + clientStreamNode *prev = NULL; + clientStreamNode *temp = this; + if (this->node.prev) + prev = this->node.prev->data; + assert (this->node.next == NULL); + debug (87,3)("clientStreamDetach: Detaching node %p\n",this); + /* And clean up this node */ + /* ESI TODO: push refcount class through to head */ + cbdataReferenceDone (temp); + cbdataFree (this); + /* and tell the prev that the detach has occured */ + /* We do it in this order so that the detaching node is always at the end of the list */ + if (prev) { + debug (87,3)("clientStreamDetach: Calling %p with cbdata %p\n",prev->detach,prev->data); + prev->detach (prev,http); + } +} + +/* Abort the stream - detach every node in the pipeline. */ +void +clientStreamAbort (clientStreamNode *this, clientHttpRequest *http) +{ + dlink_list *list; + assert (this != NULL); + assert (http != NULL); + list = this->head; + debug (87,3)("clientStreamAbort: Aborting stream with tail %p\n",list->tail); + if (list->tail) { + clientStreamDetach (list->tail->data, http); + } +} + +/* Call the upstream node to find it's status + */ +clientStream_status_t +clientStreamStatus(clientStreamNode *this, clientHttpRequest *http) +{ + /* no asserts for speed. This could even be a #def if needed */ + clientStreamNode *prev = this->node.prev->data; + return prev->status (prev, http); +} /* Local function bodies */ void clientStreamFree (void *foo) { - clientStreamNode *node = foo; - debug (87, 9) ("Freeing clientStreamNode %p\n", node); - /* TODO: push refcount class through to head for cleanness*/ - if (node->data) { - cbdataFree (node->data); + clientStreamNode *this = foo; + debug (87, 3) ("Freeing clientStreamNode %p\n", this); + if (this->data) { + cbdataFree (this->data); } - if (node->next) { - clientStreamNode *temp = node->next; - cbdataReferenceDone (temp); - cbdataFree (node->next); + if (this->node.next || this->node.prev) { + dlinkDelete (&this->node, this->head); } }