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
> <https://groups.google.com/d/msgid/libuv/893eb3ed-685e-416b-9d05-3501588095den%40googlegroups.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/CADnnjUU5fwbCzRO4eLRRjdzD0jtTZUxcQ6B84aQ9P_pt_McW3Q%40mail.gmail.com.

Reply via email to