After reading ready events from the kernel to user arrays using epoll_wait, events need to be processed. This includes the handling of timeout events.
tk_handle_expire_timers(); tk_pq_t tk_timer;//Priority queues are global variables void tk_handle_expire_timers(){ while(!tk_pq_is_empty(&tk_timer)){ // Update the current time tk_time_update(); tk_timer_t* timer_node = (tk_timer_t*)tk_pq_min(&tk_timer); // Release this node if deleted if(timer_node->deleted){//Delete nodes marked as deleted int rc = tk_pq_delmin(&tk_timer); free(timer_node); continue; } // The earliest queued node timeout time is longer than the current time (not timeout) to end timeout checking if(timer_node->key > tk_current_msec){//No timeout, return return; } // There is a time-out situation that has not been deleted. Call handler to process if(timer_node->handler){ timer_node->handler(timer_node->request);//handler marks the node as deleted } int rc = tk_pq_delmin(&tk_timer); free(timer_node); } } int tk_pq_is_empty(tk_pq_t *tk_pq){ // Fast judgment of nullity by nalloc value return (tk_pq->nalloc == 0) ? 1 : 0; } void *tk_pq_min(tk_pq_t *tk_pq){ // The minimum value of the priority queue returns directly to the first element (pointer) if (tk_pq_is_empty(tk_pq)) return (void *)(-1); return tk_pq->pq[1]; } typedef struct priority_queue{//Priority queue is used to store tk_timer_t time nodes. The smaller the time points, the higher the priority, and each time node contains request pointer. void **pq;//Priority Queue Node Pointer size_t nalloc;//Priority queues use the number of elements (the actual number of elements) to determine whether the queue is empty size_t size;//Number of elements available for priority queues tk_pq_comparator_pt comp;//Heap mode, minimum heap mode // A variable comp of type tk_pq_comparator_pt is defined. The comp must be given a function pointer, and the function header pointed by the function pointer is int (void *pi, void *pj). }tk_pq_t; typedef struct tk_timer{//Define the time structure size_t key; // The markup time should be the connection request deadline. The smaller the key, the faster the timeout. int deleted; // Is the tag deleted timer_handler_pt handler; // Define a variable using the timer_handler_pt type, timeout processing, specified in the tk_add_timer() function tk_http_request_t* request; // Point to the corresponding request request request to facilitate timeout deletion } tk_timer_t; // Function pointer, responsible for timeout processing, tk_add_timer specified processing function typedef int (*timer_handler_pt)(tk_http_request_t* request); //A new type of timer_handler_pt is defined, which points to a function whose input parameter is tk_http_request_t* request. //The return value type is int void tk_del_timer(tk_http_request_t* request) {//Deleted markup for nodes tk_time_update(); tk_timer_t* timer_node = request->timer; // Lazy deletion // Marked as deleted, deleted when find_timer and handle_expire_timers check queues timer_node->deleted = 1; }
Priority queue save time node. Time nodes include request deadlines, whether deleted, timeout handlers, and request structures. As long as the priority queue is not empty, the first queue pair node is continuously acquired to check the inert tag, and the marked memory is released; if the time is timed out, the timeout processing function is called to place the inert tag of the structure at 1, and the node is placed at the end of the priority queue. Do nothing without overtime.
- Check if the inert label of the node is 1, then release the memory space of the node and return.
- Check whether the node has timed out or not. If it does not, it does nothing and returns.
- If the timeout occurs, the node is marked inertly and the timeout node is moved to the end of the queue.
int tk_pq_delmin(tk_pq_t *tk_pq){//After the inert tag is labeled, move the node to the end of the queue if(tk_pq_is_empty(tk_pq)) return 0; exch(tk_pq, 1, tk_pq->nalloc);//Exchange the last node with the header node for easy deletion --tk_pq->nalloc;//Queue number minus one sink(tk_pq, 1);//Underflow node if((tk_pq->nalloc > 0) && (tk_pq->nalloc <= (tk_pq->size - 1)/4)){//If the number of actual elements is less than one quarter of the number of available elements if(resize(tk_pq, tk_pq->size / 2) < 0)//Halve the priority queue memory return -1; } return 0; } void exch(tk_pq_t *tk_pq, size_t i, size_t j){//Switching Node Operation void *tmp = tk_pq->pq[i]; tk_pq->pq[i] = tk_pq->pq[j]; tk_pq->pq[j] = tmp; } int sink(tk_pq_t *tk_pq, size_t k){//Underflow operation size_t j; size_t nalloc = tk_pq->nalloc; while((k << 1) <= nalloc){ j = k << 1;//Multiply 2 to get the child's node position if((j < nalloc) && (tk_pq->comp(tk_pq->pq[j+1], tk_pq->pq[j])))//The smaller of the left and right children's nodes at k node j++; if(!tk_pq->comp(tk_pq->pq[j], tk_pq->pq[k]))//If the k node is smaller than the child node, the underflow ends break; exch(tk_pq, j, k);//k node time is longer than child node, exchange k = j; } return k; }
- Exchange the node with the tail node
- The tail node is overflowed, which compares the tail node with the smaller one in the two child nodes, and swaps with the smaller one, or terminates the swap.