4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-01-22 23:17:28 +08:00
Matt Joyce ea99f21ce6 Add --enable-newlib-reent-thread-local option
By default, Newlib uses a huge object of type struct _reent to store
thread-specific data.  This object is returned by __getreent() if the
__DYNAMIC_REENT__ Newlib configuration option is defined.

The reentrancy structure contains for example errno and the standard input,
output, and error file streams.  This means that if an application only uses
errno it has a dependency on the file stream support even if it does not use
it.  This is an issue for lower end targets and applications which need to
qualify the software according to safety standards (for example ECSS-E-ST-40C,
ECSS-Q-ST-80C, IEC 61508, ISO 26262, DO-178, DO-330, DO-333).

If the new _REENT_THREAD_LOCAL configuration option is enabled, then struct
_reent is replaced by dedicated thread-local objects for each struct _reent
member.  The thread-local objects are defined in translation units which use
the corresponding object.
2022-07-13 06:55:46 +02:00

308 lines
7.0 KiB
C

/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* and/or other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/* No user fns here. Pesch 15apr92. */
#include <_ansi.h>
#include <reent.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/lock.h>
#include "local.h"
void (*__stdio_exit_handler) (void);
__FILE __sf[3];
struct _glue __sglue = {NULL, 3, &__sf[0]};
#ifdef _REENT_THREAD_LOCAL
_Thread_local __FILE *_tls_stdin = &__sf[0];
_Thread_local __FILE *_tls_stdout = &__sf[1];
_Thread_local __FILE *_tls_stderr = &__sf[2];
_Thread_local void (*_tls_cleanup)(struct _reent *);
#endif
#ifdef _STDIO_BSD_SEMANTICS
/* BSD and Glibc systems only flush streams which have been written to
at exit time. Calling flush rather than close for speed, as on
the aforementioned systems. */
#define CLEANUP_FILE __sflushw_r
#else
/* Otherwise close files and flush read streams, too.
Note we call flush directly if "--enable-lite-exit" is in effect. */
#ifdef _LITE_EXIT
#define CLEANUP_FILE _fflush_r
#else
#define CLEANUP_FILE _fclose_r
#endif
#endif
#if (defined (__OPTIMIZE_SIZE__) || defined (PREFER_SIZE_OVER_SPEED))
_NOINLINE_STATIC void
#else
static void
#endif
std (FILE *ptr,
int flags,
int file)
{
ptr->_p = 0;
ptr->_r = 0;
ptr->_w = 0;
ptr->_flags = flags;
ptr->_flags2 = 0;
ptr->_file = file;
ptr->_bf._base = 0;
ptr->_bf._size = 0;
ptr->_lbfsize = 0;
memset (&ptr->_mbstate, 0, sizeof (_mbstate_t));
ptr->_cookie = ptr;
ptr->_read = __sread;
#ifndef __LARGE64_FILES
ptr->_write = __swrite;
#else /* __LARGE64_FILES */
ptr->_write = __swrite64;
ptr->_seek64 = __sseek64;
ptr->_flags |= __SL64;
#endif /* __LARGE64_FILES */
ptr->_seek = __sseek;
#ifdef _STDIO_CLOSE_PER_REENT_STD_STREAMS
ptr->_close = __sclose;
#else /* _STDIO_CLOSE_STD_STREAMS */
ptr->_close = NULL;
#endif /* _STDIO_CLOSE_STD_STREAMS */
#ifdef __SCLE
if (__stextmode (ptr->_file))
ptr->_flags |= __SCLE;
#endif
}
static inline void
stdin_init(FILE *ptr)
{
std (ptr, __SRD, 0);
}
static inline void
stdout_init(FILE *ptr)
{
/* On platforms that have true file system I/O, we can verify
whether stdout is an interactive terminal or not, as part of
__smakebuf on first use of the stream. For all other platforms,
we will default to line buffered mode here. Technically, POSIX
requires both stdin and stdout to be line-buffered, but tradition
leaves stdin alone on systems without fcntl. */
#ifdef HAVE_FCNTL
std (ptr, __SWR, 1);
#else
std (ptr, __SWR | __SLBF, 1);
#endif
}
static inline void
stderr_init(FILE *ptr)
{
/* POSIX requires stderr to be opened for reading and writing, even
when the underlying fd 2 is write-only. */
std (ptr, __SRW | __SNBF, 2);
}
struct glue_with_file {
struct _glue glue;
FILE file;
};
static struct _glue *
sfmoreglue (struct _reent *d, int n)
{
struct glue_with_file *g;
g = (struct glue_with_file *)
_malloc_r (d, sizeof (*g) + (n - 1) * sizeof (FILE));
if (g == NULL)
return NULL;
g->glue._next = NULL;
g->glue._niobs = n;
g->glue._iobs = &g->file;
memset (&g->file, 0, n * sizeof (FILE));
return &g->glue;
}
static void
stdio_exit_handler (void)
{
(void) _fwalk_sglue (_GLOBAL_REENT, CLEANUP_FILE, &__sglue);
}
static void
global_stdio_init (void)
{
if (__stdio_exit_handler == NULL) {
__stdio_exit_handler = stdio_exit_handler;
stdin_init (&__sf[0]);
stdout_init (&__sf[1]);
stderr_init (&__sf[2]);
}
}
/*
* Find a free FILE for fopen et al.
*/
FILE *
__sfp (struct _reent *d)
{
FILE *fp;
int n;
struct _glue *g;
_newlib_sfp_lock_start ();
global_stdio_init ();
for (g = &__sglue;; g = g->_next)
{
for (fp = g->_iobs, n = g->_niobs; --n >= 0; fp++)
if (fp->_flags == 0)
goto found;
if (g->_next == NULL &&
(g->_next = sfmoreglue (d, NDYNAMIC)) == NULL)
break;
}
_newlib_sfp_lock_exit ();
_REENT_ERRNO(d) = ENOMEM;
return NULL;
found:
fp->_file = -1; /* no file */
fp->_flags = 1; /* reserve this slot; caller sets real flags */
fp->_flags2 = 0;
#ifndef __SINGLE_THREAD__
__lock_init_recursive (fp->_lock);
#endif
_newlib_sfp_lock_end ();
fp->_p = NULL; /* no current pointer */
fp->_w = 0; /* nothing to read or write */
fp->_r = 0;
fp->_bf._base = NULL; /* no buffer */
fp->_bf._size = 0;
fp->_lbfsize = 0; /* not line buffered */
memset (&fp->_mbstate, 0, sizeof (_mbstate_t));
/* fp->_cookie = <any>; */ /* caller sets cookie, _read/_write etc */
fp->_ub._base = NULL; /* no ungetc buffer */
fp->_ub._size = 0;
fp->_lb._base = NULL; /* no line buffer */
fp->_lb._size = 0;
return fp;
}
/*
* exit() calls _cleanup() through *__cleanup, set whenever we
* open or buffer a file. This chicanery is done so that programs
* that do not use stdio need not link it all in.
*
* The name `_cleanup' is, alas, fairly well known outside stdio.
*/
static void
cleanup_stdio (struct _reent *ptr)
{
if (_REENT_STDIN(ptr) != &__sf[0])
CLEANUP_FILE (ptr, _REENT_STDIN(ptr));
if (_REENT_STDOUT(ptr) != &__sf[1])
CLEANUP_FILE (ptr, _REENT_STDOUT(ptr));
if (_REENT_STDERR(ptr) != &__sf[2])
CLEANUP_FILE (ptr, _REENT_STDERR(ptr));
}
/*
* __sinit() is called whenever stdio's internal variables must be set up.
*/
void
__sinit (struct _reent *s)
{
__sfp_lock_acquire ();
if (_REENT_CLEANUP(s))
{
__sfp_lock_release ();
return;
}
/* make sure we clean up on exit */
_REENT_CLEANUP(s) = cleanup_stdio; /* conservative */
global_stdio_init ();
__sfp_lock_release ();
}
#ifndef __SINGLE_THREAD__
__LOCK_INIT_RECURSIVE(static, __sfp_recursive_mutex);
void
__sfp_lock_acquire (void)
{
__lock_acquire_recursive (__sfp_recursive_mutex);
}
void
__sfp_lock_release (void)
{
__lock_release_recursive (__sfp_recursive_mutex);
}
/* Walkable file locking routine. */
static int
__fp_lock (struct _reent * ptr __unused, FILE * fp)
{
if (!(fp->_flags2 & __SNLK))
_flockfile (fp);
return 0;
}
/* Walkable file unlocking routine. */
static int
__fp_unlock (struct _reent * ptr __unused, FILE * fp)
{
if (!(fp->_flags2 & __SNLK))
_funlockfile (fp);
return 0;
}
void
__fp_lock_all (void)
{
__sfp_lock_acquire ();
(void) _fwalk_sglue (NULL, __fp_lock, &__sglue);
}
void
__fp_unlock_all (void)
{
(void) _fwalk_sglue (NULL, __fp_unlock, &__sglue);
__sfp_lock_release ();
}
#endif