...

Text file src/runtime/testdata/testprogcgo/stackswitch.c

Documentation: runtime/testdata/testprogcgo

     1// Copyright 2023 The Go Authors. All rights reserved.
     2// Use of this source code is governed by a BSD-style
     3// license that can be found in the LICENSE file.
     4
     5//go:build unix && !android && !openbsd
     6
     7// Required for darwin ucontext.
     8#define _XOPEN_SOURCE
     9// Required for netbsd stack_t if _XOPEN_SOURCE is set.
    10#define _XOPEN_SOURCE_EXTENDED	1
    11#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    12
    13#include <assert.h>
    14#include <pthread.h>
    15#include <stddef.h>
    16#include <stdio.h>
    17#include <stdlib.h>
    18#include <ucontext.h>
    19
    20// musl libc does not provide getcontext, etc. Skip the test there.
    21//
    22// musl libc doesn't provide any direct detection mechanism. So assume any
    23// non-glibc linux is using musl.
    24//
    25// Note that bionic does not provide getcontext either, but that is skipped via
    26// the android build tag.
    27#if defined(__linux__) && !defined(__GLIBC__)
    28#define MUSL 1
    29#endif
    30#if defined(MUSL)
    31void callStackSwitchCallbackFromThread(void) {
    32	printf("SKIP\n");
    33	exit(0);
    34}
    35#else
    36
    37// Use a stack size larger than the 32kb estimate in
    38// runtime.callbackUpdateSystemStack. This ensures that a second stack
    39// allocation won't accidentally count as in bounds of the first stack
    40#define STACK_SIZE	(64ull << 10)
    41
    42static ucontext_t uctx_save, uctx_switch;
    43
    44extern void stackSwitchCallback(void);
    45
    46char *stack2;
    47
    48static void *stackSwitchThread(void *arg) {
    49	// Simple test: callback works from the normal system stack.
    50	stackSwitchCallback();
    51
    52	// Next, verify that switching stacks doesn't break callbacks.
    53
    54	char *stack1 = malloc(STACK_SIZE);
    55	if (stack1 == NULL) {
    56		perror("malloc");
    57		exit(1);
    58	}
    59
    60	// Allocate the second stack before freeing the first to ensure we don't get
    61	// the same address from malloc.
    62	//
    63	// Will be freed in stackSwitchThread2.
    64	stack2 = malloc(STACK_SIZE);
    65	if (stack1 == NULL) {
    66		perror("malloc");
    67		exit(1);
    68	}
    69
    70	if (getcontext(&uctx_switch) == -1) {
    71		perror("getcontext");
    72		exit(1);
    73	}
    74	uctx_switch.uc_stack.ss_sp = stack1;
    75	uctx_switch.uc_stack.ss_size = STACK_SIZE;
    76	uctx_switch.uc_link = &uctx_save;
    77	makecontext(&uctx_switch, stackSwitchCallback, 0);
    78
    79	if (swapcontext(&uctx_save, &uctx_switch) == -1) {
    80		perror("swapcontext");
    81		exit(1);
    82	}
    83
    84	if (getcontext(&uctx_switch) == -1) {
    85		perror("getcontext");
    86		exit(1);
    87	}
    88	uctx_switch.uc_stack.ss_sp = stack2;
    89	uctx_switch.uc_stack.ss_size = STACK_SIZE;
    90	uctx_switch.uc_link = &uctx_save;
    91	makecontext(&uctx_switch, stackSwitchCallback, 0);
    92
    93	if (swapcontext(&uctx_save, &uctx_switch) == -1) {
    94		perror("swapcontext");
    95		exit(1);
    96	}
    97
    98	free(stack1);
    99
   100	return NULL;
   101}
   102
   103static void *stackSwitchThread2(void *arg) {
   104	// New thread. Use stack bounds that partially overlap the previous
   105	// bounds. needm should refresh the stack bounds anyway since this is a
   106	// new thread.
   107
   108	// N.B. since we used a custom stack with makecontext,
   109	// callbackUpdateSystemStack had to guess the bounds. Its guess assumes
   110	// a 32KiB stack.
   111	char *prev_stack_lo = stack2 + STACK_SIZE - (32*1024);
   112
   113	// New SP is just barely in bounds, but if we don't update the bounds
   114	// we'll almost certainly overflow. The SP that
   115	// callbackUpdateSystemStack sees already has some data pushed, so it
   116	// will be a bit below what we set here. Thus we include some slack.
   117	char *new_stack_hi = prev_stack_lo + 128;
   118
   119	if (getcontext(&uctx_switch) == -1) {
   120		perror("getcontext");
   121		exit(1);
   122	}
   123	uctx_switch.uc_stack.ss_sp = new_stack_hi - (STACK_SIZE / 2);
   124	uctx_switch.uc_stack.ss_size = STACK_SIZE / 2;
   125	uctx_switch.uc_link = &uctx_save;
   126	makecontext(&uctx_switch, stackSwitchCallback, 0);
   127
   128	if (swapcontext(&uctx_save, &uctx_switch) == -1) {
   129		perror("swapcontext");
   130		exit(1);
   131	}
   132
   133	free(stack2);
   134
   135	return NULL;
   136}
   137
   138void callStackSwitchCallbackFromThread(void) {
   139	pthread_t thread;
   140	assert(pthread_create(&thread, NULL, stackSwitchThread, NULL) == 0);
   141	assert(pthread_join(thread, NULL) == 0);
   142
   143	assert(pthread_create(&thread, NULL, stackSwitchThread2, NULL) == 0);
   144	assert(pthread_join(thread, NULL) == 0);
   145}
   146
   147#endif

View as plain text