Hi Folks: I think I see the heart of my problem. Everything appears to work correctly when you establish an incoming TCP connection and release it and the related Libuv handles--the uv_tcp_t connection handle and the uv_poll_t poll handle. (I revised the release code to do things the right way.)
The comments about coalescing of uv_async_send() calls in the documentation is somewhat misleading. They should indicate that call with the same handle are synchronous. Also, I suspect that uv_async_send() is not reentrant. When you attempt another incoming connection the following things occur. Notice in 2.2, below that uv_start_loop() executes without being called. This doesn't make sense to me--at least on the surface. Can you think of a possible reason this occurs ? 1) The connection is successfully established--with uv_accept(), and the socket descriptor fd is the same as was used in the previous connection, in the main() process. (Occurs with uv_loop_t Session_Loop.) conn_handle = (uv_tcp_t *) malloc(sizeof(uv_tcp_t)); if(conn_handle == NULL) { fprintf(stderr, "MAIN: No Connect Handle Memory\n"); abort(); } uv_tcp_init(&Connect_Loop, conn_handle); if(uv_accept(listen_handle, (uv_stream_t *) conn_handle) == 0) { uv_os_fd_t fd; uv_fileno((const uv_handle_t*) conn_handle, &fd); } 2.1) A poll handle is successfully allocated in the IO_Trigger_Task() thread. (No loop involved.) poll_handle = (uv_poll_t *) malloc(sizeof(uv_poll_t)); if(poll_handle == NULL) { fprintf(stderr, "IO_TRIGGER_TASK: No Poll HAndle Memory\n"); abort(); } uv_poll_init(&Poll_Loop, poll_handle, pm->info); if((r = uv_poll_start(poll_handle, UV_READABLE, poll_callback)) < 0) { fprintf(stderr, "IO_TRIGGER_TASK: Polling Initiation Error %d: %s\n", r, uv_err_name(r)); abort(); } 2.2) uv_poll_start() is invoked via a call. uv_poll_init(&Poll_Loop, poll_handle, pm->info); if((r = uv_poll_start(poll_handle, UV_READABLE, poll_callback)) < 0) { fprintf(stderr, "IO_TRIGGER_TASK: Polling Initiation Error %d: %s\n", r, uv_err_name(r)); abort(); } 2.3) uv_poll_start() executes again without being called ! This is what you see in GDB which is very strange since I know there is only one instance of the IO_Trigger_Task() running and it was not called a second time because the line before line 212 didn't execute a second time. Breakpoint 1, IO_Trigger_Task (arg=0x0) at network_io.c:212 212 if((r = uv_poll_start(poll_handle, UV_READABLE, poll_callback)) < 0) (gdb) bt #0 IO_Trigger_Task (arg=0x0) at network_io.c:212 #1 0x0000000000413017 in uv__thread_start (arg=<optimized out>) at src/unix/thread.c:49 #2 0x00007ffff7bc26aa in start_thread (arg=0x7ffff64e6700) at pthread_create.c:333 #3 0x00007ffff75efeed in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 (gdb) s uv_poll_start (handle=0x7fffec0008c0, pevents=1, poll_cb=0x404599 <poll_callback>) at src/unix/poll.c:89 89 assert((pevents & ~(UV_READABLE | UV_WRITABLE)) == 0); (gdb) s 86 int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { (gdb) bt #0 uv_poll_start (handle=0x7fffec0008c0, pevents=1, poll_cb=0x404599 <poll_callback>) at src/unix/poll.c:86 #1 0x00000000004048bb in IO_Trigger_Task (arg=0x0) at network_io.c:212 #2 0x0000000000413017 in uv__thread_start (arg=<optimized out>) at src/unix/thread.c:49 #3 0x00007ffff7bc26aa in start_thread (arg=0x7ffff64e6700) at pthread_create.c:333 #4 0x00007ffff75efeed in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 (gdb) This is the relevant code of the IO_Trigger_Task() thread. for(;;) { // // Wait for a message from the main() process. // pm = WAIT_IO_Trigger_MSG(); poll_handle = (uv_poll_t *) malloc(sizeof(uv_poll_t)); if(poll_handle == NULL) { fprintf(stderr, "IO_TRIGGER_TASK: No Poll HAndle Memory\n"); abort(); } uv_poll_init(&Poll_Loop, poll_handle, pm->info); // // Start epoll() monitoring of the connection. // if((r = uv_poll_start(poll_handle, UV_READABLE, poll_callback)) < 0) { fprintf(stderr, "IO_TRIGGER_TASK: Polling Initiation Error %d: %s\n", r, uv_err_name(r)); abort(); } MSG_FREE(pm); } 2.4) The polling callback function never executes. NOTE: The polling loop, Poll_Loop, of type uv_loop_t is already running and was started, in the IO_Task() thread, at startup time as follows. uv_loop_init(&Poll_Loop); for(;;) { r = uv_run(&Poll_Loop, UV_RUN_DEFAULT); if(r) fprintf(stderr, "IO_TASK: Run Error %d\n", r); } This is the sequence of operations used to free the first connection. 1) Release the uv_poll_t poll handle in the IO_Task() from the Protocol_Task() // // This causes immediate socket disconnection when it is closed. // spec.l_onoff = TRUE; spec.l_linger = 0; setsockopt(cdesc->fd, SOL_SOCKET, SO_LINGER, &spec, sizeof(spec) ); // // poll_release_proxy() executes in the IO_Task() and releases poll handle. // uv_async_init(&Poll_Loop, &cdesc->async_handle, poll_release_proxy); cdesc->async_handle.data = (void *) cdesc; uv_async_send(&cdesc->async_handle); 1.1) Wait for poll handle to be freed and then release the async. handle. uv_close((uv_handle_t *) &cdesc->async_handle, NULL); 2) Release the uv_tcp_t connect handle in the main() process from the Protocol_Task() uv_async_init(&Connect_Loop, &cdesc->async_handle, conn_release_proxy); cdesc->async_handle.data = (void *) cdesc; uv_async_send(&cdesc->async_handle); 2.1) Wait for the connect handle to be free and then release the async. handle. uv_close((uv_handle_t *) &cdesc->async_handle, NULL); 3) Do protocol bookkeeping. This is the code of the proxy callback routines and the close callback routine. // // This routine executes asynchronously and frees a handle. // It is invoked in the follows two cases. // // * When the main process invokes poll_release_proxy() // // * When the IO_Task invokes conn_release_proxy(). // ROUTINE void close_callback(uv_handle_t *handle) { SDU *msg; CONN_DESC *cdesc = (CONN_DESC *) handle->data; free(handle); ENTER_MUTEX(&Service_Q_Mutex); // // Set the state correctly and validate the state. // switch(cdesc->release_state) { case RS_POLL_HANDLE_PEND: cdesc->release_state = RS_POLL_HANDLE_FREE; break; case RS_CONN_HANDLE_PEND: cdesc->release_state = RS_CONN_HANDLE_FREE; break; default: fprintf(stderr, "CLOSE_PROXY - BUG: Invalid Release State = %d\n", cdesc->release_state); abort(); } EXIT_MUTEX(&Service_Q_Mutex); // // Send a notification message to the Protocol_Task. // msg = MSG_ALLOC(0, FALSE); msg->class = C_NOTIFY; msg->type = T_HANDLE_FREE; msg->info = 0; SEND_SDU(cdesc, msg); return; } // // This routine is invoked by the IO_Task() in response to an async. wakeup by the Protocol_Task() // during TCP connection termination. It release the resources used by the Poll_Loop. // ROUTINE void poll_release_proxy(uv_async_t *async_handle) { CONN_DESC *cdesc = (CONN_DESC *) async_handle->data; // // Stop polling operations before closing the handle. // uv_poll_stop(cdesc->poll_handle); cdesc->poll_handle->data = (void *) cdesc; uv_close((uv_handle_t *) cdesc->poll_handle, close_callback); return; } // // This routine is invoked by the main process in response to an async. wakeup by the Protocol_Task() // during TCP connection termination. It release the resources used by the Connect_Loop. // ROUTINE void conn_release_proxy(uv_async_t *async_handle) { CONN_DESC *cdesc = (CONN_DESC *) async_handle->data; cdesc->conn_handle->data = (void *) cdesc; uv_close((uv_handle_t *) cdesc->conn_handle, close_callback); return; } Best Regards, Paul R. On Sunday, December 20, 2020 at 1:23:52 PM UTC-8 pa...@rcom-software.com wrote: > Hi Folks: > > With limited testing the problem ceases to happen if you force uv_run() in > the IO_Task() > enough to finish its pending work. As an interim measure I do this by > making the > Protocol_Task() to yield the CPU after calling uv_stop() and > up_poll_stop() as follows in > the RELEASE_CONNECTION() routine. This appears to cause IO_Task() to be > scheduled and run > but I am not all all convinced this is a reliable technique. > > // > // Deactive and release the poll handle. > // You have stop the Poll_Loop to deactivate and deallocate the > poll handle. > // > uv_stop(&Poll_Loop); > > uv_poll_stop(cdesc->poll_handle); > #ifdef CLOSE_KLUDGE2 > // > // Try to let run() in the IO_Task() finish pending work by > yielding the CPU. > // > for(k = 0; k < 10; k++) pthread_yield(); > #endif // CLOSE_KLUDGE2 > uv_close((uv_handle_t *) cdesc->poll_handle, close_callback); > > > Best Regards, > > Paul R. > > > On Sunday, December 20, 2020 at 10:13:34 AM UTC-8 pa...@rcom-software.com > wrote: > >> Hi Folks: >> >> I made some progress on the problem but it is definitely not solved. The >> updated code >> and more diagnostic code are included in the message. >> >> NOTE: I am using the GIT HUB distribution from the following link on >> Ubuntu Linux version 15.04. >> >> https://github.com/nikhilm/uvbook >> >> The Libuv software package looks like version 1.3.0. >> >> I have had to take extraordinary measures to make connection release >> reliable. >> The relevant code is included at near end of this message and the >> extraordinary >> measures are in the CLOSE_KLUDGE sections. The difficulty arises because >> the >> Libuv loops are not used in the Protocol_Task() yet it must affect >> operations >> on those loops to release handles. It would be nice if Libuv included an >> API >> for releasing handles reliably which could be called from any task. >> >> Connection release still fails about 15% of the time in which case a >> crash occurs >> and the following diagnostic is displayed. >> >> pexd: src/unix/core.c:210: uv__finish_close: Assertion >> `!(handle->flags & UV_CLOSED)' failed. >> >> More diagnostic information follows. Do you know what causes this crash ? >> >> Best Regards, >> >> Paul Romero >> >> >> Crash Diagnostics >> ----------------- >> The crash occurs when run() is executing in the IO_Task() in network_io.c >> according to the following >> GBD stack trace. >> >> #0 0x00007f281754c267 in __GI_raise (sig=sig@entry=6) at >> ../sysdeps/unix/sysv/linux/raise.c:55 >> #1 0x00007f281754deca in __GI_abort () at abort.c:89 >> #2 0x00007f281754503d in __assert_fail_base (fmt=0x7f28176a7028 >> "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", >> assertion=assertion@entry=0x41e093 "!(handle->flags & UV_CLOSED)", >> file=file@entry=0x41e068 "src/unix/core.c", >> line=line@entry=210, function=function@entry=0x41e2b0 >> <__PRETTY_FUNCTION__.9522> "uv__finish_close") at assert.c:92 >> #3 0x00007f28175450f2 in __GI___assert_fail >> (assertion=assertion@entry=0x41e093 "!(handle->flags & UV_CLOSED)", >> file=file@entry=0x41e068 "src/unix/core.c", line=line@entry=210, >> function=function@entry=0x41e2b0 <__PRETTY_FUNCTION__.9522> >> "uv__finish_close") at assert.c:101 >> #4 0x000000000040c967 in uv__finish_close (handle=<optimized out>) at >> src/unix/core.c:210 >> #5 uv__run_closing_handles (loop=0x638080 <Poll_Loop>) at >> src/unix/core.c:259 >> #6 uv_run (loop=0x638080 <Poll_Loop>, mode=UV_RUN_DEFAULT) at >> src/unix/core.c:326 >> #7 0x0000000000404962 in IO_Task (arg=0x0) at network_io.c:226 >> #8 0x0000000000412ad7 in uv__thread_start (arg=<optimized out>) at >> src/unix/thread.c:49 >> #9 0x00007f2817bf06aa in start_thread (arg=0x7f2816d15700) at >> pthread_create.c:333 >> #10 0x00007f281761deed in clone () at >> ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 >> >> However, the GDB thread information indicates that RELEASE_CONNECTION(), >> in protocol.c, is executing >> in the Protocol_Task() when the crash occurs. >> >> Id Target Id Frame >> 6 Thread 0x7f2817516700 (LWP 3424) syscall () at >> ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38 >> 5 Thread 0x7f2816514700 (LWP 3426) pthread_cond_wait@@GLIBC_2.3.2 () >> at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185 >> 4 Thread 0x7f2818003700 (LWP 3423) syscall () at >> ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38 >> 3 Thread 0x7f2815512700 (LWP 3428) pthread_cond_wait@@GLIBC_2.3.2 () >> at ../sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185 >> 2 Thread 0x7f2815d13700 (LWP 3427) 0x0000000000404500 in >> RELEASE_CONNECTION (cdesc=0x6384c0 <Conn_Desc_Table>) >> at protocol.c:357 >> * 1 Thread 0x7f2816d15700 (LWP 3425) 0x00007f281754c267 in __GI_raise >> (sig=sig@entry=6) >> at ../sysdeps/unix/sysv/linux/raise.c:55 >> >> Line 357 of protocol.c is as follows. >> >> while(WaitClose[cdesc->index]); >> >> Wait_Close[] is only modified in two cases and only in the >> Protocol_Task(). >> >> 1) It is initialized to a handle address in RELEASE_CONNECTION() in the >> Protocol_Task(). >> 2) It is cleared in the uv_close() callback routine close_callback(). >> >> >> Code >> ----- >> >> #define CLOSE_KLUDGE >> >> extern uv_loop_t Poll_Loop; >> extern uv_loop_t Connect_Loop; >> >> #ifdef CLOSE_KLUDGE >> uv_handle_t *WaitClose[MAX_CONN_DESC] = { NULL }; >> #endif // CLOSE_KLUDGE >> >> ROUTINE void close_callback(uv_handle_t *handle) >> { >> int k; >> >> free(handle); >> >> #ifdef CLOSE_KLUDGE >> // >> // Determine if the handle is being closed. >> // >> for(k = 0; k < MAX_CONN_DESC; k++) >> { >> if(WaitClose[k] == handle) >> { >> // >> // Closure is complete. >> // >> WaitClose[k] = NULL; >> break; >> } >> } >> #endif // CLOSE_KLUDGE >> >> return; >> } >> >> ROUTINE void RELEASE_CONNECTION(CONN_DESC *cdesc) >> { >> uv_async_t as_handle; >> struct linger spec; >> >> if(N_Sockets > 0) >> N_Sockets--; >> // >> // This causes immediate socket disconnection when it is closed. >> // >> spec.l_onoff = TRUE; >> spec.l_linger = 0; >> setsockopt(cdesc->fd, SOL_SOCKET, SO_LINGER, &spec, sizeof(spec) ); >> >> if(cdesc->poll_handle) >> { >> #ifdef CLOSE_KLUDGE >> WaitClose[cdesc->index] = (uv_handle_t *) cdesc->poll_handle; >> #endif // CLOSE_KLUDGE >> // >> // Deactive and release the poll handle. >> // You have stop the Poll_Loop to deactivate and deallocate the >> poll handle. >> // >> uv_stop(&Poll_Loop); >> >> uv_poll_stop(cdesc->poll_handle); >> uv_close((uv_handle_t *) cdesc->poll_handle, close_callback); >> // >> // Wake up the Poll_Loop in the IO_Task() >> // >> uv_async_init(&Poll_Loop, &as_handle, NULL); >> uv_async_send(&as_handle); >> uv_close((uv_handle_t *) &as_handle, NULL); >> #ifdef CLOSE_KLUDGE >> // >> // Wait for the handle to be closed and deallocated. >> // >> while(WaitClose[cdesc->index]); >> #endif // CLOSE_KLUDGE >> } >> >> if(cdesc->conn_handle) >> { >> #ifdef CLOSE_KLUDGE >> WaitClose[cdesc->index] = (uv_handle_t *) cdesc->conn_handle; >> #endif // CLOSE_KLUDGE >> // >> // Close and deallocate the connect handle in order to close the >> socket connecction. >> // You have to wake up the Connect_Loop for the close_callback() >> // routine to execute. >> // >> uv_close((uv_handle_t *) cdesc->conn_handle, close_callback); >> // >> // Wake up the Connect_Loop in the main() process. >> // >> uv_async_init(&Connect_Loop, &as_handle, NULL); >> uv_async_send(&as_handle); >> uv_close((uv_handle_t *) &as_handle, NULL); >> #ifdef CLOSE_KLUDGE >> // >> // Wait for the handle and socket connection to be release and >> closed. >> // >> while(WaitClose[cdesc->index]); >> #endif // CLOSE_KLUDGE >> } >> >> >> ENTER_MUTEX(&Service_Q_Mutex); >> DELETE_CONN(cdesc); >> cdesc->fd = -1; >> flush_msg(&cdesc->task_input_q); >> EXIT_MUTEX(&Service_Q_Mutex); >> >> return; >> } >> >> On Sunday, December 20, 2020 at 3:47:07 AM UTC-8 pa...@rcom-software.com >> wrote: >> >>> Hi Folks: >>> >>> My Libuv based Server performs all its functions correctly except for >>> TCP connection termination. >>> >>> Each TCP connection has uv_tcp_t connection handle and uv_poll_t handle >>> whose allocation >>> and operation are explained below. When the Protocol_Task() thread >>> needs to terminate >>> a connection, it must stop polling, terminate the TCP socket connection, >>> and deallocate >>> the handles. >>> >>> NOTE: I am using the GIT HUB distribution from the following link on >>> Ubuntu Linux version 15.04. >>> >>> https://github.com/nikhilm/uvbook >>> >>> I have tried the following two approaches. >>> >>> 1) Just use uv_poll_stop() to terminate polling and uv_close() to >>> terminate the TCP connection. >>> >>> 2) Use uv_poll_stop() to terminate polling and the using uv_queue_work() >>> and uv_async_send() to >>> wake up the Connect_Loop, in the main() process described below, so >>> it can terminate the >>> TCP connection, by proxy, with uv_close(). >>> >>> In both cases the following problem occurs. The callback routine >>> supplied to uv_close() >>> does not execute until another incoming TCP connection occurs, and in >>> most cases, >>> the Pool_Loop, in the IO_Task() described below, stops invoking it >>> callback routine-- >>> poll_callback(). In case 2, a crash almost alway ensues. (I probably am >>> not using >>> uv_async_send() correctly.) >>> >>> Do I have a fundamental misunderstanding of how Libuv works or am I >>> doing something wrong ? >>> >>> Also, I strongly suspect using Linux recv() to read data is not optimal >>> when epoll() is >>> being used. My understanding is that there is a way to pass buffers to >>> epoll() such that >>> data will automatically be inserted in them when a UV_READABLE event >>> occurs. Do you have >>> any advice about this ? >>> >>> An overview of my Server and the relevant code follow. >>> >>> Best Regards, >>> >>> Paul Romero >>> >>> Multi-Connection TCP Server Functional Architecture Overview >>> >>> ----------------------------------------------------------------------------------------- >>> There is a connection descriptor for each incoming TCP connection which >>> contains all data >>> needed to manage the connection and perform the relevant functions. >>> >>> When the main() process detects an incoming TCP connection, it sends a >>> notification message to the >>> IO_Trigger_Task(). The IO_Trigger_Task() then sets up epoll() monitoring >>> of incoming TCP data >>> for that connection. >>> >>> Subsequently, the IO_Task() invokes poll_callback() when incoming data >>> is available, reads a chunk >>> of data, and sends a protocol message to the Protocol_Task() when a >>> complete protocol message is >>> recognized. >>> >>> The Timer_Task() sends an expiration notification message to the >>> Protocol_Task() when a protocol >>> timer expires. >>> >>> The Protocol_Task() send messages to the Send_Op_Task() for transmission >>> across the network. >>> It spawns a DB Operation Task to perform slow data base operations and >>> the DB Operation Task >>> notifies the Protocol_Task() when the operation is complete and then >>> terminates. >>> >>> Loops of type uv_loop_t >>> ----------------------- >>> * Connect_Loop >>> * Pool_Loop >>> * Timer_Loop` >>> >>> Tasks: All Libuv thread tasks run concurrently and are launched by >>> main() at startup time. >>> >>> ------------------------------------------------------------------------------------------ >>> * main(): A Linux process that runs the Connect_Loop to detect incoming >>> TCP connections. >>> The make_incoming_connection() callback routine accepts incoming >>> connections and >>> allocates a uv_tcp_t handle on a per connection basis >>> >>> * IO_Trigger_Task(): A Libuv thread that sets up epoll() plumbing for >>> the IO_Task() >>> when an incoming TCP connection occurs. It allocates a uv_poll_t >>> handle, on a per >>> connection basis, and calls uv_poll_start() to initiate epoll() >>> operation with the >>> Poll_Loop in the IO_Task(). It configures the handle to detect >>> UV_READABLE events and >>> handles them with the poll_callback() routine. However, it does not >>> run the Poll_Loop. >>> (Basically, this task just sets up plumbing.) >>> >>> * IO_Task(): A Libuv thread that runs the Poll_Loop to handle incoming >>> TCP data, on a per >>> connection basis. The poll_callback() routine executes and uses normal >>> Linux recv() to read >>> chunks of data, in non-blocking mode, when a UV_READABLE event occurs. >>> >>> * Timer_Task(): A Libuv thread that runs the Time_Loop to handle ticks, >>> and whose main >>> function is to detect protocol timer expiration. The tick duration is >>> configured with >>> is configured with uv_timer_init() and uv_timer_start(), and ticks are >>> handled by the >>> timer_callback() routine. >>> >>> * Protocol_Task(): A Libuv thread that handles protocol messages sent to >>> it by the following tasks >>> on per connection basis: IO_Task(), Timer_Task(), DB Operation Tasks. >>> DB Operation Libuv thread tasks >>> are spawned by the Protocol_Task() to perform slow database operations >>> and send a notification message >>> to the Protocol_Task() upon completion of the operation. >>> >>> * Send_Op_Task(): A Libuv thread that transmits all network bound >>> messages with normal >>> Linux send() on a per connection basis. >>> >>> >>> Approach 1 Code >>> ------------- >>> ROUTINE void close_callback(uv_handle_t *handle) >>> { >>> >>> free(handle); >>> return; >>> } >>> >>> ROUTINE void RELEASE_CONNECTION(CONN_DESC *cdesc) >>> { >>> struct linger spec; >>> int r; >>> >>> if(N_Sockets > 0) >>> N_Sockets--; >>> >>> if(cdesc->poll_handle) >>> { >>> uv_poll_stop(cdesc->poll_handle); >>> free((void *) cdesc->poll_handle); >>> } >>> >>> if(cdesc->conn_handle) >>> { >>> struct linger spec; >>> >>> spec.l_onoff = TRUE; >>> spec.l_linger = 0; >>> setsockopt(cdesc->fd, SOL_SOCKET, SO_LINGER, &spec, sizeof(spec) >>> ); >>> >>> uv_close((uv_handle_t *) cdesc->conn_handle, close_callback); >>> } >>> >>> ENTER_MUTEX(&Service_Q_Mutex); >>> DELETE_CONN(cdesc); >>> cdesc->fd = -1; >>> flush_msg(&cdesc->task_input_q); >>> EXIT_MUTEX(&Service_Q_Mutex); >>> >>> return; >>> } >>> >>> Approach 2 Code >>> ----------------- >>> ROUTINE void close_callback(uv_handle_t *handle) >>> { >>> free(handle); >>> return; >>> } >>> >>> typedef struct close_template { >>> uv_handle_t *handle; >>> void (*callback) (uv_handle_t *); >>> } CLOSE_TEMPLATE; >>> >>> ROUTINE void close_proxy(uv_work_t *data) >>> { >>> CLOSE_TEMPLATE *cparam = (CLOSE_TEMPLATE *) cparam; >>> >>> uv_close(cparam->handle, cparam->callback); >>> return; >>> } >>> >>> >>> extern uv_loop_t Connect_Loop; >>> static CLOSE_TEMPLATE close_data; >>> >>> ROUTINE void RELEASE_CONNECTION(CONN_DESC *cdesc) >>> { >>> uv_work_t wreq; >>> uv_async_t as_handle; >>> struct linger spec; >>> >>> if(N_Sockets > 0) >>> N_Sockets--; >>> >>> // >>> // Stop this. TBD: Might need to do this via proxy in the IO_Task() >>> Poll_Loop. >>> // >>> uv_poll_stop(cdesc->poll_handle); >>> >>> uv_async_init(&Connect_Loop, &as_handle, NULL); >>> >>> close_data.handle = (uv_handle_t *) cdesc->conn_handle; >>> close_data.callback = close_callback; >>> // >>> // Call uv_close() in the close_proxy() >>> // >>> wreq.data = (void *) &close_data; >>> uv_queue_work(&Connect_Loop, &wreq, close_proxy, NULL); >>> >>> spec.l_onoff = TRUE; >>> spec.l_linger = 0; >>> setsockopt(cdesc->fd, SOL_SOCKET, SO_LINGER, &spec, sizeof(spec) ); >>> >>> uv_async_send(&as_handle); >>> uv_close((uv_handle_t *) &as_handle, NULL); >>> >>> free(cdesc->poll_handle); >>> >>> ENTER_MUTEX(&Service_Q_Mutex); >>> DELETE_CONN(cdesc); >>> cdesc->fd = -1; >>> flush_msg(&cdesc->task_input_q); >>> EXIT_MUTEX(&Service_Q_Mutex); >>> >>> return; >>> } >>> >> -- You received this message because you are subscribed to the Google Groups "libuv" group. To unsubscribe from this group and stop receiving emails from it, send an email to libuv+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/libuv/0eb70e68-57da-4f6a-aac4-a9c6b6d30334n%40googlegroups.com.