1/* Copyright (c) 2007, Google Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31// Taken from C++11 proposal N2660:
32//   "Dynamic Initialization and Destruction with Concurrency"
33//   http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm
34
35#include <barrelfish/threads.h>
36
37/// Protects global_epoch and all thread_once_t writes.
38static struct thread_mutex thread_once_mutex = THREAD_MUTEX_INITIALIZER;
39
40/// Signalled whenever a fast_pthread_once_t is finalized.
41static struct thread_cond thread_once_condvar = THREAD_COND_INITIALIZER;
42
43#define THREAD_ONCE_BEING_INITIALIZED (THREAD_ONCE_INIT - 1)
44
45static thread_once_t thread_once_global_epoch = 0;
46
47__thread thread_once_t thread_once_local_epoch;
48
49void thread_once_internal(thread_once_t *control, void (*func)(void)) {
50    thread_once_t x = *control; // unprotected access
51    if (x > thread_once_local_epoch) {
52        thread_mutex_lock(&thread_once_mutex);
53        if (*control == THREAD_ONCE_INIT) {
54            *control = THREAD_ONCE_BEING_INITIALIZED;
55            thread_mutex_unlock(&thread_once_mutex);
56            (*func)();
57            thread_mutex_lock(&thread_once_mutex);
58            thread_once_global_epoch++;
59            *control = thread_once_global_epoch;
60            thread_cond_broadcast(&thread_once_condvar);
61        } else {
62            while (*control == THREAD_ONCE_BEING_INITIALIZED) {
63                thread_cond_wait(&thread_once_condvar, &thread_once_mutex);
64            }
65        }
66        thread_once_local_epoch = thread_once_global_epoch;
67        thread_mutex_unlock(&thread_once_mutex);
68    }
69}
70