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 [email protected] 
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 [email protected] 
> 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 [email protected] 
>> 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 [email protected] 
>>> 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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/libuv/893eb3ed-685e-416b-9d05-3501588095den%40googlegroups.com.

Reply via email to