Exemple #1
0
int pthread_once(pthread_once_t *control, void (*init)(void))
{
	static int waiters;

	/* Return immediately if init finished before */
	if (*control == 2) return 0;

	/* Try to enter initializing state. Three possibilities:
	 *  0 - we're the first or the other cancelled; run init
	 *  1 - another thread is running init; wait
	 *  2 - another thread finished running init; just return */

	for (;;) switch (a_swap(control, 1)) {
	case 0:
		pthread_cleanup_push(undo, control);
		init();
		pthread_cleanup_pop(0);

		a_store(control, 2);
		if (waiters) __wake(control, -1, 0);
		return 0;
	case 1:
		__wait(control, &waiters, 1, 0);
		continue;
	case 2:
		a_store(control, 2);
		return 0;
	}
}
int pthread_barrier_wait(pthread_barrier_t *b)
{
	int limit = b->_b_limit;
	struct instance *inst;

	/* Trivial case: count was set at 1 */
	if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;

	/* Process-shared barriers require a separate, inefficient wait */
	if (limit < 0) return pshared_barrier_wait(b);

	/* Otherwise we need a lock on the barrier object */
	while (a_swap(&b->_b_lock, 1))
		__wait(&b->_b_lock, &b->_b_waiters, 1, 1);
	inst = b->_b_inst;

	/* First thread to enter the barrier becomes the "instance owner" */
	if (!inst) {
		struct instance new_inst = { 0 };
		int spins = 10000;
		b->_b_inst = inst = &new_inst;
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		while (spins-- && !inst->finished)
			a_spin();
		a_inc(&inst->finished);
		while (inst->finished == 1)
			__syscall(SYS_futex, &inst->finished, FUTEX_WAIT,1,0);
		return PTHREAD_BARRIER_SERIAL_THREAD;
	}

	/* Last thread to enter the barrier wakes all non-instance-owners */
	if (++inst->count == limit) {
		b->_b_inst = 0;
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		a_store(&inst->last, 1);
		if (inst->waiters)
			__wake(&inst->last, -1, 1);
	} else {
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		__wait(&inst->last, &inst->waiters, 0, 1);
	}

	/* Last thread to exit the barrier wakes the instance owner */
	if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
		__wake(&inst->finished, 1, 1);

	return 0;
}
Exemple #3
0
char *bindtextdomain(const char *domainname, const char *dirname)
{
	static int lock[2];
	struct binding *p, *q;

	if (!domainname) return 0;
	if (!dirname) return gettextdir(domainname, &(size_t){0});

	size_t domlen = strlen(domainname);
	size_t dirlen = strlen(dirname);
	if (domlen > NAME_MAX || dirlen >= PATH_MAX) {
		errno = EINVAL;
		return 0;
	}

	LOCK(lock);

	for (p=bindings; p; p=p->next) {
		if (!strcmp(p->domainname, domainname) &&
		    !strcmp(p->dirname, dirname)) {
			break;
		}
	}

	if (!p) {
		p = malloc(sizeof *p + domlen + dirlen + 2);
		if (!p) {
			UNLOCK(lock);
			return 0;
		}
		p->next = bindings;
		p->dirlen = dirlen;
		p->domainname = p->buf;
		p->dirname = p->buf + domlen + 1;
		memcpy(p->domainname, domainname, domlen+1);
		memcpy(p->dirname, dirname, dirlen+1);
		a_cas_p(&bindings, bindings, p);
	}

	a_store(&p->active, 1);

	for (q=bindings; q; q=q->next) {
		if (!strcmp(p->domainname, domainname) && q != p)
			a_store(&q->active, 0);
	}

	UNLOCK(lock);
	
	return (char *)p->dirname;
}
static int pshared_barrier_wait(pthread_barrier_t *b)
{
	int limit = (b->_b_limit & INT_MAX) + 1;
	int ret = 0;
	int v, w;

	if (limit==1) return PTHREAD_BARRIER_SERIAL_THREAD;

	while ((v=a_cas(&b->_b_lock, 0, limit)))
		__wait(&b->_b_lock, &b->_b_waiters, v, 0);

	/* Wait for <limit> threads to get to the barrier */
	if (++b->_b_count == limit) {
		a_store(&b->_b_count, 0);
		ret = PTHREAD_BARRIER_SERIAL_THREAD;
		if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
	} else {
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 0);
		while ((v=b->_b_count)>0)
			__wait(&b->_b_count, &b->_b_waiters2, v, 0);
	}

	__vm_lock_impl(+1);

	/* Ensure all threads have a vm lock before proceeding */
	if (a_fetch_add(&b->_b_count, -1)==1-limit) {
		a_store(&b->_b_count, 0);
		if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
	} else {
		while ((v=b->_b_count))
			__wait(&b->_b_count, &b->_b_waiters2, v, 0);
	}
	
	/* Perform a recursive unlock suitable for self-sync'd destruction */
	do {
		v = b->_b_lock;
		w = b->_b_waiters;
	} while (a_cas(&b->_b_lock, v, v==INT_MIN+1 ? 0 : v-1) != v);

	/* Wake a thread waiting to reuse or destroy the barrier */
	if (v==INT_MIN+1 || (v==1 && w))
		__wake(&b->_b_lock, 1, 0);

	__vm_unlock_impl();

	return ret;
}
Exemple #5
0
int __pthread_once(pthread_once_t *control, void (*init)(void))
{
	static int waiters;

	/* Return immediately if init finished before, but ensure that
	 * effects of the init routine are visible to the caller. */
	if (*control == 2) {
		a_barrier();
		return 0;
	}

	/* Try to enter initializing state. Three possibilities:
	 *  0 - we're the first or the other cancelled; run init
	 *  1 - another thread is running init; wait
	 *  2 - another thread finished running init; just return */

	for (;;) switch (a_cas(control, 0, 1)) {
	case 0:
		pthread_cleanup_push(undo, control);
		init();
		pthread_cleanup_pop(0);

		a_store(control, 2);
		if (waiters) __wake(control, -1, 1);
		return 0;
	case 1:
		__wait(control, &waiters, 1, 1);
		continue;
	case 2:
		return 0;
	}
}
Exemple #6
0
static inline void unlock(volatile int *lk)
{
	if (lk[0]) {
		a_store(lk, 0);
		if (lk[1]) __wake(lk, 1, 1);
	}
}
Exemple #7
0
void jflib::pool<T, CV>::side::signal(bool close) {
  if(a_load(state_) != NONE || close) {
    cond_.lock();
    a_store(state_, close ? CLOSED : NONE);
    cond_.broadcast();
    cond_.unlock();
  }
}
Exemple #8
0
int timer_delete(timer_t t) {
  if ((intptr_t)t < 0) {
    pthread_t td = (void*)((uintptr_t)t << 1);
    a_store(&td->timer_id, td->timer_id | INT_MIN);
    __wake(&td->timer_id, 1, 1);
    return 0;
  }
  return __syscall(SYS_timer_delete, t);
}
int pthread_cond_broadcast(pthread_cond_t *c)
{
	pthread_mutex_t *m;

	if (!c->_c_waiters) return 0;

	a_inc(&c->_c_seq);

#ifdef __EMSCRIPTEN__
	// XXX Emscripten: TODO: This is suboptimal but works naively correctly for now. The Emscripten-specific code path below
	// has a bug and does not work for some reason. Figure it out and remove this code block.
	__wake(&c->_c_seq, -1, 0);
	return 0;
#endif

	/* If cond var is process-shared, simply wake all waiters. */
	if (c->_c_mutex == (void *)-1) {
		__wake(&c->_c_seq, -1, 0);
		return 0;
	}

	/* Block waiters from returning so we can use the mutex. */
	while (a_swap(&c->_c_lock, 1))
		__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
	if (!c->_c_waiters)
		goto out;
	m = c->_c_mutex;

	/* Move waiter count to the mutex */
	a_fetch_add(&m->_m_waiters, c->_c_waiters2);
	c->_c_waiters2 = 0;

#ifdef __EMSCRIPTEN__
	int futexResult;
	do {
		// XXX Emscripten: Bug, this does not work correctly.
		futexResult = emscripten_futex_wake_or_requeue(&c->_c_seq, !m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid,
			c->_c_seq, &m->_m_lock);
	} while(futexResult == -EAGAIN);
#else
	/* Perform the futex requeue, waking one waiter unless we know
	 * that the calling thread holds the mutex. */
	__syscall(SYS_futex, &c->_c_seq, FUTEX_REQUEUE,
		!m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid,
		INT_MAX, &m->_m_lock);
#endif

out:
	a_store(&c->_c_lock, 0);
	if (c->_c_lockwait) __wake(&c->_c_lock, 1, 0);

	return 0;
}
static inline void unlock_requeue(volatile int *l, volatile int *r, int w)
{
	a_store(l, 0);
#ifdef __EMSCRIPTEN__
	// Here the intent is to wake one waiter, and requeue all other waiters from waiting on address 'l'
	// to wait on address 'r' instead. This is not possible at the moment with SharedArrayBuffer Atomics,
	// as it does not have a "wake X waiters and requeue the rest" primitive. However this kind of
	// primitive is strictly not needed, since it is more like an optimization to avoid spuriously waking
	// all waiters, just to make them wait on another location immediately afterwards. Here we do exactly
	// that: wake every waiter.
	emscripten_futex_wake(l, 0x7FFFFFFF);
#else
	if (w) __wake(l, 1, 1);
	else __syscall(SYS_futex, l, FUTEX_REQUEUE|128, 0, 1, r) != -ENOSYS
		|| __syscall(SYS_futex, l, FUTEX_REQUEUE, 0, 1, r);
#endif
}
Exemple #11
0
static void init_cancellation()
{
    struct sigaction sa = {
        .sa_flags = SA_SIGINFO | SA_RESTART,
        .sa_sigaction = cancel_handler
    };
    sigfillset(&sa.sa_mask);
    __libc_sigaction(SIGCANCEL, &sa, 0);
}

