summaryrefslogtreecommitdiff
path: root/libgo/runtime/go-go.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/runtime/go-go.c')
-rw-r--r--libgo/runtime/go-go.c668
1 files changed, 0 insertions, 668 deletions
diff --git a/libgo/runtime/go-go.c b/libgo/runtime/go-go.c
deleted file mode 100644
index 82b265f964e..00000000000
--- a/libgo/runtime/go-go.c
+++ /dev/null
@@ -1,668 +0,0 @@
-/* go-go.c -- the go function.
-
- Copyright 2009 The Go Authors. All rights reserved.
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file. */
-
-#include <errno.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <semaphore.h>
-
-#include "config.h"
-#include "go-assert.h"
-#include "go-panic.h"
-#include "go-alloc.h"
-#include "runtime.h"
-#include "arch.h"
-#include "malloc.h"
-
-#ifdef USING_SPLIT_STACK
-/* FIXME: This is not declared anywhere. */
-extern void *__splitstack_find (void *, void *, size_t *, void **, void **,
- void **);
-#endif
-
-/* We stop the threads by sending them the signal GO_SIG_STOP and we
- start them by sending them the signal GO_SIG_START. */
-
-#define GO_SIG_START (SIGRTMIN + 1)
-#define GO_SIG_STOP (SIGRTMIN + 2)
-
-#ifndef SA_RESTART
- #define SA_RESTART 0
-#endif
-
-/* A doubly linked list of the threads we have started. */
-
-struct __go_thread_id
-{
- /* Links. */
- struct __go_thread_id *prev;
- struct __go_thread_id *next;
- /* True if the thread ID has not yet been filled in. */
- _Bool tentative;
- /* Thread ID. */
- pthread_t id;
- /* Thread's M structure. */
- struct M *m;
- /* If the thread ID has not been filled in, the function we are
- running. */
- void (*pfn) (void *);
- /* If the thread ID has not been filled in, the argument to the
- function. */
- void *arg;
-};
-
-static struct __go_thread_id *__go_all_thread_ids;
-
-/* A lock to control access to ALL_THREAD_IDS. */
-
-static pthread_mutex_t __go_thread_ids_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* A semaphore used to wait until all the threads have stopped. */
-
-static sem_t __go_thread_ready_sem;
-
-/* A signal set used to wait until garbage collection is complete. */
-
-static sigset_t __go_thread_wait_sigset;
-
-/* Remove the current thread from the list of threads. */
-
-static void
-remove_current_thread (void *dummy __attribute__ ((unused)))
-{
- struct __go_thread_id *list_entry;
- MCache *mcache;
- int i;
-
- list_entry = m->list_entry;
- mcache = m->mcache;
-
- i = pthread_mutex_lock (&__go_thread_ids_lock);
- __go_assert (i == 0);
-
- if (list_entry->prev != NULL)
- list_entry->prev->next = list_entry->next;
- else
- __go_all_thread_ids = list_entry->next;
- if (list_entry->next != NULL)
- list_entry->next->prev = list_entry->prev;
-
- /* This will lock runtime_mheap as needed. */
- runtime_MCache_ReleaseAll (mcache);
-
- /* This should never deadlock--there shouldn't be any code that
- holds the runtime_mheap lock when locking __go_thread_ids_lock.
- We don't want to do this after releasing __go_thread_ids_lock
- because it will mean that the garbage collector might run, and
- the garbage collector does not try to lock runtime_mheap in all
- cases since it knows it is running single-threaded. */
- runtime_lock (&runtime_mheap);
- mstats.heap_alloc += mcache->local_alloc;
- mstats.heap_objects += mcache->local_objects;
- __builtin_memset (mcache, 0, sizeof (struct MCache));
- runtime_FixAlloc_Free (&runtime_mheap.cachealloc, mcache);
- runtime_unlock (&runtime_mheap);
-
- /* As soon as we release this look, a GC could run. Since this
- thread is no longer on the list, the GC will not find our M
- structure, so it could get freed at any time. That means that
- any code from here to thread exit must not assume that m is
- valid. */
- m = NULL;
- g = NULL;
-
- i = pthread_mutex_unlock (&__go_thread_ids_lock);
- __go_assert (i == 0);
-
- free (list_entry);
-}
-
-/* Start the thread. */
-
-static void *
-start_go_thread (void *thread_arg)
-{
- struct M *newm = (struct M *) thread_arg;
- void (*pfn) (void *);
- void *arg;
- struct __go_thread_id *list_entry;
- int i;
-
-#ifdef __rtems__
- __wrap_rtems_task_variable_add ((void **) &m);
- __wrap_rtems_task_variable_add ((void **) &g);
-#endif
-
- m = newm;
- g = m->curg;
-
- pthread_cleanup_push (remove_current_thread, NULL);
-
- list_entry = newm->list_entry;
-
- pfn = list_entry->pfn;
- arg = list_entry->arg;
-
-#ifndef USING_SPLIT_STACK
- /* If we don't support split stack, record the current stack as the
- top of the stack. There shouldn't be anything relevant to the
- garbage collector above this point. */
- m->gc_sp = (void *) &arg;
-#endif
-
- /* Finish up the entry on the thread list. */
-
- i = pthread_mutex_lock (&__go_thread_ids_lock);
- __go_assert (i == 0);
-
- list_entry->id = pthread_self ();
- list_entry->pfn = NULL;
- list_entry->arg = NULL;
- list_entry->tentative = 0;
-
- i = pthread_mutex_unlock (&__go_thread_ids_lock);
- __go_assert (i == 0);
-
- (*pfn) (arg);
-
- pthread_cleanup_pop (1);
-
- return NULL;
-}
-
-/* The runtime.Goexit function. */
-
-void Goexit (void) asm ("libgo_runtime.runtime.Goexit");
-
-void
-Goexit (void)
-{
- pthread_exit (NULL);
- abort ();
-}
-
-/* Count of threads created. */
-
-static volatile int mcount;
-
-/* Implement the go statement. */
-
-void
-__go_go (void (*pfn) (void*), void *arg)
-{
- int i;
- pthread_attr_t attr;
- struct M *newm;
- struct __go_thread_id *list_entry;
- pthread_t tid;
-
- i = pthread_attr_init (&attr);
- __go_assert (i == 0);
- i = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
- __go_assert (i == 0);
-
-#ifdef LINKER_SUPPORTS_SPLIT_STACK
- /* The linker knows how to handle calls between code which uses
- -fsplit-stack and code which does not. That means that we can
- run with a smaller stack and rely on the -fsplit-stack support to
- save us. The GNU/Linux glibc library won't let us have a very
- small stack, but we make it as small as we can. */
-#ifndef PTHREAD_STACK_MIN
-#define PTHREAD_STACK_MIN 8192
-#endif
- i = pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
- __go_assert (i == 0);
-#endif
-
- newm = __go_alloc (sizeof (M));
-
- list_entry = malloc (sizeof (struct __go_thread_id));
- list_entry->prev = NULL;
- list_entry->next = NULL;
- list_entry->tentative = 1;
- list_entry->m = newm;
- list_entry->pfn = pfn;
- list_entry->arg = arg;
-
- newm->list_entry = list_entry;
-
- newm->curg = __go_alloc (sizeof (G));
- newm->curg->m = newm;
-
- newm->id = __sync_fetch_and_add (&mcount, 1);
- newm->fastrand = 0x49f6428aUL + newm->id;
-
- newm->mcache = runtime_allocmcache ();
-
- /* Add the thread to the list of all threads, marked as tentative
- since it is not yet ready to go. */
- i = pthread_mutex_lock (&__go_thread_ids_lock);
- __go_assert (i == 0);
-
- if (__go_all_thread_ids != NULL)
- __go_all_thread_ids->prev = list_entry;
- list_entry->next = __go_all_thread_ids;
- __go_all_thread_ids = list_entry;
-
- i = pthread_mutex_unlock (&__go_thread_ids_lock);
- __go_assert (i == 0);
-
- /* Start the thread. */
- i = pthread_create (&tid, &attr, start_go_thread, newm);
- __go_assert (i == 0);
-
- i = pthread_attr_destroy (&attr);
- __go_assert (i == 0);
-}
-
-/* This is the signal handler for GO_SIG_START. The garbage collector
- will send this signal to a thread when it wants the thread to
- start. We don't have to actually do anything here, but we need a
- signal handler since ignoring the signal will mean that the
- sigsuspend will never see it. */
-
-static void
-gc_start_handler (int sig __attribute__ ((unused)))
-{
-}
-
-/* Tell the garbage collector that we are ready, and wait for the
- garbage collector to tell us that it is done. This may be called
- by a signal handler, so it is restricted to using functions which
- are async cancel safe. */
-
-static void
-stop_for_gc (void)
-{
- int i;
-
- /* Tell the garbage collector about our stack. */
-#ifdef USING_SPLIT_STACK
- m->gc_sp = __splitstack_find (NULL, NULL, &m->gc_len,
- &m->gc_next_segment, &m->gc_next_sp,
- &m->gc_initial_sp);
-#else
- {
- uintptr_t top = (uintptr_t) m->gc_sp;
- uintptr_t bottom = (uintptr_t) &top;
- if (top < bottom)
- {
- m->gc_next_sp = m->gc_sp;
- m->gc_len = bottom - top;
- }
- else
- {
- m->gc_next_sp = (void *) bottom;
- m->gc_len = top - bottom;
- }
- }
-#endif
-
- /* Tell the garbage collector that we are ready by posting to the
- semaphore. */
- i = sem_post (&__go_thread_ready_sem);
- __go_assert (i == 0);
-
- /* Wait for the garbage collector to tell us to continue. */
- sigsuspend (&__go_thread_wait_sigset);
-}
-
-/* This is the signal handler for GO_SIG_STOP. The garbage collector
- will send this signal to a thread when it wants the thread to
- stop. */
-
-static void
-gc_stop_handler (int sig __attribute__ ((unused)))
-{
- struct M *pm = m;
-
- if (__sync_bool_compare_and_swap (&pm->holds_finlock, 1, 1))
- {
- /* We can't interrupt the thread while it holds the finalizer
- lock. Otherwise we can get into a deadlock when mark calls
- runtime_walkfintab. */
- __sync_bool_compare_and_swap (&pm->gcing_for_finlock, 0, 1);
- return;
- }
-
- if (__sync_bool_compare_and_swap (&pm->mallocing, 1, 1))
- {
- /* m->mallocing was already non-zero. We can't interrupt the
- thread while it is running an malloc. Instead, tell it to
- call back to us when done. */
- __sync_bool_compare_and_swap (&pm->gcing, 0, 1);
- return;
- }
-
- if (__sync_bool_compare_and_swap (&pm->nomemprof, 1, 1))
- {
- /* Similarly, we can't interrupt the thread while it is building
- profiling information. Otherwise we can get into a deadlock
- when sweepspan calls MProf_Free. */
- __sync_bool_compare_and_swap (&pm->gcing_for_prof, 0, 1);
- return;
- }
-
- stop_for_gc ();
-}
-
-/* This is called by malloc when it gets a signal during the malloc
- call itself. */
-
-int
-__go_run_goroutine_gc (int r)
-{
- /* Force callee-saved registers to be saved on the stack. This is
- not needed if we are invoked from the signal handler, but it is
- needed if we are called directly, since otherwise we might miss
- something that a function somewhere up the call stack is holding
- in a register. */
- __builtin_unwind_init ();
-
- stop_for_gc ();
-
- /* This avoids tail recursion, to make sure that the saved registers
- are on the stack. */
- return r;
-}
-
-/* Stop all the other threads for garbage collection. */
-
-void
-runtime_stoptheworld (void)
-{
- int i;
- pthread_t me;
- int c;
- struct __go_thread_id *p;
-
- i = pthread_mutex_lock (&__go_thread_ids_lock);
- __go_assert (i == 0);
-
- me = pthread_self ();
- c = 0;
- p = __go_all_thread_ids;
- while (p != NULL)
- {
- if (p->tentative || pthread_equal (me, p->id))
- p = p->next;
- else
- {
- i = pthread_kill (p->id, GO_SIG_STOP);
- if (i == 0)
- {
- ++c;
- p = p->next;
- }
- else if (i == ESRCH)
- {
- struct __go_thread_id *next;
-
- /* This thread died somehow. Remove it from the
- list. */
- next = p->next;
- if (p->prev != NULL)
- p->prev->next = next;
- else
- __go_all_thread_ids = next;
- if (next != NULL)
- next->prev = p->prev;
- free (p);
- p = next;
- }
- else
- abort ();
- }
- }
-
- /* Wait for each thread to receive the signal and post to the
- semaphore. If a thread receives the signal but contrives to die
- before it posts to the semaphore, then we will hang forever
- here. */
-
- while (c > 0)
- {
- i = sem_wait (&__go_thread_ready_sem);
- if (i < 0 && errno == EINTR)
- continue;
- __go_assert (i == 0);
- --c;
- }
-
- /* Leave with __go_thread_ids_lock held. */
-}
-
-/* Scan all the stacks for garbage collection. This should be called
- with __go_thread_ids_lock held. */
-
-void
-__go_scanstacks (void (*scan) (byte *, int64))
-{
- pthread_t me;
- struct __go_thread_id *p;
-
- /* Make sure all the registers for this thread are on the stack. */
- __builtin_unwind_init ();
-
- me = pthread_self ();
- for (p = __go_all_thread_ids; p != NULL; p = p->next)
- {
- if (p->tentative)
- {
- /* The goroutine function and argument can be allocated on
- the heap, so we have to scan them for a thread that has
- not yet started. */
- scan ((void *) &p->pfn, sizeof (void *));
- scan ((void *) &p->arg, sizeof (void *));
- scan ((void *) &p->m, sizeof (void *));
- continue;
- }
-
-#ifdef USING_SPLIT_STACK
-
- void *sp;
- size_t len;
- void *next_segment;
- void *next_sp;
- void *initial_sp;
-
- if (pthread_equal (me, p->id))
- {
- next_segment = NULL;
- next_sp = NULL;
- initial_sp = NULL;
- sp = __splitstack_find (NULL, NULL, &len, &next_segment,
- &next_sp, &initial_sp);
- }
- else
- {
- sp = p->m->gc_sp;
- len = p->m->gc_len;
- next_segment = p->m->gc_next_segment;
- next_sp = p->m->gc_next_sp;
- initial_sp = p->m->gc_initial_sp;
- }
-
- while (sp != NULL)
- {
- scan (sp, len);
- sp = __splitstack_find (next_segment, next_sp, &len,
- &next_segment, &next_sp, &initial_sp);
- }
-
-#else /* !defined(USING_SPLIT_STACK) */
-
- if (pthread_equal (me, p->id))
- {
- uintptr_t top = (uintptr_t) m->gc_sp;
- uintptr_t bottom = (uintptr_t) &top;
- if (top < bottom)
- scan (m->gc_sp, bottom - top);
- else
- scan ((void *) bottom, top - bottom);
- }
- else
- {
- scan (p->m->gc_next_sp, p->m->gc_len);
- }
-
-#endif /* !defined(USING_SPLIT_STACK) */
-
- /* Also scan the M structure while we're at it. */
-
- scan ((void *) &p->m, sizeof (void *));
- }
-}
-
-/* Release all the memory caches. This is called with
- __go_thread_ids_lock held. */
-
-void
-__go_stealcache (void)
-{
- struct __go_thread_id *p;
-
- for (p = __go_all_thread_ids; p != NULL; p = p->next)
- runtime_MCache_ReleaseAll (p->m->mcache);
-}
-
-/* Gather memory cache statistics. This is called with
- __go_thread_ids_lock held. */
-
-void
-__go_cachestats (void)
-{
- struct __go_thread_id *p;
-
- for (p = __go_all_thread_ids; p != NULL; p = p->next)
- {
- MCache *c;
- int i;
-
- runtime_purgecachedstats(p->m);
- c = p->m->mcache;
- for (i = 0; i < NumSizeClasses; ++i)
- {
- mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc;
- c->local_by_size[i].nmalloc = 0;
- mstats.by_size[i].nfree += c->local_by_size[i].nfree;
- c->local_by_size[i].nfree = 0;
- }
- }
-}
-
-/* Start the other threads after garbage collection. */
-
-void
-runtime_starttheworld (bool extra __attribute__ ((unused)))
-{
- int i;
- pthread_t me;
- struct __go_thread_id *p;
-
- /* Here __go_thread_ids_lock should be held. */
-
- me = pthread_self ();
- p = __go_all_thread_ids;
- while (p != NULL)
- {
- if (p->tentative || pthread_equal (me, p->id))
- p = p->next;
- else
- {
- i = pthread_kill (p->id, GO_SIG_START);
- if (i == 0)
- p = p->next;
- else
- abort ();
- }
- }
-
- i = pthread_mutex_unlock (&__go_thread_ids_lock);
- __go_assert (i == 0);
-}
-
-/* Initialize the interaction between goroutines and the garbage
- collector. */
-
-void
-__go_gc_goroutine_init (void *sp __attribute__ ((unused)))
-{
- struct __go_thread_id *list_entry;
- int i;
- sigset_t sset;
- struct sigaction act;
-
- /* Add the initial thread to the list of all threads. */
-
- list_entry = malloc (sizeof (struct __go_thread_id));
- list_entry->prev = NULL;
- list_entry->next = NULL;
- list_entry->tentative = 0;
- list_entry->id = pthread_self ();
- list_entry->m = m;
- list_entry->pfn = NULL;
- list_entry->arg = NULL;
- __go_all_thread_ids = list_entry;
-
- /* Initialize the semaphore which signals when threads are ready for
- GC. */
-
- i = sem_init (&__go_thread_ready_sem, 0, 0);
- __go_assert (i == 0);
-
- /* Fetch the current signal mask. */
-
- i = sigemptyset (&sset);
- __go_assert (i == 0);
- i = sigprocmask (SIG_BLOCK, NULL, &sset);
- __go_assert (i == 0);
-
- /* Make sure that GO_SIG_START is not blocked and GO_SIG_STOP is
- blocked, and save that set for use with later calls to sigsuspend
- while waiting for GC to complete. */
-
- i = sigdelset (&sset, GO_SIG_START);
- __go_assert (i == 0);
- i = sigaddset (&sset, GO_SIG_STOP);
- __go_assert (i == 0);
- __go_thread_wait_sigset = sset;
-
- /* Block SIG_SET_START and unblock SIG_SET_STOP, and use that for
- the process signal mask. */
-
- i = sigaddset (&sset, GO_SIG_START);
- __go_assert (i == 0);
- i = sigdelset (&sset, GO_SIG_STOP);
- __go_assert (i == 0);
- i = sigprocmask (SIG_SETMASK, &sset, NULL);
- __go_assert (i == 0);
-
- /* Install the signal handlers. */
- memset (&act, 0, sizeof act);
- i = sigemptyset (&act.sa_mask);
- __go_assert (i == 0);
-
- act.sa_handler = gc_start_handler;
- act.sa_flags = SA_RESTART;
- i = sigaction (GO_SIG_START, &act, NULL);
- __go_assert (i == 0);
-
- /* We could consider using an alternate signal stack for this. The
- function does not use much stack space, so it may be OK. */
- act.sa_handler = gc_stop_handler;
- i = sigaction (GO_SIG_STOP, &act, NULL);
- __go_assert (i == 0);
-
-#ifndef USING_SPLIT_STACK
- /* If we don't support split stack, record the current stack as the
- top of the stack. */
- m->gc_sp = sp;
-#endif
-}