Merge "android/utils/filelock.c: Handle stale Win32 lock directories."
diff --git a/android/utils/filelock.c b/android/utils/filelock.c
index b009e25..710293b 100644
--- a/android/utils/filelock.c
+++ b/android/utils/filelock.c
@@ -29,37 +29,44 @@
# include <signal.h>
#endif
-#define D(...) ((void)0)
+// Set to 1 to enable debug traces here.
+#if 0
+#define D(...) printf(__VA_ARGS__), printf("\n"), fflush(stdout)
+#else
+#define D(...) ((void)0)
+#endif
/** FILE LOCKS SUPPORT
**
- ** a FileLock is useful to prevent several emulator instances from using the same
- ** writable file (e.g. the userdata.img disk images).
+ ** A FileLock is useful to prevent several emulator instances from using
+ ** the same writable file (e.g. the userdata.img disk images).
**
- ** create a FileLock object with filelock_create(), ithis function should return NULL
- ** only if the corresponding file path could not be locked.
+ ** Create a FileLock object with filelock_create(), this function should
+ ** return NULL only if the corresponding file path could not be locked.
**
- ** all file locks are automatically released and destroyed when the program exits.
- ** the filelock_lock() function can also detect stale file locks that can linger
- ** when the emulator crashes unexpectedly, and will happily clean them for you
+ ** All file locks are automatically released and destroyed when the program
+ ** exits. The filelock_lock() function can also detect stale file locks
+ ** that can linger when the emulator crashes unexpectedly, and will happily
+ ** clean them for you
**
- ** here's how it works, three files are used:
+ ** Here's how it works, three files are used:
** file - the data file accessed by the emulator
** lock - a lock file (file + '.lock')
- ** temp - a temporary file make unique with mkstemp
+ ** temp - a temporary file made unique with mkstemp
**
- ** when locking:
+ ** When locking:
** create 'temp' and store our pid in it
** attemp to link 'lock' to 'temp'
** if the link succeeds, we obtain the lock
** unlink 'temp'
**
- ** when unlocking:
+ ** When unlocking:
** unlink 'lock'
**
**
- ** on Windows, 'lock' is a directory name. locking is equivalent to
- ** creating it...
+ ** On Windows, 'lock' is a directory name. locking is equivalent to
+ ** creating it. The directory will contain a file named 'pid' that
+ ** contains the locking process' PID.
**
**/
@@ -211,119 +218,139 @@
#else
int temp_fd = -1;
int lock_fd = -1;
- int rc, tries, _sleep;
+ int rc, tries;
FILE* f = NULL;
char pid[8];
struct stat st_temp;
- strcpy( lock->temp, lock->file );
- strcat( lock->temp, TEMP_NAME );
- temp_fd = mkstemp( lock->temp );
+ temp_fd = mkstemp(lock->temp);
if (temp_fd < 0) {
- D("cannot create locking temp file '%s'", lock->temp );
+ D("Cannot create locking temp file '%s'", lock->temp);
goto Fail;
}
- sprintf( pid, "%d", getpid() );
- ret = write( temp_fd, pid, strlen(pid)+1 );
+ snprintf(pid, sizeof pid, "%d", getpid());
+ ret = HANDLE_EINTR(write(temp_fd, pid, strlen(pid) + 1));
if (ret < 0) {
- D( "cannot write to locking temp file '%s'", lock->temp);
+ D("Cannot write to locking temp file '%s'", lock->temp);
goto Fail;
}
- close( temp_fd );
+ close(temp_fd);
temp_fd = -1;
- rc = HANDLE_EINTR(lstat( lock->temp, &st_temp ));
+ rc = HANDLE_EINTR(lstat(lock->temp, &st_temp));
if (rc < 0) {
- D( "can't properly stat our locking temp file '%s'", lock->temp );
+ D("Can't properly stat our locking temp file '%s'", lock->temp);
goto Fail;
}
/* now attempt to link the temp file to the lock file */
- _sleep = 0;
- for ( tries = 4; tries > 0; tries-- )
+ int sleep_duration_us = 0;
+ for (tries = 4; tries > 0; tries--)
{
- struct stat st_lock;
- int rc;
+ const int kSleepDurationUsMax = 2000000; // 2 seconds.
+ const int kSleepDurationUsIncrement = 200000; // 0.2 seconds
- if (_sleep > 0) {
- if (_sleep > 2000000) {
- D( "cannot acquire lock file '%s'", lock->lock );
+ if (sleep_duration_us > 0) {
+ if (sleep_duration_us > kSleepDurationUsMax) {
+ D("Cannot acquire lock file '%s'", lock->lock);
goto Fail;
}
- usleep( _sleep );
+ usleep(sleep_duration_us);
}
- _sleep += 200000;
+ sleep_duration_us += kSleepDurationUsIncrement;
- /* the return value of link() is buggy on NFS */
- rc = HANDLE_EINTR(link( lock->temp, lock->lock ));
+ // The return value of link() is buggy on NFS, so ignore it.
+ // and use lstat() to look at the result.
+ rc = HANDLE_EINTR(link(lock->temp, lock->lock));
- rc = HANDLE_EINTR(lstat( lock->lock, &st_lock ));
- if (rc == 0 &&
- st_temp.st_rdev == st_lock.st_rdev &&
- st_temp.st_ino == st_lock.st_ino )
- {
- /* SUCCESS */
+ struct stat st_lock;
+ rc = HANDLE_EINTR(lstat(lock->lock, &st_lock));
+ if (rc != 0) {
+ // Try again after sleeping a little.
+ continue;
+ }
+
+ if (st_temp.st_rdev == st_lock.st_rdev &&
+ st_temp.st_ino == st_lock.st_ino ) {
+ /* The link() operation suceeded */
lock->locked = 1;
- rc = HANDLE_EINTR(unlink( lock->temp ));
+ rc = HANDLE_EINTR(unlink(lock->temp));
return 0;
}
+ if (S_ISDIR(st_lock.st_mode)) {
+ // The .lock file is a directory. This can only happen
+ // when the AVD was previously used by a Win32 emulator
+ // instance running under Wine on the same machine.
+ fprintf(stderr,
+ "Stale Win32 lock file detected: %s\n",
+ lock->lock);
+ goto Fail;
+ }
+
/* if we get there, it means that the link() call failed */
/* check the lockfile to see if it is stale */
- if (rc == 0) {
- char buf[16];
- time_t now;
- int lockpid = 0;
- int lockfd;
- int stale = 2; /* means don't know */
- struct stat st;
+ typedef enum {
+ FRESHNESS_UNKNOWN = 0,
+ FRESHNESS_FRESH,
+ FRESHNESS_STALE,
+ } Freshness;
- rc = HANDLE_EINTR(time( &now));
- st.st_mtime = now - 120;
+ Freshness freshness = FRESHNESS_UNKNOWN;
- lockfd = HANDLE_EINTR(open( lock->lock,O_RDONLY ));
- if ( lockfd >= 0 ) {
- int len;
+ struct stat st;
+ time_t now;
+ rc = HANDLE_EINTR(time(&now));
+ st.st_mtime = now - 120;
- len = HANDLE_EINTR(read( lockfd, buf, sizeof(buf)-1 ));
- buf[len] = 0;
- lockpid = atoi(buf);
-
- rc = HANDLE_EINTR(fstat( lockfd, &st ));
- if (rc == 0)
- now = st.st_atime;
-
- IGNORE_EINTR(close(lockfd));
+ int lockpid = 0;
+ int lockfd = HANDLE_EINTR(open(lock->lock,O_RDONLY));
+ if (lockfd >= 0) {
+ char buf[16];
+ int len = HANDLE_EINTR(read(lockfd, buf, sizeof(buf) - 1U));
+ if (len < 0) {
+ len = 0;
}
- /* if there is a PID, check that it is still alive */
- if (lockpid > 0) {
- rc = HANDLE_EINTR(kill( lockpid, 0 ));
- if (rc == 0 || errno == EPERM) {
- stale = 0;
- } else if (rc < 0 && errno == ESRCH) {
- stale = 1;
- }
- }
- if (stale == 2) {
- /* no pid, stale if the file is older than 1 minute */
- stale = (now >= st.st_mtime + 60);
- }
+ buf[len] = 0;
+ lockpid = atoi(buf);
- if (stale) {
- D( "removing stale lockfile '%s'", lock->lock );
- rc = HANDLE_EINTR(unlink( lock->lock ));
- _sleep = 0;
- tries++;
+ rc = HANDLE_EINTR(fstat(lockfd, &st));
+ if (rc == 0) {
+ now = st.st_atime;
+ }
+ IGNORE_EINTR(close(lockfd));
+ }
+ /* if there is a PID, check that it is still alive */
+ if (lockpid > 0) {
+ rc = HANDLE_EINTR(kill(lockpid, 0));
+ if (rc == 0 || errno == EPERM) {
+ freshness = FRESHNESS_FRESH;
+ } else if (rc < 0 && errno == ESRCH) {
+ freshness = FRESHNESS_STALE;
}
}
+ if (freshness == FRESHNESS_UNKNOWN) {
+ /* no pid, stale if the file is older than 1 minute */
+ freshness = (now >= st.st_mtime + 60) ?
+ FRESHNESS_STALE :
+ FRESHNESS_FRESH;
+ }
+
+ if (freshness == FRESHNESS_STALE) {
+ D("Removing stale lockfile '%s'", lock->lock);
+ rc = HANDLE_EINTR(unlink(lock->lock));
+ sleep_duration_us = 0;
+ tries++;
+ }
}
- D("file '%s' is already in use by another process", lock->file );
+ D("file '%s' is already in use by another process", lock->file);
Fail:
- if (f)
+ if (f) {
fclose(f);
+ }
if (temp_fd >= 0) {
IGNORE_EINTR(close(temp_fd));
@@ -388,7 +415,7 @@
#ifdef _WIN32
snprintf( (char*)lock->temp, temp_len, "%s\\" PIDFILE_NAME, lock->lock );
#else
- lock->temp[0] = 0;
+ snprintf((char*)lock->temp, temp_len, "%s%s", lock->file, TEMP_NAME);
#endif
lock->locked = 0;