blob: adf688065736588c0728bd1095c4a8fac4fea6f7 [file] [log] [blame]
/*
* Copyright (c) 2011 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/*
* NaCl Server Runtime interruptible binary mutex, based on nacl_sync
* interface.
*/
#include "native_client/src/include/portability.h"
#include "native_client/src/shared/platform/nacl_interruptible_mutex.h"
#include "native_client/src/shared/platform/nacl_log.h"
#include "native_client/src/shared/platform/nacl_sync_checked.h"
int NaClIntrMutexCtor(struct NaClIntrMutex *mp) {
if (!NaClMutexCtor(&mp->mu)) {
return 0;
}
if (!NaClCondVarCtor(&mp->cv)) {
NaClMutexDtor(&mp->mu);
return 0;
}
mp->lock_state = NACL_INTR_LOCK_FREE;
return 1;
}
void NaClIntrMutexDtor(struct NaClIntrMutex *mp) {
NaClCondVarDtor(&mp->cv);
NaClMutexDtor(&mp->mu);
}
NaClSyncStatus NaClIntrMutexLock(struct NaClIntrMutex *mp) {
NaClSyncStatus rv = NACL_SYNC_INTERNAL_ERROR;
NaClXMutexLock(&mp->mu);
while (NACL_INTR_LOCK_HELD == mp->lock_state) {
NaClXCondVarWait(&mp->cv, &mp->mu);
}
if (NACL_INTR_LOCK_FREE == mp->lock_state) {
mp->lock_state = NACL_INTR_LOCK_HELD;
rv = NACL_SYNC_OK;
}
if (NACL_INTR_LOCK_INTERRUPTED == mp->lock_state) {
rv = NACL_SYNC_MUTEX_INTERRUPTED;
}
NaClXMutexUnlock(&mp->mu);
return rv;
}
NaClSyncStatus NaClIntrMutexTryLock(struct NaClIntrMutex *mp) {
NaClSyncStatus rv = NACL_SYNC_INTERNAL_ERROR;
NaClXMutexLock(&mp->mu);
switch (mp->lock_state) {
case NACL_INTR_LOCK_FREE:
mp->lock_state = NACL_INTR_LOCK_HELD;
rv = NACL_SYNC_OK;
break;
case NACL_INTR_LOCK_HELD:
rv = NACL_SYNC_BUSY;
break;
case NACL_INTR_LOCK_INTERRUPTED:
rv = NACL_SYNC_MUTEX_INTERRUPTED;
break;
default:
rv = NACL_SYNC_INTERNAL_ERROR;
break;
}
NaClXMutexUnlock(&mp->mu);
return rv;
}
NaClSyncStatus NaClIntrMutexUnlock(struct NaClIntrMutex *mp) {
NaClSyncStatus rv = NACL_SYNC_INTERNAL_ERROR;
NaClXMutexLock(&mp->mu);
if (NACL_INTR_LOCK_HELD != mp->lock_state) {
NaClLog(1, "NaClIntrMutxUnlock: unlocking when lock is not held\n");
rv = NACL_SYNC_MUTEX_PERMISSION;
} else {
rv = NACL_SYNC_OK;
}
mp->lock_state = NACL_INTR_LOCK_FREE;
NaClXCondVarSignal(&mp->cv);
NaClXMutexUnlock(&mp->mu);
return rv;
}
void NaClIntrMutexIntr(struct NaClIntrMutex *mp) {
NaClXMutexLock(&mp->mu);
if (NACL_INTR_LOCK_HELD == mp->lock_state) {
/* potentially there are threads waiting for this thread */
mp->lock_state = NACL_INTR_LOCK_INTERRUPTED;
NaClXCondVarBroadcast(&mp->cv);
} else {
mp->lock_state = NACL_INTR_LOCK_INTERRUPTED;
}
NaClXMutexUnlock(&mp->mu);
}
/*
* Reset the interruptible mutex, presumably after the condition
* causing the interrupt has been cleared. In our case, this would be
* an E_MOVE_ADDRESS_SPACE induced address space move.
*
* This is safe to invoke only after all threads are known to be in a
* quiescent state -- i.e., will no longer call
* NaClIntrMutex{Try,}Lock on the interruptible mutex -- since there
* is no guarntee that all the threads awaken by NaClIntrMutexIntr
* have actually been run yet.
*/
void NaClIntrMutexReset(struct NaClIntrMutex *mp) {
NaClXMutexLock(&mp->mu);
if (NACL_INTR_LOCK_INTERRUPTED != mp->lock_state) {
NaClLog(LOG_FATAL,
"NaClIntrMutexReset: lock at 0x%08"NACL_PRIxPTR" not interrupted\n",
(uintptr_t) mp);
}
mp->lock_state = NACL_INTR_LOCK_FREE;
NaClXMutexUnlock(&mp->mu);
}