mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-15 02:37:55 +00:00
149 lines
4.5 KiB
C
149 lines
4.5 KiB
C
|
// clang-format off
|
||
|
/* Copyright 2016 Google Inc.
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License. */
|
||
|
|
||
|
#include "libc/thread/nsync_cpp.h"
|
||
|
#include "libc/thread/platform.h"
|
||
|
#include "libc/thread/compiler.h"
|
||
|
#include "libc/thread/cputype.h"
|
||
|
#include "libc/thread/nsync.h"
|
||
|
#include "libc/thread/dll.h"
|
||
|
#include "libc/thread/sem.h"
|
||
|
#include "libc/thread/wait_internal.h"
|
||
|
#include "libc/thread/common.h"
|
||
|
#include "libc/thread/atomic.h"
|
||
|
|
||
|
NSYNC_CPP_START_
|
||
|
|
||
|
/* An once_sync_s struct contains a lock, and a condition variable on which
|
||
|
threads may wait for an nsync_once to be initialized by another thread.
|
||
|
|
||
|
A separate struct is used only to keep nsync_once small.
|
||
|
|
||
|
A given nsync_once can be associated with any once_sync_s struct, but cannot
|
||
|
be associated with more than one. nsync_once instances are mapped to
|
||
|
once_sync_s instances by a trivial hashing scheme implemented by
|
||
|
NSYNC_ONCE_SYNC_().
|
||
|
|
||
|
The number of once_sync_s structs in the following array is greater than one
|
||
|
only to reduce the probability of contention if a great many distinct
|
||
|
nsync_once variables are initialized concurrently. */
|
||
|
static struct once_sync_s {
|
||
|
nsync_mu once_mu;
|
||
|
nsync_cv once_cv;
|
||
|
} once_sync[64];
|
||
|
|
||
|
/* Return a pointer to the once_sync_s struct associated with the nsync_once *p. */
|
||
|
#define NSYNC_ONCE_SYNC_(p) &once_sync[(((uintptr_t) (p)) / sizeof (*(p))) % \
|
||
|
(sizeof (once_sync) / sizeof (once_sync[0]))]
|
||
|
|
||
|
/* Implement nsync_run_once, nsync_run_once_arg, nsync_run_once_spin, or
|
||
|
nsync_run_once_arg_spin, chosen as described below.
|
||
|
|
||
|
If s!=NULL, s is required to point to the once_sync_s associated with *once,
|
||
|
and the semantics of nsync_run_once or nsync_run_once_arg are provided.
|
||
|
If s==NULL, the semantics of nsync_run_once_spin, or nsync_run_once_arg_spin
|
||
|
are provided.
|
||
|
|
||
|
If f!=NULL, the semantics of nsync_run_once or nsync_run_once_spin are
|
||
|
provided. Otherwise, farg is required to be non-NULL, and the semantics of
|
||
|
nsync_run_once_arg or nsync_run_once_arg_spin are provided. */
|
||
|
static void nsync_run_once_impl (nsync_once *once, struct once_sync_s *s,
|
||
|
void (*f) (void), void (*farg) (void *arg), void *arg) {
|
||
|
uint32_t o = ATM_LOAD_ACQ (once);
|
||
|
if (o != 2) {
|
||
|
unsigned attempts = 0;
|
||
|
if (s != NULL) {
|
||
|
nsync_mu_lock (&s->once_mu);
|
||
|
}
|
||
|
while (o == 0 && !ATM_CAS_ACQ (once, 0, 1)) {
|
||
|
o = ATM_LOAD (once);
|
||
|
}
|
||
|
if (o == 0) {
|
||
|
if (s != NULL) {
|
||
|
nsync_mu_unlock (&s->once_mu);
|
||
|
}
|
||
|
if (f != NULL) {
|
||
|
(*f) ();
|
||
|
} else {
|
||
|
(*farg) (arg);
|
||
|
}
|
||
|
if (s != NULL) {
|
||
|
nsync_mu_lock (&s->once_mu);
|
||
|
nsync_cv_broadcast (&s->once_cv);
|
||
|
}
|
||
|
ATM_STORE_REL (once, 2);
|
||
|
}
|
||
|
while (ATM_LOAD_ACQ (once) != 2) {
|
||
|
if (s != NULL) {
|
||
|
nsync_time deadline;
|
||
|
if (attempts < 50) {
|
||
|
attempts += 10;
|
||
|
}
|
||
|
deadline = nsync_time_add (nsync_time_now (), nsync_time_ms (attempts));
|
||
|
nsync_cv_wait_with_deadline (&s->once_cv, &s->once_mu, deadline, NULL);
|
||
|
} else {
|
||
|
attempts = nsync_spin_delay_ (attempts);
|
||
|
}
|
||
|
}
|
||
|
if (s != NULL) {
|
||
|
nsync_mu_unlock (&s->once_mu);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void nsync_run_once (nsync_once *once, void (*f) (void)) {
|
||
|
uint32_t o;
|
||
|
IGNORE_RACES_START ();
|
||
|
o = ATM_LOAD_ACQ (once);
|
||
|
if (o != 2) {
|
||
|
struct once_sync_s *s = NSYNC_ONCE_SYNC_ (once);
|
||
|
nsync_run_once_impl (once, s, f, NULL, NULL);
|
||
|
}
|
||
|
IGNORE_RACES_END ();
|
||
|
}
|
||
|
|
||
|
void nsync_run_once_arg (nsync_once *once, void (*farg) (void *arg), void *arg) {
|
||
|
uint32_t o;
|
||
|
IGNORE_RACES_START ();
|
||
|
o = ATM_LOAD_ACQ (once);
|
||
|
if (o != 2) {
|
||
|
struct once_sync_s *s = NSYNC_ONCE_SYNC_ (once);
|
||
|
nsync_run_once_impl (once, s, NULL, farg, arg);
|
||
|
}
|
||
|
IGNORE_RACES_END ();
|
||
|
}
|
||
|
|
||
|
void nsync_run_once_spin (nsync_once *once, void (*f) (void)) {
|
||
|
uint32_t o;
|
||
|
IGNORE_RACES_START ();
|
||
|
o = ATM_LOAD_ACQ (once);
|
||
|
if (o != 2) {
|
||
|
nsync_run_once_impl (once, NULL, f, NULL, NULL);
|
||
|
}
|
||
|
IGNORE_RACES_END ();
|
||
|
}
|
||
|
|
||
|
void nsync_run_once_arg_spin (nsync_once *once, void (*farg) (void *arg), void *arg) {
|
||
|
uint32_t o;
|
||
|
IGNORE_RACES_START ();
|
||
|
o = ATM_LOAD_ACQ (once);
|
||
|
if (o != 2) {
|
||
|
nsync_run_once_impl (once, NULL, NULL, farg, arg);
|
||
|
}
|
||
|
IGNORE_RACES_END ();
|
||
|
}
|
||
|
|
||
|
NSYNC_CPP_END_
|