public inbox for cygwin-developers@cygwin.com
 help / color / mirror / Atom feed
From: David McFarland <corngood@gmail.com>
To: cygwin-developers@cygwin.com
Cc: corngood@gmail.com
Subject: Re: Fibers and cygtls
Date: Mon, 20 Dec 2021 11:01:36 -0400	[thread overview]
Message-ID: <vritczlrz5f3.fsf@gmail.com> (raw)
In-Reply-To: <vritzh657xfr.fsf@gmail.com>

> > Bottom line is, before trying to use Windows fibers, or before letting
> > boost use Windows functions on Cygwin (sigh), check what other POSIX or
> > BSD systems provide to implement coroutines.  If there's an API, let's
> > implement this API in Cygwin and then use that from user space.
> 
> There's also an implementation (ucontext) using makecontext, which may
> be the best option. I'll try that next.

I've been at this on-and-off since my last message.  I tried the
ucontext-based implementation of coroutines in boost, but unfortunately
ucontext seems to break exceptions.

The only reference I could find to this was:

https://stackoverflow.com/questions/30725276/modifying-the-stack-on-windows-tib-and-exceptions

> On a related note, I wondered how Cygwin dealt with this for ucontext.
> The source here
> http://szupervigyor.ddsi.hu/source/in/openjdk-6-6b18-1.8.13/cacao-0.99.4/src/vm/jit/i386/cygwin/ucontext.c
> uses GetThreadContext/SetThreadContext to implement ucontext. However,
> from experimentation I see that this also fails when an exception is
> thrown from inside a new context. In fact the SetThreadContext call
> doesn't even update the TIB block!

This seems to match what I'm seeing. The boost unit tests fail with
ucontext, seemingly because the exception handlers are wrong after
switching contexts. They pass using fcontext, but only if I apply the
TLS patches I shared here:

https://cygwin.com/pipermail/cygwin-developers/2020-September/011970.html

Here's a test program:

test-context.cc:
=====
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>

static ucontext_t uctx_main, uctx_func1;

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

struct test_ex {};

static void func1(void) {
    try {
        printf("func1: throw\n");
        throw test_ex();
    } catch(test_ex const&) {
        printf("func1: caught\n");
    }
    printf("func1: swapcontext(&uctx_func1, &uctx_main)\n");
    if (swapcontext(&uctx_func1, &uctx_main) == -1)
        handle_error("swapcontext");
}

const size_t stack_size = 16 * 1024;

void test (char *stack) {
    if (getcontext(&uctx_func1) == -1)
        handle_error("getcontext");
    uctx_func1.uc_stack.ss_sp = stack;
    uctx_func1.uc_stack.ss_size = stack_size;
    uctx_func1.uc_link = &uctx_main;
    makecontext(&uctx_func1, func1, 0);

    printf("main: swapcontext(&uctx_main, &uctx_func1)\n");
    if (swapcontext(&uctx_main, &uctx_func1) == -1)
        handle_error("swapcontext");
}

int main(int argc, char *argv[])
{
    char on_stack[stack_size];
    test(on_stack);

    char *from_malloc = (char*)malloc(stack_size);
    test(from_malloc);

    printf("main: exiting\n");
    exit(EXIT_SUCCESS);
}
=====

When the stack provided to makecontext is allocated on the main thread
stack, it works, but when the stack is allocated via malloc, weird stuff
happens:

When run from a shell it just exits with 0 when the exception is thrown.
This is of particular concern because it may be causing e.g. boost unit
tests to fail silently.

With gdb attached (with or without cygwin-exceptions enabled):

> func1: throw
> gdb: unknown target exception 0x20474343 at 0x7ffca0da4f69
> 
> Thread 1 "test-context" received signal ?, Unknown signal.
> 0x00007ffca0da4f69 in RaiseException () from /cygdrive/c/WINDOWS/System32/KERNELBASE.dll
> (gdb) bt
> #0  0x00007ffca0da4f69 in RaiseException () from /cygdrive/c/WINDOWS/System32/KERNELBASE.dll
> #1  0x00000003ffabcca1 in _Unwind_RaiseException (exc=<optimized out>) at /usr/src/debug/gcc-11.2.0-1/libgcc/unwind-seh.c:334
> #2  0x00000003fd067f1b in __cxxabiv1::__cxa_throw (obj=<optimized out>, tinfo=0x1004030c0 <__fu0__ZTVN10__cxxabiv117__class_type_infoE>, dest=0x0) at /usr/src/debug/gcc-11.2.0-1/libstdc++-v3/libsupc++/eh_throw.cc:90
> #3  0x00000001004010b9 in func1 () at test-context.cc:15
> #4  0x0000000180068fac in swapcontext (oucp=0x0, ucp=0x0) at ../../../../winsup/cygwin/exceptions.cc:1935
> #5  0x0000000100407000 in ?? ()
> #6  0x0000000000000000 in ?? ()
> Backtrace stopped: previous frame inner to this frame (corrupt stack?)
> (gdb) c
> Continuing.
> [Thread 11348.0x2fa0 exited with code 541541187]
> [Thread 11348.0x2bec exited with code 541541187]
> [Thread 11348.0xd50 exited with code 541541187]
> [Thread 11348.0xacc exited with code 541541187]
> 
> Program terminated with signal ?, Unknown signal.
> The program no longer exists.

With strace:

> func1: throw
>   680  409603 [main] test-context 1515 fhandler_console::write: 13 = fhandler_console::write(...)
>  1123  410726 [main] test-context 1515 write: 13 = write(1, 0x80005C2B0, 13)
> --- Process 1724 (pid: 1515) thread 5548 exited with status 0x20474343
> --- Process 1724 (pid: 1515) thread 10792 exited with status 0x20474343
> --- Process 1724 (pid: 1515) thread 2568 exited with status 0x20474343
> --- Process 1724 (pid: 1515) thread 12124 exited with status 0x20474343
> --- Process 1724 (pid: 1515) exited with status 0x20474343

I assume this has something to do with the stack still being in the
valid range in the TIB?

My goal here is to get nix working on cygwin. It depends on boost
coroutines, which can use ucontext, but it uses malloc to allocate heaps
by default.

Anyone have any thoughts on how I could proceed? For now I'll keep
digging into exception handling and context switching.

Cheers,
Dave

  reply	other threads:[~2021-12-20 15:01 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-04 15:13 David McFarland
2020-09-04 19:51 ` Corinna Vinschen
2020-09-05  2:46   ` David McFarland
2021-12-20 15:01     ` David McFarland [this message]
2021-12-22 15:46       ` David McFarland
2020-09-06 14:19 ` Jon Turney

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=vritczlrz5f3.fsf@gmail.com \
    --to=corngood@gmail.com \
    --cc=cygwin-developers@cygwin.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).