view libcilkrts/runtime/cilk_fiber-unix.cpp @ 143:76e1cf5455ef

add cbc_gc test
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sun, 23 Dec 2018 19:24:05 +0900
parents 04ced10e8804
children
line wrap: on
line source

/* cilk_fiber-unix.cpp                  -*-C++-*-
 *
 *************************************************************************
 *
 *  Copyright (C) 2012-2016, Intel Corporation
 *  All rights reserved.
 *  
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in
 *      the documentation and/or other materials provided with the
 *      distribution.
 *    * Neither the name of Intel Corporation nor the names of its
 *      contributors may be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 *  
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 *  WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *  
 *  *********************************************************************
 *  
 *  PLEASE NOTE: This file is a downstream copy of a file mainitained in
 *  a repository at cilkplus.org. Changes made to this file that are not
 *  submitted through the contribution process detailed at
 *  http://www.cilkplus.org/submit-cilk-contribution will be lost the next
 *  time that a new version is released. Changes only submitted to the
 *  GNU compiler collection or posted to the git repository at
 *  https://bitbucket.org/intelcilkruntime/intel-cilk-runtime.git are
 *  not tracked.
 *  
 *  We welcome your contributions to this open source project. Thank you
 *  for your assistance in helping us improve Cilk Plus.
 **************************************************************************/

#include "cilk_fiber-unix.h"
#include "cilk_malloc.h"
#include "bug.h"
#include "os.h"

#include <cstdio>
#include <cstdlib>

#include <errno.h>
#include <sys/mman.h>
#include <unistd.h>
#include "declare-alloca.h"

// MAP_ANON is deprecated on Linux, but seems to be required on Mac...
#ifndef MAP_ANONYMOUS
#   define MAP_ANONYMOUS MAP_ANON
#endif

// MAP_STACK and MAP_GROWSDOWN have no affect in Linux as of 2014-04-04, but
// could be very useful in future versions.  If they are not defined, then set
// them to zero (no bits set).
#ifndef MAP_STACK
#   define MAP_STACK 0
#endif
#ifndef MAP_GROWSDOWN
#   define MAP_GROWSDOWN 0
#endif

// Magic number for sanity checking fiber structure
const unsigned magic_number = 0x5afef00d;

// Page size for stacks
#ifdef _WRS_KERNEL
long cilk_fiber_sysdep::s_page_size = 4096;
#else
long cilk_fiber_sysdep::s_page_size = sysconf(_SC_PAGESIZE);
#endif

cilk_fiber_sysdep::cilk_fiber_sysdep(std::size_t stack_size)
    : cilk_fiber(stack_size)
    , m_magic(magic_number)
{
    // Set m_stack and m_stack_base.
    make_stack(stack_size);

    // Get high-address of stack, with 32-bytes of spare space, and rounded
    // down to the nearest 32-byte boundary.
    const uintptr_t align_mask = 32 - 1;
    m_stack_base -= ((std::size_t) m_stack_base) & align_mask;
}

cilk_fiber_sysdep::cilk_fiber_sysdep(from_thread_t)
    : cilk_fiber()
    , m_magic(magic_number)
{
    this->set_allocated_from_thread(true);

    // Dummy stack data for thread-main fiber
    m_stack      = NULL;
    m_stack_base = NULL;
}

void cilk_fiber_sysdep::convert_fiber_back_to_thread()
{
    // Does nothing on Linux.
}

cilk_fiber_sysdep::~cilk_fiber_sysdep()
{
    CILK_ASSERT(magic_number == m_magic);
    if (!this->is_allocated_from_thread())
        free_stack();
}

#if SUPPORT_GET_CURRENT_FIBER
cilk_fiber_sysdep* cilk_fiber_sysdep::get_current_fiber_sysdep()
{
    return cilkos_get_tls_cilk_fiber();
}
#endif

// Jump to resume other fiber.  We may or may not come back.
inline void cilk_fiber_sysdep::resume_other_sysdep(cilk_fiber_sysdep* other)
{
    if (other->is_resumable()) {
        other->set_resumable(false);
        // Resume by longjmp'ing to the place where we suspended.
        CILK_LONGJMP(other->m_resume_jmpbuf);
    }
    else {
        // Otherwise, we've never ran this fiber before.  Start the
        // proc method.
        other->run();
    }
}

void cilk_fiber_sysdep::suspend_self_and_resume_other_sysdep(cilk_fiber_sysdep* other)
{
#if SUPPORT_GET_CURRENT_FIBER
    cilkos_set_tls_cilk_fiber(other);
#endif
    CILK_ASSERT(this->is_resumable());


    // Jump to the other fiber.  We expect to come back.
    if (! CILK_SETJMP(m_resume_jmpbuf)) {
        resume_other_sysdep(other);
    }

    // Return here when another fiber resumes me.
    // If the fiber that switched to me wants to be deallocated, do it now.
    do_post_switch_actions();
}

NORETURN cilk_fiber_sysdep::jump_to_resume_other_sysdep(cilk_fiber_sysdep* other)
{
#if SUPPORT_GET_CURRENT_FIBER
    cilkos_set_tls_cilk_fiber(other);
#endif
    CILK_ASSERT(!this->is_resumable());

    // Jump to the other fiber.  But we are never coming back because
    // this fiber is being reset.
    resume_other_sysdep(other);

    // We should never come back here...
    __cilkrts_bug("Should not get here");
}

