Skip to content

Commit c8e263b

Browse files
committed
Revert fwalk/sfp locking to fix concurrent reads
The locking can fail in a couple of ways: - A concurrent fread from an unbuffered or line-buffered file flushes the output of other line-buffered files, and if _fwalk locks every file, then the fread blocks until other file reads have completed. - __sfp can initialize a file lock while _fwalk is locking/unlocking it. For now, revert to the behavior Bionic had in previous releases. This commit reverts the file locking parts of commit 468efc8. Bug: http://b/131251441 Bug: http://b/130189834 Test: bionic unit tests Change-Id: I9e20b9cd8ccd14e7962f7308e174f08af72b56c6 (cherry picked from commit c485cdb0249415b8aee5968b2b8854921e152854)
1 parent 125d32c commit c8e263b

File tree

5 files changed

+38
-7
lines changed

5 files changed

+38
-7
lines changed

libc/stdio/local.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ __LIBC32_LEGACY_PUBLIC__ int __sclose(void*);
204204
__LIBC32_LEGACY_PUBLIC__ int _fwalk(int (*)(FILE*));
205205

206206
off64_t __sseek64(void*, off64_t, int);
207+
int __sflush_locked(FILE*);
207208
int __swhatbuf(FILE*, size_t*, int*);
208209
wint_t __fgetwc_unlock(FILE*);
209210
wint_t __ungetwc(wint_t, FILE*);

libc/stdio/refill.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ static int
4040
lflush(FILE *fp)
4141
{
4242
if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
43-
return (__sflush(fp)); /* ignored... */
43+
return (__sflush_locked(fp)); /* ignored... */
4444
return (0);
4545
}
4646

libc/stdio/stdio.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ FILE* stdin = &__sF[0];
106106
FILE* stdout = &__sF[1];
107107
FILE* stderr = &__sF[2];
108108

109-
static pthread_mutex_t __stdio_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
109+
static pthread_mutex_t __stdio_mutex = PTHREAD_MUTEX_INITIALIZER;
110110

111111
static uint64_t __get_file_tag(FILE* fp) {
112112
// Don't use a tag for the standard streams.
@@ -211,21 +211,23 @@ FILE* __sfp(void) {
211211
}
212212

213213
int _fwalk(int (*callback)(FILE*)) {
214-
pthread_mutex_lock(&__stdio_mutex);
215214
int result = 0;
216215
for (glue* g = &__sglue; g != nullptr; g = g->next) {
217216
FILE* fp = g->iobs;
218217
for (int n = g->niobs; --n >= 0; ++fp) {
219-
ScopedFileLock sfl(fp);
220218
if (fp->_flags != 0 && (fp->_flags & __SIGN) == 0) {
221219
result |= (*callback)(fp);
222220
}
223221
}
224222
}
225-
pthread_mutex_unlock(&__stdio_mutex);
226223
return result;
227224
}
228225

226+
extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
227+
// Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway.
228+
_fwalk(__sflush);
229+
}
230+
229231
static FILE* __fopen(int fd, int flags) {
230232
#if !defined(__LP64__)
231233
if (fd > SHRT_MAX) {
@@ -520,6 +522,11 @@ int __sflush(FILE* fp) {
520522
return 0;
521523
}
522524

525+
int __sflush_locked(FILE* fp) {
526+
ScopedFileLock sfl(fp);
527+
return __sflush(fp);
528+
}
529+
523530
int __sread(void* cookie, char* buf, int n) {
524531
FILE* fp = reinterpret_cast<FILE*>(cookie);
525532
return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
@@ -1061,7 +1068,7 @@ int wscanf(const wchar_t* fmt, ...) {
10611068
}
10621069

10631070
static int fflush_all() {
1064-
return _fwalk(__sflush);
1071+
return _fwalk(__sflush_locked);
10651072
}
10661073

10671074
int fflush(FILE* fp) {

libc/stdlib/atexit.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ __cxa_finalize(void *dso)
188188

189189
/* If called via exit(), flush output of all open files. */
190190
if (dso == NULL) {
191-
fflush(NULL);
191+
extern void __libc_stdio_cleanup(void);
192+
__libc_stdio_cleanup();
192193
}
193194

194195
/* BEGIN android-changed: call __unregister_atfork if dso is not null */

tests/stdio_test.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <locale.h>
3030

3131
#include <string>
32+
#include <thread>
3233
#include <vector>
3334

3435
#include <android-base/file.h>
@@ -2577,3 +2578,24 @@ TEST(STDIO_TEST, dev_std_files) {
25772578
ASSERT_LT(0, length);
25782579
ASSERT_EQ("/proc/self/fd/2", std::string(path, length));
25792580
}
2581+
2582+
TEST(STDIO_TEST, fread_with_locked_file) {
2583+
// Reading an unbuffered/line-buffered file from one thread shouldn't block on
2584+
// files locked on other threads, even if it flushes some line-buffered files.
2585+
FILE* fp1 = fopen("/dev/zero", "r");
2586+
ASSERT_TRUE(fp1 != nullptr);
2587+
flockfile(fp1);
2588+
2589+
std::thread([] {
2590+
for (int mode : { _IONBF, _IOLBF }) {
2591+
FILE* fp2 = fopen("/dev/zero", "r");
2592+
ASSERT_TRUE(fp2 != nullptr);
2593+
setvbuf(fp2, nullptr, mode, 0);
2594+
ASSERT_EQ('\0', fgetc(fp2));
2595+
fclose(fp2);
2596+
}
2597+
}).join();
2598+
2599+
funlockfile(fp1);
2600+
fclose(fp1);
2601+
}

0 commit comments

Comments
 (0)