Implement __fsetlocking.

The old __isthreaded hack was never very useful on Android because all user
code runs in a VM where there are lots of threads running. But __fsetlocking
lets a caller say "I'll worry about the locking for this FILE*", which is
useful for the normal case where you don't share a FILE* between threads
so you don't need any locking.

Bug: 17154740
Bug: 18593728
Change-Id: I2a8dddc29d3edff39a3d7d793387f2253608a68d
diff --git a/libc/stdio/stdio_ext.cpp b/libc/stdio/stdio_ext.cpp
index bfdecb8..fea44f6 100644
--- a/libc/stdio/stdio_ext.cpp
+++ b/libc/stdio/stdio_ext.cpp
@@ -27,13 +27,10 @@
  */
 
 #include <stdio_ext.h>
+#include <stdlib.h>
 
-#include <stdio.h>
 #include "local.h"
-
-#define FSETLOCKING_QUERY 0
-#define FSETLOCKING_INTERNAL 1
-#define FSETLOCKING_BYCALLER 2
+#include "private/libc_logging.h"
 
 size_t __fbufsize(FILE* fp) {
   return fp->_bf._size;
@@ -76,11 +73,19 @@
   fflush(NULL);
 }
 
-int __fsetlocking(FILE*, int) {
-  // We don't currently have an implementation that would obey this,
-  // so make setting the state a no-op and always return "we handle locking for you".
-  // http://b/17154740 suggests ways we could fix this.
-  return FSETLOCKING_INTERNAL;
+int __fsetlocking(FILE* fp, int type) {
+  int old_state = _EXT(fp)->_stdio_handles_locking ? FSETLOCKING_INTERNAL : FSETLOCKING_BYCALLER;
+  if (type == FSETLOCKING_QUERY) {
+    return old_state;
+  }
+
+  if (type != FSETLOCKING_INTERNAL && type != FSETLOCKING_BYCALLER) {
+    // The API doesn't let us report an error, so blow up.
+    __libc_fatal("Bad type (%d) passed to __fsetlocking", type);
+  }
+
+  _EXT(fp)->_stdio_handles_locking = (type == FSETLOCKING_INTERNAL);
+  return old_state;
 }
 
 void clearerr_unlocked(FILE* fp) {