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