Compdigitec Labs

« | Home | »

fclose() freezes/hangs when run inside pthread on Android 2.1

By admin | June 25, 2012

There appears to be an obscure bug that has somehow recently activated on ARMv6, non-FPU builds of VLC for Android. This has been only observed on Android 2.1 and possibly on other Android 2.x platforms with ARMv6, non-FPU builds Android 2.2. On the other hand, it works perfectly on an Android 4.0 on a NEON device (same code), so it is probably specific to Android 2.2 and 2.1 (not tested on 1.6).

The obscure bug involves hanging during a fclose() call whilst being run inside a POSIX pthread_once thread. This obscure bug was causing hanging on startup of VLC for Android on an Android 2.1, no-FPU, no-NEON device. Further testing with the Android emulator also revealed that the Android 2.1 and 2.2 emulator crashes too, suggesting a bionic bug (Android 2.3 works fine). Here is a condensed, VLC-neutral version of the code from VLC core that is specific to this problem, adapted from src/posix/linux_cpu.c:

// arm-linux-androideabi-gcc --sysroot=/opt/android-ndk-r8/platforms/android-9/arch-arm -I. -g -march=armv6j -mtune=arm1136j-s -msoft-float android.c -o android
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>

/* getdelim and getline courtesy of VLC's compat/getdelim.c */
ssize_t getdelim(char**v1,size_t*v2,int v3,FILE*v4){char*A1=*v1;size_t A2=(A1 != NULL)?*v2:0;size_t A3 = 0;for(;;){if((A2 - A3) <= 2){A2=A2?(A2*2):256;A1=realloc(*v1,A2);if(A1 ==NULL)return-1;*v1=A1;*v2=A2;}int c=fgetc(v4);if(c==-1){if(A3==0||ferror(v4))return -1;break;}A1[A3++]=c;if(c==v3)break;}A1[A3]='\0';return A3;} ssize_t getline(char** a,size_t* b,FILE* c){return getdelim(a,b,'\n',c);}

static uint32_t cpu_flags = 0;

static void vlc_CPU_init (void) {
    FILE *info = fopen ("/proc/cpuinfo", "rt");
    if (info == NULL) return;
    char *line = NULL;
    size_t linelen = 0;
	// ...irrelevant code skipped...
    while (getline (&line, &linelen, info) != -1) {
        // ...do stuff with the loop...
    }
    fclose(info); /* hangs here */
    free(line);
	// ...parse and set cpu_flags...
    cpu_flags = 1;
}

unsigned vlc_CPU(void) {
    static pthread_once_t once = PTHREAD_ONCE_INIT;
    pthread_once (&once, vlc_CPU_init);
    //vlc_CPU_init();
    return cpu_flags;
}

int main(void) {
    if(vlc_CPU() > 0)
        puts("Successful");
    else
        puts("Failure, or a.k.a. will never reach here");
    return 0;
}

Interestingly, after debugging there are two ways to workaround this bug:

  1. Comment out the fclose() on line 21 above, or
  2. Don’t run vlc_CPU_init() inside a thread – comment out line 29 and uncomment line 30 to run vlc_CPU_init() unthreaded.

Further research - “The stdio library uses locks internally” - yields that this may be a bug in bionic, Android’s own custom implementation of libc as well as pthreads. So far, the fact that fclose() works successfully if not run inside a pthread, as well as the knowledge that stdio has internal locks, may suggest a bug in the bionic implementation on Android 2.2 / 2.1 and below.

See also http://code.google.com/p/android/issues/detail?id=5116.

If you found this article helpful or interesting, please help Compdigitec spread the word. Don’t forget to subscribe to Compdigitec Labs for more useful and interesting articles!

Topics: Linux | No Comments »

Comments