// GCC doesn't allow us to call __builtin_longjmp in the same function that
// calls __builtin_setjmp, so create a new function to house the call to
// __builtin_longjmp
static void __attribute__((noinline))
do_cilk_longjmp(__CILK_JUMP_BUFFER jmpbuf)
{
    CILK_LONGJMP(jmpbuf);
}

NORETURN cilk_fiber_sysdep::run()
{
    // Only fibers created from a pool have a proc method to run and execute. 
    CILK_ASSERT(m_start_proc);
    CILK_ASSERT(!this->is_allocated_from_thread());
    CILK_ASSERT(!this->is_resumable());

    // TBD: This setjmp/longjmp pair simply changes the stack pointer.
    // We could probably replace this code with some assembly.
    if (! CILK_SETJMP(m_resume_jmpbuf))
    {
        // Calculate the size of the current stack frame (i.e., this
        // run() function.  
        size_t frame_size = (size_t)JMPBUF_FP(m_resume_jmpbuf) - (size_t)JMPBUF_SP(m_resume_jmpbuf);

        // Macs require 16-byte alignment.  Do it always because it just
        // doesn't matter
        if (frame_size & (16-1))
            frame_size += 16 - (frame_size  & (16-1));

        // Assert that we are getting a reasonable frame size out of
        // it.  If this run() function is using more than 4096 bytes
        // of space for its local variables / any state that spills to
        // registers, something is probably *very* wrong here...
        //
        // 4096 bytes just happens to be a number that seems "large
        // enough" --- for an example GCC 32-bit compilation, the
        // frame size was 48 bytes.
        CILK_ASSERT(frame_size < 4096);

        // Change stack pointer to fiber stack.  Offset the
        // calculation by the frame size, so that we've allocated
        // enough extra space from the top of the stack we are
        // switching to for any temporaries required for this run()
        // function.
        JMPBUF_SP(m_resume_jmpbuf) = CILK_ADJUST_SP(m_stack_base - frame_size);

        // GCC doesn't allow us to call __builtin_longjmp in the same function
        // that calls __builtin_setjmp, so it's been moved into it's own
        // function that cannot be inlined.
        do_cilk_longjmp(m_resume_jmpbuf);
    }

    // Note: our resetting of the stack pointer is valid only if the
    // compiler has not saved any temporaries onto the stack for this
    // function before the longjmp that we still care about at this
    // point.
    
    // Verify that 1) 'this' is still valid and 2) '*this' has not been
    // corrupted.
    CILK_ASSERT(magic_number == m_magic);

    // If the fiber that switched to me wants to be deallocated, do it now.
    do_post_switch_actions();

    // Now call the user proc on the new stack
    m_start_proc(this);

    // alloca() to force generation of frame pointer.  The argument to alloca
    // is contrived to prevent the compiler from optimizing it away.  This
    // code should never actually be executed.
    int* dummy = (int*) alloca((sizeof(int) + (std::size_t) m_start_proc) & 0x1);
    *dummy = 0xface;

    // User proc should never return.
    __cilkrts_bug("Should not get here");
}

void cilk_fiber_sysdep::make_stack(size_t stack_size)
{
    char* p;
    // We've already validated that the stack size is page-aligned and
    // is a reasonable value.  No need to do any extra rounding here.
    size_t rounded_stack_size = stack_size;

    // Normally, we have already validated that the stack size is
    // aligned to 4K.  In the rare case that pages are huge though, we
    // need to do some extra checks.
    if (rounded_stack_size < 3 * (size_t)s_page_size) {
        // If the specified stack size is too small, round up to 3
        // pages.  We need at least 2 extra for the guard pages.
        rounded_stack_size = 3 * (size_t)s_page_size;
    }
    else {
        // Otherwise, the stack size is large enough, but might not be
        // a multiple of page size.  Round up to nearest multiple of
        // s_page_size, just to be safe.
        size_t remainder = rounded_stack_size % s_page_size;
        if (remainder) {
            rounded_stack_size += s_page_size - remainder;
        }
    }

    p = (char*)mmap(0, rounded_stack_size,
                    PROT_READ|PROT_WRITE,
                    MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK|MAP_GROWSDOWN,
                    -1, 0);
    if (MAP_FAILED == p) {
        // For whatever reason (probably ran out of memory), mmap() failed.
        // There is no stack to return, so the program loses parallelism.
        m_stack = NULL;
        m_stack_base = NULL;
        return;
    }

    // mprotect guard pages.
    mprotect(p + rounded_stack_size - s_page_size, s_page_size, PROT_NONE);
    mprotect(p, s_page_size, PROT_NONE);

    m_stack = p;
    m_stack_base = p + rounded_stack_size - s_page_size;
}


void cilk_fiber_sysdep::free_stack()
{
    if (m_stack) {
        size_t rounded_stack_size = m_stack_base - m_stack + s_page_size;
        if (munmap(m_stack, rounded_stack_size) < 0)
            __cilkrts_bug("Cilk: stack munmap failed error %d\n", errno);
    }
}

/* End cilk_fiber-unix.cpp */