So you called uv_async_init after destroying the object with uv_close? That sounds like a classic-use-after-free client bug, not a libuv issue
> coalescing of uv_async_send Again, it's not a queue, you're responsible for getting that from another library, not a libuv issue On Wed, Dec 23, 2020 at 7:53 PM Paul Romero <pa...@rcom-software.com> wrote: > Hi Folks: > > After taking several wrong paths I isolated the problem. > > It occurs when uv_close() is called as follows to close the async. handle > used to initiate an async. operation with uv_async_send(). > > uv_close((uv_handle_t *) &cdesc->async_handle, NULL); > > Now if another async. operation is initiated with the same handle, > after calling uv_async_init(), it triggers uv__finish_close() to be > invoked in both the main() process and IO_Task() thread concurrently. > Then the following familiar crash occurs at the assert(). > > #0 0x00007f256fdf0267 in __GI_raise (sig=sig@entry=6) at > ../sysdeps/unix/sysv/linux/raise.c:55 > #1 0x00007f256fdf1eca in __GI_abort () at abort.c:89 > #2 0x00007f256fde903d in __assert_fail_base (fmt=0x7f256ff4b028 > "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", > assertion=assertion@entry=0x4261e0 "handle->flags & > UV_HANDLE_CLOSING", > file=file@entry=0x426115 "src/unix/core.c", line=line@entry=254, > function=function@entry=0x426480 <__PRETTY_FUNCTION__.10077> > "uv__finish_close") at assert.c:92 > #3 0x00007f256fde90f2 in __GI___assert_fail > (assertion=assertion@entry=0x4261e0 > "handle->flags & UV_HANDLE_CLOSING", > file=file@entry=0x426115 "src/unix/core.c", line=line@entry=254, > function=function@entry=0x426480 <__PRETTY_FUNCTION__.10077> > "uv__finish_close") at assert.c:101 > #4 0x0000000000410149 in uv__finish_close (handle=<optimized out>) at > src/unix/core.c:254 > #5 uv__run_closing_handles (loop=0x839880 <Connect_Loop>) at > src/unix/core.c:317 > #6 uv_run (loop=0x839880 <Connect_Loop>, mode=UV_RUN_DEFAULT) at > src/unix/core.c:395 > #7 0x0000000000404b5f in main () at main.c:188 > > There are two obvious possible causes. > > 1) The async. operation is not really complete. > 2) A bug in Libuv. > > This problem stops occurring if you don't call uv_close(). This is not a > problem in my > application, since the cdesc->async->handle is not dynamically allocated, > but I am not sure > if the same is true for the Libuv software. (My application is Server that > handles large numbers > of concurrent TCP connections and is a Linux Daemon.) > > The Libuv documentation states that a uv_async_t handle remains activated > until uv_close() > is called. Are there negative Libuv software consequences if an async. > handle is left > activated ? My guess is no. > > If this is a Libuv problem, perhaps dealing with it may be better to deal > with it at the > documentation rather than code level. > > It appears that caolescing of uv_async_send() calls is still an issue. > > Best Regards, > > Paul R. > > PS: The reason I am considering using Libuv is that it provides a > convenient epoll() wrapper > and epoll() allows very quick data reception without user space file > descriptor polling or > the like. > > On 12/22/2020 10:58 AM, Jameson Nash wrote: > > Hi Paul, > > This list isn't really likely to give you free consulting work. My brief > skimming suggested the following tips as possibly relevant: > - libuv is an async library first-and-foremost. You must adapt your > mental model to use callback-managed state machines (inverted control > flow). Learning Javascript can be helpful here for the mental model aspects. > - libuv is not thread-safe. There's some specific operations defined for > concurrent program integration, but you're responsible for the details. > - uv_async_t is not a queue, it is a condition object. You are > responsible for providing your own queue. > > -jameson > > On Tue, Dec 22, 2020 at 11:56 AM pa...@rcom-software.com < > pa...@rcom-software.com> wrote: > >> Hi Folks: >> >> This is what happens at a lower level. Do you have insight about the >> cause ? >> The relevant code segments and the GDB stack traces follow below. >> >> The main() process and IO_Task() are executing concurrently. >> >> NOTE: The IO_Trigger_Task() has been eliminated and the uv_start_poll() >> call is invoked in the IO_Task() >> by a proxy routine and an async. wakeup in the main() process. The wakeup >> is done in the make_incoming_connection() >> callback routine by the following code segment. >> >> poll_handle->data = (void *) cdesc; >> // >> // Start epoll() monitoring of the connection. The poll_start_proxy() >> // routine executes in the IO_Task(). >> // >> uv_async_init(&Poll_Loop, &cdesc->async_handle, poll_start_proxy); >> cdesc->async_handle.data = (void *) cdesc; >> uv_async_send(&cdesc->async_handle); >> >> >> * The main() process is executing uv__finish_close() which is invoked via >> uv_run() and uv__run_closing_handles(). >> It crashes in the call to uv__finish_close() >> >> This is the code from unix/core.c: Line 210 is the first assert() >> statement. >> >> ------------------------------------------------------------------------------ >> static void uv__finish_close(uv_handle_t* handle) { >> /* Note: while the handle is in the UV_CLOSING state now, it's still >> possible >> * for it to be active in the sense that uv__is_active() returns true. >> * A good example is when the user calls uv_shutdown(), immediately >> followed >> * by uv_close(). The handle is considered active at this point because >> the >> * completion of the shutdown req is still pending. >> */ >> #ifndef SUPRESS >> assert(handle->flags & UV_CLOSING); >> assert(!(handle->flags & UV_CLOSED)); >> #endif /* SUPRESS */ >> handle->flags |= UV_CLOSED; >> >> * The IO_Task() thread is executing uv_poll_start() which is called from >> start_poll_proxy() in >> response to an async. request from the main() process. >> (uv__async_event() makes the call to >> start_poll_proxy()). >> >> This is the code that executes in the IO_Task() from network_io.c >> ----------------------------------------------------------------- >> // >> // Executes in IO_Task >> // >> ROUTINE void poll_start_proxy(uv_async_t *async_handle) >> { >> int r; >> >> CONN_DESC *cdesc = (CONN_DESC *) async_handle->data; >> >> uv_poll_init(&Poll_Loop, cdesc->poll_handle, cdesc->fd); >> if((r = uv_poll_start(cdesc->poll_handle, UV_READABLE, >> poll_callback)) < 0) >> { >> fprintf(stderr, "PROXY: IO_TASK - Polling Initiation Error %d: >> %s\n", r, uv_err_name(r)); >> abort(); >> } >> uv_close( (uv_handle_t *) async_handle, NULL); >> >> return; >> } >> >> This is the code from unix/poll.c: Line 92 is the call to uv__poll_stop() >> -------------------------------------------------------------------------- >> int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { >> int events; >> >> assert((pevents & ~(UV_READABLE | UV_WRITABLE)) == 0); >> assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); >> >> uv__poll_stop(handle); >> >> if (pevents == 0) >> return 0; >> >> events = 0; >> if (pevents & UV_READABLE) >> events |= UV__POLLIN; >> if (pevents & UV_WRITABLE) >> events |= UV__POLLOUT; >> >> uv__io_start(handle->loop, &handle->io_watcher, events); >> uv__handle_start(handle); >> handle->poll_cb = poll_cb; >> >> return 0; >> } >> >> >> >> main() Stack Trace >> -------------------- >> #0 0x00007ffff751e267 in __GI_raise (sig=sig@entry=6) at >> ../sysdeps/unix/sysv/linux/raise.c:55 >> #1 0x00007ffff751feca in __GI_abort () at abort.c:89 >> #2 0x00007ffff751703d in __assert_fail_base (fmt=0x7ffff7679028 >> "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", >> assertion=assertion@entry=0x41e678 "handle->flags & UV_CLOSING", >> file=file@entry=0x41e668 "src/unix/core.c", >> line=line@entry=210, function=function@entry=0x41e8b0 >> <__PRETTY_FUNCTION__.9522> "uv__finish_close") >> at assert.c:92 >> #3 0x00007ffff75170f2 in __GI___assert_fail >> (assertion=assertion@entry=0x41e678 >> "handle->flags & UV_CLOSING", >> file=file@entry=0x41e668 "src/unix/core.c", line=line@entry=210, >> function=function@entry=0x41e8b0 <__PRETTY_FUNCTION__.9522> >> "uv__finish_close") at assert.c:101 >> #4 0x000000000040cd60 in uv__finish_close (handle=<optimized out>) at >> src/unix/core.c:210 >> #5 uv__run_closing_handles (loop=0x830680 <Connect_Loop>) at >> src/unix/core.c:261 >> #6 uv_run (loop=0x830680 <Connect_Loop>, mode=UV_RUN_DEFAULT) at >> src/unix/core.c:328 >> #7 0x0000000000403e0f in main () at main.c:184 >> >> IO_Task() Stack Trace >> ----------------------- >> #0 uv__poll_stop (handle=0x831bc0) at src/unix/poll.c:73 >> #1 0x000000000040e93f in uv_poll_start (handle=0x831bc0, pevents=1, >> poll_cb=0x404576 <poll_callback>) >> at src/unix/poll.c:92 >> #2 0x00000000004047d4 in poll_start_proxy (async_handle=0x639468 >> <Conn_Desc_Table+40>) at network_io.c:146 >> #3 0x000000000040c1dd in uv__async_event (loop=0x639000 <Poll_Loop>, >> w=<optimized out>, nevents=<optimized out>) >> at src/unix/async.c:89 >> #4 0x000000000040c29e in uv__async_io (loop=0x639000 <Poll_Loop>, >> w=0x6391c8 <Poll_Loop+456>, >> events=<optimized out>) at src/unix/async.c:160 >> #5 0x000000000041590d in uv__io_poll (loop=loop@entry=0x639000 >> <Poll_Loop>, timeout=-1) at src/unix/linux-core.c:319 >> #6 0x000000000040cac7 in uv_run (loop=0x639000 <Poll_Loop>, >> mode=UV_RUN_DEFAULT) at src/unix/core.c:326 >> #7 0x000000000040484e in IO_Task (arg=0x0) at network_io.c:171 >> #8 0x0000000000412eb7 in uv__thread_start (arg=<optimized out>) at >> src/unix/thread.c:49 >> #9 0x00007ffff7bc26aa in start_thread (arg=0x7ffff6ce7700) at >> pthread_create.c:333 >> #10 0x00007ffff75efeed in clone () at >> ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 >> >> >> Best Regards, >> >> Paul R. >> >> On Monday, December 21, 2020 at 5:39:09 PM UTC-8 pa...@rcom-software.com >> wrote: >> >>> 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/893eb3ed-685e-416b-9d05-3501588095den%40googlegroups.com?utm_medium=email&utm_source=footer> >> https://groups.google.com/d/msgid/libuv/893eb3ed-685e-416b-9d05-3501588095den%40googlegroups.com >> . >> > -- > 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/CADnnjUU5fwbCzRO4eLRRjdzD0jtTZUxcQ6B84aQ9P_pt_McW3Q%40mail.gmail.com?utm_medium=email&utm_source=footer> > https://groups.google.com/d/msgid/libuv/CADnnjUU5fwbCzRO4eLRRjdzD0jtTZUxcQ6B84aQ9P_pt_McW3Q%40mail.gmail.com > . > > > -- > > > Paul Romero > ----------- > RCOM Communications Software > EMAIL: pa...@rcom-software.com > PHONE: (510)482-2769 > > > > > > -- > 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/5FE3E67E.4020303%40rcom-software.com > <https://groups.google.com/d/msgid/libuv/5FE3E67E.4020303%40rcom-software.com?utm_medium=email&utm_source=footer> > . > -- 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/CADnnjUWzHR8NMuyXgYmHFi3aSz4R4iuAeDXYF6-qcxguS3RzNQ%40mail.gmail.com.