| /* 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 <stdlib.h> |
| |
| /* Define cilk_semaphore_t for all of the respective systems. */ |
| #if defined __APPLE__ |
| # include <mach/mach_init.h> |
| # include <mach/semaphore.h> |
| # include <mach/task.h> |
| typedef semaphore_t cilk_semaphore_t; |
| #elif defined _WIN32 |
| # include "windows-clean.h" |
| typedef HANDLE cilk_semaphore_t; |
| #else // Linux/MIC |
| # include <errno.h> |
| # include <semaphore.h> |
| # include <stdio.h> |
| 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); |
| } |
| } |