diff options
author | 2003-02-04 22:14:27 +0000 | |
---|---|---|
committer | 2003-02-04 22:14:27 +0000 | |
commit | 2c58a6ed58ae116f56ea454eedb1c1dc9677b218 (patch) | |
tree | edb66ad54861bace0403d129698fe36a9cb02150 /lib/libpthread/uthread/uthread_fd.c | |
parent | Add test to compare threaded vs non-threaded fd handling. (diff) | |
download | wireguard-openbsd-2c58a6ed58ae116f56ea454eedb1c1dc9677b218.tar.xz wireguard-openbsd-2c58a6ed58ae116f56ea454eedb1c1dc9677b218.zip |
Part 1 of thread fd handling fixes. In the new scheme fd_table_entries
for dup-ed fds are shared to ensure proper flag handling. A refcnt
was added to control when entries should be freed. Specific changes:
close: don't free entry unless refcnt is zero
dup: rewrite to use new function _thread_fd_table_dup
dup2: rewrite to use new function _thread_fd_table_dup
fcntl: use _thread_fd_table_dup
uthread_fd: initialize thread fd table, searching for dup-ed fds. Add
function to share _thread_fd_table entries when an fd is dup-ed.
uthread_init: make it readable. Call fd init functions.
All current regression tests plus the mysql torture test pass. The
new stdfiles regression test fails (I/O redirection problem). Part
2 is intended to fix that problem
Diffstat (limited to 'lib/libpthread/uthread/uthread_fd.c')
-rw-r--r-- | lib/libpthread/uthread/uthread_fd.c | 148 |
1 files changed, 127 insertions, 21 deletions
diff --git a/lib/libpthread/uthread/uthread_fd.c b/lib/libpthread/uthread/uthread_fd.c index 5a998fbba7e..f583fe04067 100644 --- a/lib/libpthread/uthread/uthread_fd.c +++ b/lib/libpthread/uthread/uthread_fd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_fd.c,v 1.16 2003/01/19 21:22:31 marc Exp $ */ +/* $OpenBSD: uthread_fd.c,v 1.17 2003/02/04 22:14:27 marc Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -45,6 +45,97 @@ static spinlock_t fd_table_lock = _SPINLOCK_INITIALIZER; /* + * Build a new fd entry and return it. + */ +static struct fd_table_entry * +_thread_fd_entry(void) +{ + struct fd_table_entry *entry; + + entry = (struct fd_table_entry *) malloc(sizeof(struct fd_table_entry)); + if (entry != NULL) { + memset(entry, 0, sizeof *entry); + _SPINLOCK_INIT(&entry->lock); + TAILQ_INIT(&entry->r_queue); + TAILQ_INIT(&entry->w_queue); + } + return entry; +} + +/* + * Initialize the thread fd table for dup-ed fds, typically the stdio + * fds. + */ + +void +_thread_fd_init(void) +{ + int saved_errno; + int fd; + int fd2; + int flag; + int *flags; + struct fd_table_entry *entry; + + saved_errno = errno; + flags = calloc(_thread_dtablesize, sizeof *flags); + if (flags == NULL) + PANIC("Cannot allocate memory for flags table"); + + + /* read the current file flags */ + for (fd = 0; fd < _thread_dtablesize; fd += 1) + flags[fd] = _thread_sys_fcntl(fd, F_GETFL, 0); + + /* + * Now toggle the non-block flags and see what other fd's + * change. Those are the dup-ed fd's. Dup-ed fd's are + * added to the table, all others are NOT added to the + * table. They MUST NOT be added as the fds may belong + * to dlopen and dlclose doesn't go through the thread code + * so the entries would never be cleaned. + */ + + _SPINLOCK(&fd_table_lock); + for (fd = 0; fd < _thread_dtablesize; fd += 1) { + if (flags[fd] == -1) + continue; + entry = _thread_fd_entry(); + if (entry != NULL) { + entry->flags = flags[fd]; + _thread_sys_fcntl(fd, F_SETFL, + entry->flags ^ O_NONBLOCK); + for (fd2 = fd + 1; fd2 < _thread_dtablesize; fd2 += 1) { + if (flags[fd2] == -1) + continue; + flag = _thread_sys_fcntl(fd2, F_GETFL, 0); + if (flag != flags[fd2]) { + entry->refcnt += 1; + _thread_fd_table[fd2] = entry; + flags[fd2] = -1; + } + } + if (entry->refcnt) { + entry->refcnt += 1; + _thread_fd_table[fd] = entry; + } else + free(entry); + } + } + _SPINUNLOCK(&fd_table_lock); + + /* lastly, set all files to non-blocking, ignoring errors for + those files/devices that don't support such a mode. */ + for (fd = 0; fd < _thread_dtablesize; fd += 1) + if (flags[fd] != -1) + _thread_sys_fcntl(fd, F_SETFL, + flags[fd] | O_NONBLOCK); + + free(flags); + errno = saved_errno; +} + +/* * Initialize the fd_table entry for the given fd. * * This function *must* return -1 and set the thread specific errno @@ -68,28 +159,11 @@ _thread_fd_table_init(int fd) ret = -1; } else if (_thread_fd_table[fd] == NULL) { /* First time for this fd, build an entry */ - entry = (struct fd_table_entry *) - malloc(sizeof(struct fd_table_entry)); + entry = _thread_fd_entry(); if (entry == NULL) { errno = ENOMEM; ret = -1; } else { - /* Initialise the file locks: */ - _SPINLOCK_INIT(&entry->lock); - entry->r_owner = NULL; - entry->w_owner = NULL; - entry->r_fname = NULL; - entry->w_fname = NULL; - entry->r_lineno = 0; - entry->w_lineno = 0; - entry->r_lockcount = 0; - entry->w_lockcount = 0; - - /* Initialise the read/write queues: */ - TAILQ_INIT(&entry->r_queue); - TAILQ_INIT(&entry->w_queue); - - /* Get the flags for the file: */ entry->flags = _thread_sys_fcntl(fd, F_GETFL, 0); if (entry->flags == -1) /* use the errno fcntl returned */ @@ -118,8 +192,8 @@ _thread_fd_table_init(int fd) */ if (_thread_fd_table[fd] == NULL) { /* This thread wins: */ + entry->refcnt += 1; _thread_fd_table[fd] = entry; - entry = NULL; } /* Unlock the file descriptor table: */ @@ -131,7 +205,7 @@ _thread_fd_table_init(int fd) * the file or if another thread initialized the * table entry throw this entry away. */ - if (entry != NULL) + if (entry->refcnt == 0) free(entry); } } @@ -141,6 +215,38 @@ _thread_fd_table_init(int fd) } /* + * Dup from_fd -> to_fd. from_fd is assumed to be locked (which + * guarantees that _thread_fd_table[from_fd] exists). + */ +int +_thread_fd_table_dup(int from_fd, int to_fd) +{ + struct fd_table_entry *entry; + int ret; + + /* release any existing to_fd table entry */ + entry = _thread_fd_table[to_fd]; + if (entry != NULL) { + ret = _FD_LOCK(to_fd, FD_RDWR, NULL); + if (ret != -1) { + if (--entry->refcnt == 0) + free(entry); + } + } else + ret = 0; + + /* to_fd is a copy of from_fd */ + if (ret != -1) { + _SPINLOCK(&fd_table_lock); + _thread_fd_table[to_fd] = _thread_fd_table[from_fd]; + _thread_fd_table[to_fd]->refcnt += 1; + _SPINUNLOCK(&fd_table_lock); + } + + return (ret); +} + +/* * Unlock the fd table entry for a given thread, fd, and lock type. */ void |