/* signal_node.c -*-C-*- * ************************************************************************* * * @copyright * Copyright (C) 2011-2013, Intel Corporation * All rights reserved. * * @copyright * 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. * * @copyright * 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. * **************************************************************************/ #include "signal_node.h" #include /* Define cilk_semaphore_t for all of the respective systems. */ #if defined __APPLE__ # include # include # include typedef semaphore_t cilk_semaphore_t; #elif defined _WIN32 # include "windows-clean.h" typedef HANDLE cilk_semaphore_t; #else // Linux/MIC # include # include # include typedef sem_t cilk_semaphore_t; #endif // Linux/MIC #include "bug.h" #include "cilk_malloc.h" #include "signal_node.h" /** * Interface within the tree to notify workers to wait without consuming cycles * to expend cycles trying to steal. * * cilk_semaphore_t is implemented as an auto-reset event on Windows, and * as a semaphore_t on Linux and MacOS. */ struct signal_node_t { /** 0 if the worker should wait, 1 if it should be running. */ volatile unsigned int run; /** OS-specific semaphore on which the worker can wait. */ cilk_semaphore_t sem; }; /******************************************************************************/ /* Semaphore-abstraction functions */ /******************************************************************************/ /* * All of these functions are simple wrappers for the system-specific semaphore * functions. This keeps the rest of the code reasonably clean and readable. */ #if defined __APPLE__ static void initialize_cilk_semaphore (cilk_semaphore_t *sem) { kern_return_t kstatus = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, 0); assert(kstatus == KERN_SUCCESS); } static void deinitialize_cilk_semaphore (cilk_semaphore_t *sem) { kern_return_t kstatus = semaphore_destroy(mach_task_self(), *sem); assert(kstatus == KERN_SUCCESS); } static void wait_on_cilk_semaphore (cilk_semaphore_t *sem) { kern_return_t kstatus = semaphore_wait(*sem); assert(kstatus == KERN_SUCCESS); } static void signal_cilk_semaphore (cilk_semaphore_t *sem) { kern_return_t kstatus = semaphore_signal(*sem); assert(kstatus == KERN_SUCCESS); } #elif defined _WIN32 // Note: Windows only provides counting semaphores, and we don't really // care about the count. So this is implemented using an auto-reset // event which will automatically reset after the WaitForSingleObject // call static void initialize_cilk_semaphore (cilk_semaphore_t *sem) { // Create an auto-reset event *sem = CreateEvent(NULL, // Security attributes FALSE, // Manual reset FALSE, // Initial state (initially reset) NULL); // Name (anonymous) CILK_ASSERT (NULL != *sem); } static void deinitialize_cilk_semaphore (cilk_semaphore_t *sem) { BOOL result = CloseHandle(*sem); CILK_ASSERT (0 != result); } static void wait_on_cilk_semaphore (cilk_semaphore_t *sem) { // WaitForSingleObject will reset the event DWORD result = WaitForSingleObject (*sem, INFINITE); CILK_ASSERT (WAIT_OBJECT_0 == result); } static void signal_cilk_semaphore (cilk_semaphore_t *sem) { BOOL result = SetEvent (*sem); CILK_ASSERT (0 != result); } #else // Linux/MIC static void initialize_cilk_semaphore (cilk_semaphore_t *sem) { int status = sem_init(sem, 0, 0); assert(0 == status); } static void deinitialize_cilk_semaphore (cilk_semaphore_t *sem) { int status = sem_destroy(sem); assert(0 == status); } static void wait_on_cilk_semaphore (cilk_semaphore_t *sem) { int status; do { status = sem_wait(sem); } while (status != 0 && errno == EINTR); if (status != 0) { perror("sem_wait"); abort(); } } static void signal_cilk_semaphore (cilk_semaphore_t *sem) { sem_post(sem); } #endif // Linux/MIC /******************************************************************************/ /* Runtime interface functions */ /******************************************************************************/ /* * Return a newly malloc'd and initialized signal_node_t. */ COMMON_SYSDEP signal_node_t *signal_node_create(void) { signal_node_t *node; node = ( signal_node_t*) __cilkrts_malloc(sizeof( signal_node_t)); node->run = 0; initialize_cilk_semaphore(&node->sem); return node; } /* * Clean and free a signal_node_t. */ void signal_node_destroy(signal_node_t *node) { CILK_ASSERT(node); deinitialize_cilk_semaphore(&node->sem); __cilkrts_free(node); } /* * Return 1 if the node thinks the worker should go to sleep, 0 otherwise. */ unsigned int signal_node_should_wait(signal_node_t *node) { CILK_ASSERT(node); return !node->run; } /* * Send a message to the node that the worker will eventually read. */ void signal_node_msg(signal_node_t *node, unsigned int msg) { CILK_ASSERT(node); switch (msg) { case 0: // worker should go to sleep. node->run = msg; break; case 1: // worker should be awake. node->run = msg; signal_cilk_semaphore(&node->sem); break; default: // error. CILK_ASSERT(0 == "Bad signal_node_t message."); } } /* * The current worker will wait on the semaphore. */ void signal_node_wait(signal_node_t *node) { CILK_ASSERT(node); while (signal_node_should_wait(node)) { // The loop is here to consume extra semaphore signals that might have // accumulated. No point in passing on the accumulation. wait_on_cilk_semaphore(&node->sem); } }