--- findfp.c.orig 2011-02-15 16:29:38.000000000 -0800 +++ findfp.c 2011-02-16 10:36:00.000000000 -0800 @@ -36,13 +36,18 @@ static char sccsid[] = "@(#)findfp.c 8.2 #include <sys/cdefs.h> __FBSDID("$FreeBSD: src/lib/libc/stdio/findfp.c,v 1.34 2009/12/05 19:31:38 ed Exp $"); +#include <TargetConditionals.h> + #include <sys/param.h> #include <machine/atomic.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <libkern/OSAtomic.h> +#include <errno.h> +#include <pthread.h> #include <spinlock.h> #include "libc_private.h" @@ -51,7 +56,11 @@ __FBSDID("$FreeBSD: src/lib/libc/stdio/f int __sdidinit; +#if !TARGET_OS_EMBEDDED #define NDYNAMIC 10 /* add ten more whenever necessary */ +#else +#define NDYNAMIC 1 /* add one at a time on embedded */ +#endif #define std(flags, file) { \ ._flags = (flags), \ @@ -61,12 +70,32 @@ int __sdidinit; ._read = __sread, \ ._seek = __sseek, \ ._write = __swrite, \ + ._extra = __sFX + file, \ } +#define __sFXInit {.fl_mutex = PTHREAD_MUTEX_INITIALIZER} + /* set counted */ +#define __sFXInit3 {.fl_mutex = PTHREAD_MUTEX_INITIALIZER, .counted = 1} + +static int __scounted; /* streams counted against STREAM_MAX */ +static int __stream_max; + +#if !TARGET_OS_EMBEDDED +/* usual and usual_extra are data pigs. See 7929728. For embedded we should + * always allocate dynamically, and probably should for desktop too. */ /* the usual - (stdin + stdout + stderr) */ static FILE usual[FOPEN_MAX - 3]; +static struct __sFILEX usual_extra[FOPEN_MAX - 3]; static struct glue uglue = { NULL, FOPEN_MAX - 3, usual }; +#endif /* !TARGET_OS_EMBEDDED */ -static FILE __sF[3] = { +static struct __sFILEX __sFX[3] = {__sFXInit3, __sFXInit3, __sFXInit3}; + +/* + * We can't make this 'static' due to binary compatibility concerns. + * This also means we cannot change the sizeof(FILE) and must continue to + * use the __sFILEX stuff to add to FILE. + */ +FILE __sF[3] = { std(__SRD, STDIN_FILENO), std(__SWR, STDOUT_FILENO), std(__SWR|__SNBF, STDERR_FILENO) @@ -76,8 +105,13 @@ FILE *__stdinp = &__sF[0]; FILE *__stdoutp = &__sF[1]; FILE *__stderrp = &__sF[2]; +#if !TARGET_OS_EMBEDDED struct glue __sglue = { &uglue, 3, __sF }; static struct glue *lastglue = &uglue; +#else +struct glue __sglue = { NULL, 3, __sF }; +static struct glue *lastglue = &__sglue; +#endif static struct glue * moreglue(int); @@ -98,16 +132,25 @@ moreglue(n) struct glue *g; static FILE empty; FILE *p; + static struct __sFILEX emptyx = __sFXInit; + struct __sFILEX *fx; - g = (struct glue *)malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(FILE)); + g = (struct glue *)malloc(sizeof(*g) + ALIGNBYTES + n * sizeof(FILE) + + n * sizeof(struct __sFILEX)); if (g == NULL) return (NULL); p = (FILE *)ALIGN(g + 1); + fx = (struct __sFILEX *)&p[n]; g->next = NULL; g->niobs = n; g->iobs = p; - while (--n >= 0) - *p++ = empty; + + while (--n >= 0) { + *p = empty; + p->_extra = fx; + *p->_extra = emptyx; + p++, fx++; + } return (g); } @@ -115,7 +158,7 @@ moreglue(n) * Find a free FILE for fopen et al. */ FILE * -__sfp() +__sfp(int count) { FILE *fp; int n; @@ -123,6 +166,15 @@ __sfp() if (!__sdidinit) __sinit(); + + if (count) { + if (__scounted >= __stream_max) { + THREAD_UNLOCK(); + errno = EMFILE; + return NULL; + } + OSAtomicIncrement32(&__scounted); + } /* * The list must be locked because a FILE may be updated. */ @@ -155,12 +207,25 @@ found: fp->_lb._base = NULL; /* no line buffer */ fp->_lb._size = 0; /* fp->_lock = NULL; */ /* once set always set (reused) */ - fp->_orientation = 0; - memset(&fp->_mbstate, 0, sizeof(mbstate_t)); + INITEXTRA(fp); + fp->_extra->counted = count ? 1 : 0; return (fp); } /* + * Mark as free and update count as needed + */ +__private_extern__ void +__sfprelease(FILE *fp) +{ + if (fp->_counted) { + OSAtomicDecrement32(&__scounted); + fp->_counted = 0; + } + fp->_flags = 0; +} + +/* * XXX. Force immediate allocation of internal memory. Not used by stdio, * but documented historically for certain applications. Bad applications. */ @@ -209,8 +274,25 @@ _cleanup() void __sinit() { + THREAD_LOCK(); + if (__sdidinit == 0) { +#if !TARGET_OS_EMBEDDED + int i; +#endif + /* Make sure we clean up on exit. */ + __cleanup = _cleanup; /* conservative */ + __stream_max = sysconf(_SC_STREAM_MAX); + __scounted = 3; /* std{in,out,err} already exists */ + +#if !TARGET_OS_EMBEDDED + /* Set _extra for the usual suspects. */ + for (i = 0; i < FOPEN_MAX - 3; i++) { + usual[i]._extra = &usual_extra[i]; + INITEXTRA(&usual[i]); + } +#endif - /* Make sure we clean up on exit. */ - __cleanup = _cleanup; /* conservative */ - __sdidinit = 1; + __sdidinit = 1; + } + THREAD_UNLOCK(); }