From: Rich Felker <dalias@libc.org>
To: musl@lists.openwall.com
Subject: First draft of new synccall, seems to be working
Date: Wed, 14 Jan 2015 23:33:15 -0500 [thread overview]
Message-ID: <20150115043315.GA12039@brightrain.aerifal.cx> (raw)
[-- Attachment #1: Type: text/plain, Size: 820 bytes --]
I just wrote the first draft of the new __synccall, used for
multithreaded set*id(). All the concepts seem sound and seem to be
working. It's missing the code to block creation of unboundedly more
threads while __synccall is running, but otherwise everything's in
place. Both the /proc/self/task processing logic and the PI futex
logic seem to be working as expected. I haven't yet tested in the
presence of multiple realtime priorities where there's a risk of some
threads never getting a chance to run; I need a framework with a
high-RT-prio supervisor process to kill the test if it runs too long,
because I don't want to be rebooting boxes over and over to test. :)
Attached is the current draft of the code, as a source file rather
than a patch since the whole file is basically rewritten. Comments
welcome.
Rich
[-- Attachment #2: synccall.c --]
[-- Type: text/plain, Size: 3767 bytes --]
#include "pthread_impl.h"
#include <semaphore.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <ctype.h>
#include "futex.h"
#include <../dirent/__dirent.h>
static struct chain {
struct chain *next;
int tid;
sem_t start_sem, next_sem, finish_sem;
} *head;
static int synccall_lock[2];
static int data_lock[2];
static int cur_tid;
static void (*callback)(void *), *context;
static void handler(int sig)
{
struct chain ch;
int old_errno = errno;
sem_init(&ch.start_sem, 0, 0);
sem_init(&ch.next_sem, 0, 0);
sem_init(&ch.finish_sem, 0, 0);
LOCK(data_lock);
ch.tid = __syscall(SYS_gettid);
ch.next = head;
head = &ch;
UNLOCK(data_lock);
if (a_cas(&cur_tid, ch.tid, 0) == (ch.tid | 0x80000000))
__syscall(SYS_futex, &cur_tid, FUTEX_UNLOCK_PI|FUTEX_PRIVATE);
sem_wait(&ch.start_sem);
callback(context);
sem_post(&ch.next_sem);
sem_wait(&ch.finish_sem);
errno = old_errno;
}
static int is_member(struct chain *p, int tid)
{
while (p) {
if (p->tid == tid) return 1;
p = p->next;
}
return 0;
}
void __synccall(void (*func)(void *), void *ctx)
{
sigset_t oldmask;
int cs;
int pid, self;
int i;
DIR dir = {0};
struct dirent *de;
struct sigaction sa = { .sa_flags = 0, .sa_handler = handler };
struct chain *cp, *next;
struct timespec ts;
__block_all_sigs(&oldmask);
LOCK(synccall_lock);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
LOCK(data_lock);
head = 0;
callback = func;
context = ctx;
UNLOCK(data_lock);
if (!libc.threaded) goto single_threaded;
/* Block even impl-internal signals */
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) continue;
LOCK(data_lock);
if (is_member(head, tid)) {
UNLOCK(data_lock);
continue;
}
UNLOCK(data_lock);
__syscall(SYS_tgkill, pid, tid, SIGSYNCCALL);
/* 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;
}
a_store(&cur_tid, tid);
__syscall(SYS_futex, &cur_tid, FUTEX_LOCK_PI|FUTEX_PRIVATE, 0, &ts);
LOCK(data_lock);
if (cur_tid != self && !is_member(head, tid))
miss_cnt++;
UNLOCK(data_lock);
}
if (!miss_cnt) break;
rewinddir(&dir);
}
close(dir.fd);
for (cp=head; cp; cp=next) {
next = cp->next;
sem_post(&cp->start_sem);
sem_wait(&cp->next_sem);
}
sa.sa_handler = SIG_IGN;
__libc_sigaction(SIGSYNCCALL, &sa, 0);
single_threaded:
func(ctx);
for (cp=head; cp; cp=next) {
next = cp->next;
sem_post(&cp->finish_sem);
}
out:
pthread_setcancelstate(cs, 0);
UNLOCK(synccall_lock);
__restore_sigs(&oldmask);
}
next reply other threads:[~2015-01-15 4:33 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-01-15 4:33 Rich Felker [this message]
2015-01-15 12:52 ` Rich Felker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150115043315.GA12039@brightrain.aerifal.cx \
--to=dalias@libc.org \
--cc=musl@lists.openwall.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/musl/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).