mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-22 06:57:36 +08:00
b0cb9f85ca
The _REENT_GLOBAL_STDIO_STREAMS was introduced by commit 668a4c8722090fffd10869dbb15b879651c1370d in 2017. Since then it was enabled by default for RTEMS. Recently, the option was enabled for Cygwin which previously used an alternative implementation to use global stdio streams. In Newlib, the stdio streams are defined to thread-specific pointers _reent::_stdin, _reent::_stdout and _reent::_stderr. If the option is disabled (the default for most systems), then these pointers are initialized to thread-specific FILE objects which use file descriptors 0, 1, and 2, respectively. There are at least three problems with this: (1) The thread-specific FILE objects are closed by _reclaim_reent(). This leads to problems with language run-time libraries that provide wrappers to the C/POSIX stdio streams (for example C++ and Ada), since they use the thread-specific FILE objects of the initialization thread. In case the initialization thread is deleted, then they use freed memory. (2) Since thread-specific FILE objects are used with a common output device via file descriptors 0, 1 and 2, the locking at FILE object level cannot ensure atomicity of the output, e.g. a call to printf(). (3) There are resource managment issues, see: https://sourceware.org/pipermail/newlib/2022/019558.html https://bugs.linaro.org/show_bug.cgi?id=5841 This patch enables the _REENT_GLOBAL_STDIO_STREAMS behaviour for all Newlib configurations and removes the option. This removes a couple of #ifdef blocks.
301 lines
6.8 KiB
C
301 lines
6.8 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 _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 ();
|
|
d->_errno = 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 (ptr->_stdin != &__sf[0])
|
|
CLEANUP_FILE (ptr, ptr->_stdin);
|
|
if (ptr->_stdout != &__sf[1])
|
|
CLEANUP_FILE (ptr, ptr->_stdout);
|
|
if (ptr->_stderr != &__sf[2])
|
|
CLEANUP_FILE (ptr, ptr->_stderr);
|
|
}
|
|
|
|
/*
|
|
* __sinit() is called whenever stdio's internal variables must be set up.
|
|
*/
|
|
|
|
void
|
|
__sinit (struct _reent *s)
|
|
{
|
|
__sfp_lock_acquire ();
|
|
|
|
if (s->__cleanup)
|
|
{
|
|
__sfp_lock_release ();
|
|
return;
|
|
}
|
|
|
|
/* make sure we clean up on exit */
|
|
s->__cleanup = 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
|