#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "base/check_op.h"
#include "base/debug/leak_annotations.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
#include "base/threading/sequence_local_storage_map.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "mojo/public/c/system/core.h"
namespace mojo {
namespace {
bool g_enable_sync_call_interrupts = true;
#if ENABLE_SYNC_CALL_RESTRICTIONS
class GlobalSyncCallSettings {
public:
GlobalSyncCallSettings() = default;
GlobalSyncCallSettings(const GlobalSyncCallSettings&) = delete;
GlobalSyncCallSettings& operator=(const GlobalSyncCallSettings&) = delete;
~GlobalSyncCallSettings() = default;
bool sync_call_allowed_by_default() const {
base::AutoLock lock(lock_);
return sync_call_allowed_by_default_;
}
void DisallowSyncCallByDefault() {
base::AutoLock lock(lock_);
sync_call_allowed_by_default_ = false;
}
private:
mutable base::Lock lock_;
bool sync_call_allowed_by_default_ = true;
};
GlobalSyncCallSettings& GetGlobalSettings() {
static base::NoDestructor<GlobalSyncCallSettings> global_settings;
return *global_settings;
}
size_t& GetSequenceLocalScopedAllowCount() {
static base::SequenceLocalStorageSlot<size_t> count;
return count.GetOrCreateValue();
}
bool SyncCallRestrictionsEnforceable() {
return base::internal::SequenceLocalStorageMap::IsSetForCurrentThread();
}
#endif
}
#if ENABLE_SYNC_CALL_RESTRICTIONS
void SyncCallRestrictions::AssertSyncCallAllowed() {
if (GetGlobalSettings().sync_call_allowed_by_default() ||
!SyncCallRestrictionsEnforceable()) {
return;
}
if (GetSequenceLocalScopedAllowCount() > 0)
return;
LOG(FATAL) << "Mojo sync calls are not allowed in this process because "
<< "they can lead to jank and deadlock. If you must make an "
<< "exception, please see "
<< "SyncCallRestrictions::ScopedAllowSyncCall and consult "
<< "mojo/OWNERS.";
}
void SyncCallRestrictions::DisallowSyncCall() {
GetGlobalSettings().DisallowSyncCallByDefault();
}
void SyncCallRestrictions::IncreaseScopedAllowCount() {
if (!SyncCallRestrictionsEnforceable())
return;
++GetSequenceLocalScopedAllowCount();
}
void SyncCallRestrictions::DecreaseScopedAllowCount() {
if (!SyncCallRestrictionsEnforceable())
return;
DCHECK_GT(GetSequenceLocalScopedAllowCount(), 0u);
--GetSequenceLocalScopedAllowCount();
}
#endif
void SyncCallRestrictions::DisableSyncCallInterrupts() {
g_enable_sync_call_interrupts = false;
}
void SyncCallRestrictions::EnableSyncCallInterruptsForTesting() {
g_enable_sync_call_interrupts = true;
}
bool SyncCallRestrictions::AreSyncCallInterruptsEnabled() {
return g_enable_sync_call_interrupts;
}
}