1/* signal_node.c -*-C-*- 2 * 3 ************************************************************************* 4 * 5 * @copyright 6 * Copyright (C) 2011-2013, Intel Corporation 7 * All rights reserved. 8 * 9 * @copyright 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * * Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * * Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * * Neither the name of Intel Corporation nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific prior written permission. 23 * 24 * @copyright 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 32 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 35 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 * 38 **************************************************************************/ 39 40#include "signal_node.h" 41#include <stdlib.h> 42 43/* Define cilk_semaphore_t for all of the respective systems. */ 44#if defined __APPLE__ 45# include <mach/mach_init.h> 46# include <mach/semaphore.h> 47# include <mach/task.h> 48 typedef semaphore_t cilk_semaphore_t; 49#elif defined _WIN32 50# include "windows-clean.h" 51 typedef HANDLE cilk_semaphore_t; 52#else // Linux/MIC 53# include <errno.h> 54# include <semaphore.h> 55# include <stdio.h> 56 typedef sem_t cilk_semaphore_t; 57#endif // Linux/MIC 58 59#include "bug.h" 60#include "cilk_malloc.h" 61#include "signal_node.h" 62 63/** 64 * Interface within the tree to notify workers to wait without consuming cycles 65 * to expend cycles trying to steal. 66 * 67 * cilk_semaphore_t is implemented as an auto-reset event on Windows, and 68 * as a semaphore_t on Linux and MacOS. 69 */ 70struct signal_node_t 71{ 72 /** 0 if the worker should wait, 1 if it should be running. */ 73 volatile unsigned int run; 74 75 /** OS-specific semaphore on which the worker can wait. */ 76 cilk_semaphore_t sem; 77}; 78 79/******************************************************************************/ 80/* Semaphore-abstraction functions */ 81/******************************************************************************/ 82 83/* 84 * All of these functions are simple wrappers for the system-specific semaphore 85 * functions. This keeps the rest of the code reasonably clean and readable. 86 */ 87 88#if defined __APPLE__ 89static void initialize_cilk_semaphore (cilk_semaphore_t *sem) 90{ 91 kern_return_t kstatus 92 = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, 0); 93 assert(kstatus == KERN_SUCCESS); 94} 95static void deinitialize_cilk_semaphore (cilk_semaphore_t *sem) 96{ 97 kern_return_t kstatus = semaphore_destroy(mach_task_self(), *sem); 98 assert(kstatus == KERN_SUCCESS); 99} 100static void wait_on_cilk_semaphore (cilk_semaphore_t *sem) 101{ 102 kern_return_t kstatus = semaphore_wait(*sem); 103 assert(kstatus == KERN_SUCCESS); 104} 105static void signal_cilk_semaphore (cilk_semaphore_t *sem) 106{ 107 kern_return_t kstatus = semaphore_signal(*sem); 108 assert(kstatus == KERN_SUCCESS); 109} 110#elif defined _WIN32 111// Note: Windows only provides counting semaphores, and we don't really 112// care about the count. So this is implemented using an auto-reset 113// event which will automatically reset after the WaitForSingleObject 114// call 115static void initialize_cilk_semaphore (cilk_semaphore_t *sem) 116{ 117 // Create an auto-reset event 118 *sem = CreateEvent(NULL, // Security attributes 119 FALSE, // Manual reset 120 FALSE, // Initial state (initially reset) 121 NULL); // Name (anonymous) 122 CILK_ASSERT (NULL != *sem); 123} 124 125static void deinitialize_cilk_semaphore (cilk_semaphore_t *sem) 126{ 127 BOOL result = CloseHandle(*sem); 128 CILK_ASSERT (0 != result); 129} 130 131static void wait_on_cilk_semaphore (cilk_semaphore_t *sem) 132{ 133 // WaitForSingleObject will reset the event 134 DWORD result = WaitForSingleObject (*sem, INFINITE); 135 CILK_ASSERT (WAIT_OBJECT_0 == result); 136} 137static void signal_cilk_semaphore (cilk_semaphore_t *sem) 138{ 139 BOOL result = SetEvent (*sem); 140 CILK_ASSERT (0 != result); 141} 142#else // Linux/MIC 143static void initialize_cilk_semaphore (cilk_semaphore_t *sem) 144{ 145 int status = sem_init(sem, 0, 0); 146 assert(0 == status); 147} 148static void deinitialize_cilk_semaphore (cilk_semaphore_t *sem) 149{ 150 int status = sem_destroy(sem); 151 assert(0 == status); 152} 153static void wait_on_cilk_semaphore (cilk_semaphore_t *sem) 154{ 155 int status; 156 157 do { 158 status = sem_wait(sem); 159 } while (status != 0 && errno == EINTR); 160 161 if (status != 0) { 162 perror("sem_wait"); 163 abort(); 164 } 165} 166static void signal_cilk_semaphore (cilk_semaphore_t *sem) 167{ 168 sem_post(sem); 169} 170#endif // Linux/MIC 171 172/******************************************************************************/ 173/* Runtime interface functions */ 174/******************************************************************************/ 175 176/* 177 * Return a newly malloc'd and initialized signal_node_t. 178 */ 179COMMON_SYSDEP 180signal_node_t *signal_node_create(void) 181{ 182 signal_node_t *node; 183 184 node = ( signal_node_t*) 185 __cilkrts_malloc(sizeof( signal_node_t)); 186 node->run = 0; 187 initialize_cilk_semaphore(&node->sem); 188 189 return node; 190} 191 192/* 193 * Clean and free a signal_node_t. 194 */ 195void signal_node_destroy(signal_node_t *node) 196{ 197 CILK_ASSERT(node); 198 deinitialize_cilk_semaphore(&node->sem); 199 __cilkrts_free(node); 200} 201 202/* 203 * Return 1 if the node thinks the worker should go to sleep, 0 otherwise. 204 */ 205unsigned int signal_node_should_wait(signal_node_t *node) 206{ 207 CILK_ASSERT(node); 208 return !node->run; 209} 210 211/* 212 * Send a message to the node that the worker will eventually read. 213 */ 214void signal_node_msg(signal_node_t *node, unsigned int msg) 215{ 216 CILK_ASSERT(node); 217 switch (msg) { 218 case 0: // worker should go to sleep. 219 node->run = msg; 220 break; 221 case 1: // worker should be awake. 222 node->run = msg; 223 signal_cilk_semaphore(&node->sem); 224 break; 225 default: // error. 226 CILK_ASSERT(0 == "Bad signal_node_t message."); 227 } 228} 229 230/* 231 * The current worker will wait on the semaphore. 232 */ 233void signal_node_wait(signal_node_t *node) 234{ 235 CILK_ASSERT(node); 236 while (signal_node_should_wait(node)) { 237 // The loop is here to consume extra semaphore signals that might have 238 // accumulated. No point in passing on the accumulation. 239 wait_on_cilk_semaphore(&node->sem); 240 } 241} 242