int pthread_cancel(pthread_t t)
{
    static int init;
    if (!init) {
        init_cancellation();
        init = 1;
    }
    a_store(&t->cancel, 1);
    return pthread_kill(t, SIGCANCEL);
}
Exemple #12
0
uint32_t jflib::pool<T, CV>::side::get() {
  bool     last_attempt = false;
  uint32_t res          = fifo_.dequeue();
  while(res == cbT::guard) {
    cond_.lock();

    switch(a_load(state_)) {
    case CLOSED:
      if(last_attempt) {
        cond_.unlock();
        return cbT::guard;
      } else {
        last_attempt = true;
        break;
      }
    case NONE:
      a_store(state_, WAITING);
      break;
    case WAITING:
      break;
    }
    res = fifo_.dequeue();
    if(res == cbT::guard) {
      if(last_attempt) {
        cond_.unlock();
        break;
      }
    } else {
      cond_.unlock();
      break;
    }
    do {
      cond_.timedwait(5);
    } while(a_load(state_) == WAITING);
    cond_.unlock();
  }

  return res;
}
int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *ts)
{
    struct cm cm = { .c=c, .m=m };
    int r, e=0, seq;

    if (m->_m_type && (m->_m_lock&INT_MAX) != pthread_self()->tid)
        return EPERM;

    if (ts && ts->tv_nsec >= 1000000000UL)
        return EINVAL;

    pthread_testcancel();

    a_inc(&c->_c_waiters);

    if (c->_c_mutex != (void *)-1) {
        c->_c_mutex = m;
        while (a_swap(&c->_c_lock, 1))
            __wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
        c->_c_waiters2++;
        a_store(&c->_c_lock, 0);
        if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1);
    }

    seq = c->_c_seq;

    pthread_mutex_unlock(m);

    do e = __timedwait(&c->_c_seq, seq, c->_c_clock, ts, cleanup, &cm, 0);
    while (c->_c_seq == seq && (!e || e==EINTR));
    if (e == EINTR) e = 0;

    unwait(c, m);

    if ((r=pthread_mutex_lock(m))) return r;

    return e;
}
static void unwait(pthread_cond_t *c, pthread_mutex_t *m)
{
    /* Removing a waiter is non-trivial if we could be using requeue
     * based broadcast signals, due to mutex access issues, etc. */

    if (c->_c_mutex == (void *)-1) {
        a_dec(&c->_c_waiters);
        if (c->_c_destroy) __wake(&c->_c_waiters, 1, 0);
        return;
    }

    while (a_swap(&c->_c_lock, 1))
        __wait(&c->_c_lock, &c->_c_lockwait, 1, 1);

    if (c->_c_waiters2) c->_c_waiters2--;
    else a_dec(&m->_m_waiters);

    a_store(&c->_c_lock, 0);
    if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1);

    a_dec(&c->_c_waiters);
    if (c->_c_destroy) __wake(&c->_c_waiters, 1, 1);
}
Exemple #15
0
int pthread_cond_broadcast(pthread_cond_t *c)
{
	pthread_mutex_t *m;

	if (!c->_c_waiters) return 0;

	a_inc(&c->_c_seq);

	/* If cond var is process-shared, simply wake all waiters. */
	if (c->_c_mutex == (void *)-1) {
		__wake(&c->_c_seq, -1, 0);
		return 0;
	}

	/* Block waiters from returning so we can use the mutex. */
	while (a_swap(&c->_c_lock, 1))
		__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
	if (!c->_c_waiters)
		goto out;
	m = c->_c_mutex;

	/* Move waiter count to the mutex */
	a_fetch_add(&m->_m_waiters, c->_c_waiters2);
	c->_c_waiters2 = 0;

	/* Perform the futex requeue, waking one waiter unless we know
	 * that the calling thread holds the mutex. */
	__syscall(SYS_futex, &c->_c_seq, FUTEX_REQUEUE,
		!m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid,
		INT_MAX, &m->_m_lock);

out:
	a_store(&c->_c_lock, 0);
	if (c->_c_lockwait) __wake(&c->_c_lock, 1, 0);

	return 0;
}
Exemple #16
0
void __do_orphaned_stdio_locks()
{
	FILE *f;
	for (f=__pthread_self()->stdio_locks; f; f=f->next_locked)
		a_store(&f->lock, 0x40000000);
}
Exemple #17
0
static void unlock(volatile int *lk)
{
	if (!libc.threads_minus_1) return;
	a_store(lk, 0);
	if (lk[1]) __wake(lk, 1, 1);
}
Exemple #18
0
void __unlockfile(FILE *f)
{
	a_store(&f->lock, 0);
	if (f->waiters) __wake(&f->lock, 1, 1);
}
Exemple #19
0
int pthread_spin_unlock(pthread_spinlock_t *s)
{
	a_store(s, 0);
	return 0;
}
Exemple #20
0
static void undo(void *control)
{
	a_store(control, 0);
	__wake(control, 1, 1);
}
void __synccall(void (*func)(void *), void *ctx)
{
	sigset_t oldmask;
	int cs, i, r, pid, self;;
	DIR dir = {0};
	struct dirent *de;
	struct sigaction sa = { .sa_flags = 0, .sa_handler = handler };
	struct chain *cp, *next;
	struct timespec ts;

	/* Blocking signals in two steps, first only app-level signals
	 * before taking the lock, then all signals after taking the lock,
	 * is necessary to achieve AS-safety. Blocking them all first would
	 * deadlock if multiple threads called __synccall. Waiting to block
	 * any until after the lock would allow re-entry in the same thread
	 * with the lock already held. */
	__block_app_sigs(&oldmask);
	LOCK(synccall_lock);
	__block_all_sigs(0);
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);

	head = 0;

	if (!libc.threaded) goto single_threaded;

	callback = func;
	context = ctx;

	/* This atomic store ensures that any signaled threads will see the
	 * above stores, and prevents more than a bounded number of threads,
	 * those already in pthread_create, from creating new threads until
	 * the value is cleared to zero again. */
	a_store(&__block_new_threads, 1);

	/* Block even implementation-internal signals, so that nothing
	 * interrupts the SIGSYNCCALL handlers. The main possible source
	 * of trouble is asynchronous cancellation. */
	memset(&sa.sa_mask, -1, sizeof sa.sa_mask);
	__libc_sigaction(SIGSYNCCALL, &sa, 0);

	pid = __syscall(SYS_getpid);
	self = __syscall(SYS_gettid);

	/* Since opendir is not AS-safe, the DIR needs to be setup manually
	 * in automatic storage. Thankfully this is easy. */
	dir.fd = open("/proc/self/task", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
	if (dir.fd < 0) goto out;

	/* Initially send one signal per counted thread. But since we can't
	 * synchronize with thread creation/exit here, there could be too
	 * few signals. This initial signaling is just an optimization, not
	 * part of the logic. */
	for (i=libc.threads_minus_1; i; i--)
		__syscall(SYS_kill, pid, SIGSYNCCALL);

	/* Loop scanning the kernel-provided thread list until it shows no
	 * threads that have not already replied to the signal. */
	for (;;) {
		int miss_cnt = 0;
		while ((de = readdir(&dir))) {
			if (!isdigit(de->d_name[0])) continue;
			int tid = atoi(de->d_name);
			if (tid == self || !tid) continue;

			/* Set the target thread as the PI futex owner before
			 * checking if it's in the list of caught threads. If it
			 * adds itself to the list after we check for it, then
			 * it will see its own tid in the PI futex and perform
			 * the unlock operation. */
			a_store(&target_tid, tid);

			/* Thread-already-caught is a success condition. */
			for (cp = head; cp && cp->tid != tid; cp=cp->next);
			if (cp) continue;

			r = -__syscall(SYS_tgkill, pid, tid, SIGSYNCCALL);

			/* Target thread exit is a success condition. */
			if (r == ESRCH) continue;

			/* The FUTEX_LOCK_PI operation is used to loan priority
			 * to the target thread, which otherwise may be unable
			 * to run. Timeout is necessary because there is a race
			 * condition where the tid may be reused by a different
			 * process. */
			clock_gettime(CLOCK_REALTIME, &ts);
			ts.tv_nsec += 10000000;
			if (ts.tv_nsec >= 1000000000) {
				ts.tv_sec++;
				ts.tv_nsec -= 1000000000;
			}
			r = -__syscall(SYS_futex, &target_tid,
				FUTEX_LOCK_PI|FUTEX_PRIVATE, 0, &ts);

			/* Obtaining the lock means the thread responded. ESRCH
			 * means the target thread exited, which is okay too. */
			if (!r || r == ESRCH) continue;

			miss_cnt++;
		}
		if (!miss_cnt) break;
		rewinddir(&dir);
	}
	close(dir.fd);

	/* Serialize execution of callback in caught threads. */
	for (cp=head; cp; cp=cp->next) {
		sem_post(&cp->target_sem);
		sem_wait(&cp->caller_sem);
	}

	sa.sa_handler = SIG_IGN;
	__libc_sigaction(SIGSYNCCALL, &sa, 0);

single_threaded:
	func(ctx);

	/* Only release the caught threads once all threads, including the
	 * caller, have returned from the callback function. */
	for (cp=head; cp; cp=next) {
		next = cp->next;
		sem_post(&cp->target_sem);
	}

out:
	a_store(&__block_new_threads, 0);
	__wake(&__block_new_threads, -1, 1);

	pthread_setcancelstate(cs, 0);
	UNLOCK(synccall_lock);
	__restore_sigs(&oldmask);
}
Exemple #22
0
char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category)
{
	static struct msgcat *volatile cats;
	struct msgcat *p;
	struct __locale_struct *loc = CURRENT_LOCALE;
	struct __locale_map *lm;
	const char *dirname, *locname, *catname;
	size_t dirlen, loclen, catlen, domlen;

	if (!domainname) domainname = __gettextdomain();

	domlen = strlen(domainname);
	if (domlen > NAME_MAX) goto notrans;

	dirname = gettextdir(domainname, &dirlen);
	if (!dirname) goto notrans;

	switch (category) {
	case LC_MESSAGES:
		locname = loc->messages_name;
		if (!*locname) goto notrans;
		break;
	case LC_TIME:
	case LC_MONETARY:
	case LC_COLLATE:
		lm = loc->cat[category-2];
		if (!lm) goto notrans;
		locname = lm->name;
		break;
	default:
notrans:
		return (char *) ((n == 1) ? msgid1 : msgid2);
	}

	catname = catnames[category-2];
	catlen = catlens[category-2];
	loclen = strlen(locname);

	size_t namelen = dirlen+1 + loclen+1 + catlen+1 + domlen+3;
	char name[namelen+1], *s = name;

	memcpy(s, dirname, dirlen);
	s[dirlen] = '/';
	s += dirlen + 1;
	memcpy(s, locname, loclen);
	s[loclen] = '/';
	s += loclen + 1;
	memcpy(s, catname, catlen);
	s[catlen] = '/';
	s += catlen + 1;
	memcpy(s, domainname, domlen);
	s[domlen] = '.';
	s[domlen+1] = 'm';
	s[domlen+2] = 'o';
	s[domlen+3] = 0;

	for (p=cats; p; p=p->next)
		if (!strcmp(p->name, name))
			break;

	if (!p) {
		void *old_cats;
		size_t map_size;
		const void *map = __map_file(name, &map_size);
		if (!map) goto notrans;
		p = malloc(sizeof *p + namelen + 1);
		if (!p) {
			__munmap((void *)map, map_size);
			goto notrans;
		}
		p->map = map;
		p->map_size = map_size;
		memcpy(p->name, name, namelen+1);
		do {
			old_cats = cats;
			p->next = old_cats;
		} while (a_cas_p(&cats, old_cats, p) != old_cats);
	}

	const char *trans = __mo_lookup(p->map, p->map_size, msgid1);
	if (!trans) goto notrans;

	/* Non-plural-processing gettext forms pass a null pointer as
	 * msgid2 to request that dcngettext suppress plural processing. */
	if (!msgid2) return (char *)trans;

	if (!p->plural_rule) {
		const char *rule = "n!=1;";
		unsigned long np = 2;
		const char *r = __mo_lookup(p->map, p->map_size, "");
		char *z;
		while (r && strncmp(r, "Plural-Forms:", 13)) {
			z = strchr(r, '\n');
			r = z ? z+1 : 0;
		}
		if (r) {
			r += 13;
			while (isspace(*r)) r++;
			if (!strncmp(r, "nplurals=", 9)) {
				np = strtoul(r+9, &z, 10);
				r = z;
			}
			while (*r && *r != ';') r++;
			if (*r) {
				r++;
				while (isspace(*r)) r++;
				if (!strncmp(r, "plural=", 7))
					rule = r+7;
			}
		}
		a_store(&p->nplurals, np);
		a_cas_p(&p->plural_rule, 0, (void *)rule);
	}
	if (p->nplurals) {
		unsigned long plural = __pleval(p->plural_rule, n);
		if (plural > p->nplurals) goto notrans;
		while (plural--) {
			size_t rem = p->map_size - (trans - (char *)p->map);
			size_t l = strnlen(trans, rem);
			if (l+1 >= rem)
				goto notrans;
			trans += l+1;
		}
	}
	return (char *)trans;
}