+2017-07-04 Szabolcs Nagy <szabolcs.nagy@arm.com>
+
+ * libio/libio.h (_IO_FLAGS2_NEED_LOCK, _IO_need_lock): Define.
+ * libio/libioP.h (_IO_enable_locks): Declare.
+ * libio/Versions (_IO_enable_locks): New symbol.
+ * libio/genops.c (_IO_enable_locks): Define.
+ (_IO_old_init): Initialize flags2.
+ * libio/feof.c.c (_IO_feof): Avoid locking when not needed.
+ * libio/ferror.c (_IO_ferror): Likewise.
+ * libio/fputc.c (fputc): Likewise.
+ * libio/putc.c (_IO_putc): Likewise.
+ * libio/getc.c (_IO_getc): Likewise.
+ * libio/getchar.c (getchar): Likewise.
+ * libio/ioungetc.c (_IO_ungetc): Likewise.
+ * nptl/pthread_create.c (__pthread_create_2_1): Enable stdio locks.
+ * libio/iofopncook.c (_IO_fopencookie): Enable locking for the file.
+ * sysdeps/pthread/flockfile.c (__flockfile): Likewise.
+
2017-07-04 Florian Weimer <fweimer@redhat.com>
[BZ #21542]
GLIBC_PRIVATE {
# Used by NPTL and librt
__libc_fatal;
+
+ # Used by NPTL
+ _IO_enable_locks;
}
}
{
int result;
CHECK_FILE (fp, EOF);
+ if (!_IO_need_lock (fp))
+ return _IO_feof_unlocked (fp);
_IO_flockfile (fp);
result = _IO_feof_unlocked (fp);
_IO_funlockfile (fp);
{
int result;
CHECK_FILE (fp, EOF);
+ if (!_IO_need_lock (fp))
+ return _IO_ferror_unlocked (fp);
_IO_flockfile (fp);
result = _IO_ferror_unlocked (fp);
_IO_funlockfile (fp);
{
int result;
CHECK_FILE (fp, EOF);
+ if (!_IO_need_lock (fp))
+ return _IO_putc_unlocked (c, fp);
_IO_acquire_lock (fp);
result = _IO_putc_unlocked (c, fp);
_IO_release_lock (fp);
_IO_init_internal (fp, flags);
}
+static int stdio_needs_locking;
+
+/* In a single-threaded process most stdio locks can be omitted. After
+ _IO_enable_locks is called, locks are not optimized away any more.
+ It must be first called while the process is still single-threaded.
+
+ This lock optimization can be disabled on a per-file basis by setting
+ _IO_FLAGS2_NEED_LOCK, because a file can have user-defined callbacks
+ or can be locked with flockfile and then a thread may be created
+ between a lock and unlock, so omitting the lock is not valid.
+
+ Here we have to make sure that the flag is set on all existing files
+ and files created later. */
+void
+_IO_enable_locks (void)
+{
+ _IO_ITER i;
+
+ if (stdio_needs_locking)
+ return;
+ stdio_needs_locking = 1;
+ for (i = _IO_iter_begin (); i != _IO_iter_end (); i = _IO_iter_next (i))
+ _IO_iter_file (i)->_flags2 |= _IO_FLAGS2_NEED_LOCK;
+}
+libc_hidden_def (_IO_enable_locks)
+
void
_IO_old_init (_IO_FILE *fp, int flags)
{
fp->_flags = _IO_MAGIC|flags;
fp->_flags2 = 0;
+ if (stdio_needs_locking)
+ fp->_flags2 |= _IO_FLAGS2_NEED_LOCK;
fp->_IO_buf_base = NULL;
fp->_IO_buf_end = NULL;
fp->_IO_read_base = NULL;
{
int result;
CHECK_FILE (fp, EOF);
+ if (!_IO_need_lock (fp))
+ return _IO_getc_unlocked (fp);
_IO_acquire_lock (fp);
result = _IO_getc_unlocked (fp);
_IO_release_lock (fp);
getchar (void)
{
int result;
+ if (!_IO_need_lock (_IO_stdin))
+ return _IO_getc_unlocked (_IO_stdin);
_IO_acquire_lock (_IO_stdin);
result = _IO_getc_unlocked (_IO_stdin);
_IO_release_lock (_IO_stdin);
_IO_mask_flags (&cfile->__fp.file, read_write,
_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
+ cfile->__fp.file._flags2 |= _IO_FLAGS2_NEED_LOCK;
+
/* We use a negative number different from -1 for _fileno to mark that
this special stream is not associated with a real file, but still has
to be treated as such. */
CHECK_FILE (fp, EOF);
if (c == EOF)
return EOF;
+ if (!_IO_need_lock (fp))
+ return _IO_sputbackc (fp, (unsigned char) c);
_IO_acquire_lock (fp);
result = _IO_sputbackc (fp, (unsigned char) c);
_IO_release_lock (fp);
# define _IO_FLAGS2_SCANF_STD 16
# define _IO_FLAGS2_NOCLOSE 32
# define _IO_FLAGS2_CLOEXEC 64
+# define _IO_FLAGS2_NEED_LOCK 128
#endif
/* These are "formatting flags" matching the iostream fmtflags enum values. */
#define _IO_cleanup_region_end(_Doit) /**/
#endif
+#define _IO_need_lock(_fp) \
+ (((_fp)->_flags2 & _IO_FLAGS2_NEED_LOCK) != 0)
+
extern int _IO_vfscanf (_IO_FILE * __restrict, const char * __restrict,
_IO_va_list, int *__restrict);
extern int _IO_vfprintf (_IO_FILE *__restrict, const char *__restrict,
libc_hidden_proto (_IO_list_unlock)
extern void _IO_list_resetlock (void) __THROW;
libc_hidden_proto (_IO_list_resetlock)
+extern void _IO_enable_locks (void) __THROW;
+libc_hidden_proto (_IO_enable_locks)
/* Default jumptable functions. */
{
int result;
CHECK_FILE (fp, EOF);
+ if (!_IO_need_lock (fp))
+ return _IO_putc_unlocked (c, fp);
_IO_acquire_lock (fp);
result = _IO_putc_unlocked (c, fp);
_IO_release_lock (fp);
#include <exit-thread.h>
#include <default-sched.h>
#include <futex-internal.h>
+#include "libioP.h"
#include <shlib-compat.h>
collect_default_sched (pd);
}
+ if (__glibc_unlikely (__nptl_nthreads == 1))
+ _IO_enable_locks ();
+
/* Pass the descriptor to the caller. */
*newthread = (pthread_t) pd;
void
__flockfile (FILE *stream)
{
+ stream->_flags2 |= _IO_FLAGS2_NEED_LOCK;
_IO_lock_lock (*stream->_lock);
}
strong_alias (__flockfile, _IO_flockfile)