зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge
--HG-- extra : rebase_source : 29933c2409d7ab8c51343cf463aa4303e569c530
This commit is contained in:
Коммит
0201242956
|
@ -13,6 +13,7 @@ import shutil
|
|||
import sys
|
||||
|
||||
from automation import Automation
|
||||
from mozdevice import ADBTimeoutError
|
||||
from mozlog import get_default_logger
|
||||
from mozscreenshot import dump_screen
|
||||
import mozcrash
|
||||
|
@ -294,6 +295,8 @@ class RemoteAutomation(Automation):
|
|||
try:
|
||||
newLogContent = self.device.get_file(
|
||||
self.proc, offset=self.stdoutlen)
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except Exception:
|
||||
return False
|
||||
if not newLogContent:
|
||||
|
@ -344,6 +347,8 @@ class RemoteAutomation(Automation):
|
|||
self.counts['fail'] += val
|
||||
elif "Todo:" in line:
|
||||
self.counts['todo'] += val
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
@ -412,12 +417,16 @@ class RemoteAutomation(Automation):
|
|||
# Trigger an ANR report with "kill -3" (SIGQUIT)
|
||||
try:
|
||||
self.device.pkill(self.procName, sig=3, attempts=1)
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except: # NOQA: E722
|
||||
pass
|
||||
time.sleep(3)
|
||||
# Trigger a breakpad dump with "kill -6" (SIGABRT)
|
||||
try:
|
||||
self.device.pkill(self.procName, sig=6, attempts=1)
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except: # NOQA: E722
|
||||
pass
|
||||
# Wait for process to end
|
||||
|
@ -431,6 +440,8 @@ class RemoteAutomation(Automation):
|
|||
retries += 1
|
||||
try:
|
||||
self.device.pkill(self.procName, sig=9, attempts=1)
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except: # NOQA: E722
|
||||
print("%s still alive after SIGKILL!" % self.procName)
|
||||
if self.device.process_exist(self.procName):
|
||||
|
|
|
@ -59,7 +59,6 @@ struct AudioIpcInitParams {
|
|||
int mServerConnection;
|
||||
size_t mPoolSize;
|
||||
size_t mStackSize;
|
||||
void (*mThreadCreateCallback)(const char*);
|
||||
};
|
||||
|
||||
// These functions are provided by audioipc-server crate
|
||||
|
@ -431,9 +430,6 @@ cubeb* GetCubebContextUnlocked()
|
|||
initParams.mPoolSize = sAudioIPCPoolSize;
|
||||
initParams.mStackSize = sAudioIPCStackSize;
|
||||
initParams.mServerConnection = sIPCConnection->ClonePlatformHandle().release();
|
||||
initParams.mThreadCreateCallback = [](const char* aName) {
|
||||
PROFILER_REGISTER_THREAD(aName);
|
||||
};
|
||||
|
||||
MOZ_LOG(gCubebLog, LogLevel::Debug, ("%s: %d", PREF_AUDIOIPC_POOL_SIZE, (int) initParams.mPoolSize));
|
||||
MOZ_LOG(gCubebLog, LogLevel::Debug, ("%s: %d", PREF_AUDIOIPC_STACK_SIZE, (int) initParams.mStackSize));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
52849
|
||||
52853
|
||||
0/nm
|
||||
0th/pt
|
||||
1/n1
|
||||
|
@ -22179,6 +22179,7 @@ dc
|
|||
dd/SDG
|
||||
dded/K
|
||||
dding/K
|
||||
de
|
||||
deacon/MS
|
||||
deaconess/MS
|
||||
dead/XTMNRY
|
||||
|
@ -25795,6 +25796,7 @@ factional
|
|||
factionalism/M
|
||||
factious
|
||||
factitious
|
||||
facto
|
||||
factoid/SM
|
||||
factor's
|
||||
factor/ASDG
|
||||
|
@ -42052,6 +42054,7 @@ reasoner/M
|
|||
reasoning/M
|
||||
reassemble/DSG
|
||||
reassuring/Y
|
||||
rebar/S
|
||||
rebate/M
|
||||
rebel/MS
|
||||
rebellion/MS
|
||||
|
@ -44263,6 +44266,7 @@ segfault/S
|
|||
segment/GSMD
|
||||
segmentation/M
|
||||
segmented/U
|
||||
segregable
|
||||
segregate/CDSGN
|
||||
segregated/U
|
||||
segregation/CM
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
#include <pthread_np.h>
|
||||
#endif
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
namespace base {
|
||||
void InitThreading();
|
||||
|
@ -35,10 +33,6 @@ void InitThreading();
|
|||
#endif
|
||||
|
||||
static void* ThreadFunc(void* closure) {
|
||||
// Create a nsThread wrapper for the current platform thread, and register it
|
||||
// with the thread manager.
|
||||
(void) NS_GetCurrentThread();
|
||||
|
||||
PlatformThread::Delegate* delegate =
|
||||
static_cast<PlatformThread::Delegate*>(closure);
|
||||
delegate->ThreadMain();
|
||||
|
@ -98,10 +92,21 @@ void PlatformThread::SetName(const char* name) {
|
|||
if (PlatformThread::CurrentId() == getpid())
|
||||
return;
|
||||
|
||||
// Using NS_SetCurrentThreadName, as opposed to using platform APIs directly,
|
||||
// also sets the thread name on the PRThread wrapper, and allows us to
|
||||
// retrieve it using PR_GetThreadName.
|
||||
NS_SetCurrentThreadName(name);
|
||||
// http://0pointer.de/blog/projects/name-your-threads.html
|
||||
// Set the name for the LWP (which gets truncated to 15 characters).
|
||||
// Note that glibc also has a 'pthread_setname_np' api, but it may not be
|
||||
// available everywhere and it's only benefit over using prctl directly is
|
||||
// that it can set the name of threads other than the current thread.
|
||||
#if defined(OS_LINUX)
|
||||
prctl(PR_SET_NAME, reinterpret_cast<uintptr_t>(name), 0, 0, 0);
|
||||
#elif defined(OS_NETBSD)
|
||||
pthread_setname_np(pthread_self(), "%s", (void *)name);
|
||||
#elif defined(OS_BSD) && !defined(__GLIBC__)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
#elif defined(OS_SOLARIS)
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#else
|
||||
#endif
|
||||
}
|
||||
#endif // !OS_MACOSX
|
||||
|
||||
|
@ -124,9 +129,8 @@ bool CreateThread(size_t stack_size, bool joinable,
|
|||
pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
|
||||
}
|
||||
|
||||
if (stack_size == 0)
|
||||
stack_size = nsIThreadManager::DEFAULT_STACK_SIZE;
|
||||
pthread_attr_setstacksize(&attributes, stack_size);
|
||||
if (stack_size > 0)
|
||||
pthread_attr_setstacksize(&attributes, stack_size);
|
||||
|
||||
success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate);
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include "base/logging.h"
|
||||
#include "base/win_util.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// The information on how to set the thread name comes from
|
||||
|
@ -25,10 +23,6 @@ typedef struct tagTHREADNAME_INFO {
|
|||
} THREADNAME_INFO;
|
||||
|
||||
DWORD __stdcall ThreadFunc(void* closure) {
|
||||
// Create a nsThread wrapper for the current platform thread, and register it
|
||||
// with the thread manager.
|
||||
(void) NS_GetCurrentThread();
|
||||
|
||||
PlatformThread::Delegate* delegate =
|
||||
static_cast<PlatformThread::Delegate*>(closure);
|
||||
delegate->ThreadMain();
|
||||
|
@ -54,10 +48,24 @@ void PlatformThread::Sleep(int duration_ms) {
|
|||
|
||||
// static
|
||||
void PlatformThread::SetName(const char* name) {
|
||||
// Using NS_SetCurrentThreadName, as opposed to using platform APIs directly,
|
||||
// also sets the thread name on the PRThread wrapper, and allows us to
|
||||
// retrieve it using PR_GetThreadName.
|
||||
NS_SetCurrentThreadName(name);
|
||||
#ifdef HAVE_SEH_EXCEPTIONS
|
||||
// The debugger needs to be around to catch the name in the exception. If
|
||||
// there isn't a debugger, we are just needlessly throwing an exception.
|
||||
if (!::IsDebuggerPresent())
|
||||
return;
|
||||
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name;
|
||||
info.dwThreadID = CurrentId();
|
||||
info.dwFlags = 0;
|
||||
|
||||
MOZ_SEH_TRY {
|
||||
RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
|
||||
reinterpret_cast<DWORD_PTR*>(&info));
|
||||
} MOZ_SEH_EXCEPT(EXCEPTION_CONTINUE_EXECUTION) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -113,6 +113,14 @@ class SharedMemory {
|
|||
return ShareToProcessCommon(target_pid, new_handle, true);
|
||||
}
|
||||
|
||||
#ifdef OS_POSIX
|
||||
// If named POSIX shm is being used, append the prefix (including
|
||||
// the leading '/') that would be used by a process with the given
|
||||
// pid to the given string and return true. If not, return false.
|
||||
// (This is public so that the Linux sandboxing code can use it.)
|
||||
static bool AppendPosixShmPrefix(std::string* str, pid_t pid);
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool ShareToProcessCommon(ProcessId target_pid,
|
||||
SharedMemoryHandle* new_handle,
|
||||
|
|
|
@ -12,11 +12,15 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/file_util.h"
|
||||
#ifdef ANDROID
|
||||
#include <linux/ashmem.h>
|
||||
#endif
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/platform_thread.h"
|
||||
#include "base/string_util.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "prenv.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
|
@ -49,20 +53,28 @@ SharedMemoryHandle SharedMemory::NULLHandle() {
|
|||
return SharedMemoryHandle();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A class to handle auto-closing of FILE*'s.
|
||||
class ScopedFILEClose {
|
||||
public:
|
||||
inline void operator()(FILE* x) const {
|
||||
if (x) {
|
||||
fclose(x);
|
||||
}
|
||||
// static
|
||||
bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid)
|
||||
{
|
||||
#if defined(ANDROID) || defined(SHM_ANON)
|
||||
return false;
|
||||
#else
|
||||
*str += '/';
|
||||
#ifdef OS_LINUX
|
||||
// The Snap package environment doesn't provide a private /dev/shm
|
||||
// (it's used for communication with services like PulseAudio);
|
||||
// instead AppArmor is used to restrict access to it. Anything with
|
||||
// this prefix is allowed:
|
||||
static const char* const kSnap = PR_GetEnv("SNAP_NAME");
|
||||
if (kSnap) {
|
||||
StringAppendF(str, "snap.%s.", kSnap);
|
||||
}
|
||||
};
|
||||
|
||||
typedef mozilla::UniquePtr<FILE, ScopedFILEClose> ScopedFILE;
|
||||
|
||||
#endif // OS_LINUX
|
||||
// Hopefully the "implementation defined" name length limit is long
|
||||
// enough for this.
|
||||
StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid));
|
||||
return true;
|
||||
#endif // !ANDROID && !SHM_ANON
|
||||
}
|
||||
|
||||
bool SharedMemory::Create(size_t size) {
|
||||
|
@ -71,37 +83,62 @@ bool SharedMemory::Create(size_t size) {
|
|||
DCHECK(size > 0);
|
||||
DCHECK(mapped_file_ == -1);
|
||||
|
||||
ScopedFILE file_closer;
|
||||
FILE *fp;
|
||||
int fd;
|
||||
bool needs_truncate = true;
|
||||
|
||||
FilePath path;
|
||||
fp = file_util::CreateAndOpenTemporaryShmemFile(&path);
|
||||
|
||||
// Deleting the file prevents anyone else from mapping it in
|
||||
// (making it private), and prevents the need for cleanup (once
|
||||
// the last fd is closed, it is truly freed).
|
||||
file_util::Delete(path);
|
||||
|
||||
if (fp == NULL)
|
||||
#ifdef ANDROID
|
||||
// Android has its own shared memory facility:
|
||||
fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
|
||||
if (fd < 0) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
|
||||
return false;
|
||||
file_closer.reset(fp); // close when we go out of scope
|
||||
|
||||
// Set the file size.
|
||||
//
|
||||
// According to POSIX, (f)truncate can be used to extend files;
|
||||
// previously this required the XSI option but as of the 2008
|
||||
// edition it's required for everything. (Linux documents that this
|
||||
// may fail on non-"native" filesystems like FAT, but /dev/shm
|
||||
// should always be tmpfs.)
|
||||
if (ftruncate(fileno(fp), size) != 0)
|
||||
}
|
||||
if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
|
||||
close(fd);
|
||||
return false;
|
||||
// This probably isn't needed.
|
||||
if (fseeko(fp, size, SEEK_SET) != 0)
|
||||
}
|
||||
needs_truncate = false;
|
||||
#elif defined(SHM_ANON)
|
||||
// FreeBSD (or any other Unix that might decide to implement this
|
||||
// nice, simple API):
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#else
|
||||
// Generic Unix: shm_open + shm_unlink
|
||||
do {
|
||||
// The names don't need to be unique, but it saves time if they
|
||||
// usually are.
|
||||
static mozilla::Atomic<size_t> sNameCounter;
|
||||
std::string name;
|
||||
CHECK(AppendPosixShmPrefix(&name, getpid()));
|
||||
StringAppendF(&name, "%zu", sNameCounter++);
|
||||
// O_EXCL means the names being predictable shouldn't be a problem.
|
||||
fd = HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
|
||||
if (fd >= 0) {
|
||||
if (shm_unlink(name.c_str()) != 0) {
|
||||
// This shouldn't happen, but if it does: assume the file is
|
||||
// in fact leaked, and bail out now while it's still 0-length.
|
||||
DLOG(FATAL) << "failed to unlink shm: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (fd < 0 && errno == EEXIST);
|
||||
#endif
|
||||
|
||||
if (fd < 0) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
mapped_file_ = dup(fileno(fp));
|
||||
DCHECK(mapped_file_ >= 0);
|
||||
if (needs_truncate) {
|
||||
if (HANDLE_EINTR(ftruncate(fd, static_cast<off_t>(size))) != 0) {
|
||||
CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mapped_file_ = fd;
|
||||
max_size_ = size;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -381,7 +381,7 @@ def find_tests(substring=None):
|
|||
|
||||
|
||||
def run_test_remote(test, device, prefix, options):
|
||||
from mozdevice import ADBDevice, ADBProcessError
|
||||
from mozdevice import ADBDevice, ADBProcessError, ADBTimeoutError
|
||||
|
||||
if options.test_reflect_stringify:
|
||||
raise ValueError("can't run Reflect.stringify tests remotely")
|
||||
|
@ -405,6 +405,8 @@ def run_test_remote(test, device, prefix, options):
|
|||
cwd=options.remote_test_root,
|
||||
timeout=int(options.timeout))
|
||||
returncode = 0
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except ADBProcessError as e:
|
||||
out = e.adb_process.stdout
|
||||
print("exception output: %s" % str(out))
|
||||
|
|
|
@ -943,12 +943,7 @@ js::CurrentThreadIsParseThread()
|
|||
}
|
||||
#endif
|
||||
|
||||
// We want our default stack size limit to be approximately 2MB, to be safe, but
|
||||
// expect most threads to use much less. On Linux, however, requesting a stack
|
||||
// of 2MB or larger risks the kernel allocating an entire 2MB huge page for it
|
||||
// on first access, which we do not want. To avoid this possibility, we subtract
|
||||
// 2 standard VM page sizes from our default.
|
||||
static const uint32_t kDefaultHelperStackSize = 2048 * 1024 - 2 * 4096;
|
||||
static const uint32_t kDefaultHelperStackSize = 2048 * 1024;
|
||||
static const uint32_t kDefaultHelperStackQuota = 1800 * 1024;
|
||||
|
||||
// TSan enforces a minimum stack size that's just slightly larger than our
|
||||
|
|
|
@ -79,10 +79,6 @@ using namespace xpc;
|
|||
using namespace JS;
|
||||
using mozilla::dom::AutoEntryScript;
|
||||
|
||||
// The watchdog thread loop is pretty trivial, and should not require much stack
|
||||
// space to do its job. So only give it 32KiB.
|
||||
static constexpr size_t kWatchdogStackSize = 32 * 1024;
|
||||
|
||||
static void WatchdogMain(void* arg);
|
||||
class Watchdog;
|
||||
class WatchdogManager;
|
||||
|
@ -147,7 +143,7 @@ class Watchdog
|
|||
// join it on shutdown.
|
||||
mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
|
||||
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD, kWatchdogStackSize);
|
||||
PR_JOINABLE_THREAD, 0);
|
||||
if (!mThread)
|
||||
MOZ_CRASH("PR_CreateThread failed!");
|
||||
|
||||
|
@ -476,8 +472,6 @@ static void
|
|||
WatchdogMain(void* arg)
|
||||
{
|
||||
AUTO_PROFILER_REGISTER_THREAD("JS Watchdog");
|
||||
// Create an nsThread wrapper for the thread and register it with the thread manager.
|
||||
Unused << NS_GetCurrentThread();
|
||||
NS_SetCurrentThreadName("JS Watchdog");
|
||||
|
||||
Watchdog* self = static_cast<Watchdog*>(arg);
|
||||
|
|
|
@ -13,7 +13,7 @@ import traceback
|
|||
import urllib2
|
||||
from contextlib import closing
|
||||
|
||||
from mozdevice import ADBAndroid
|
||||
from mozdevice import ADBAndroid, ADBTimeoutError
|
||||
import mozinfo
|
||||
from automation import Automation
|
||||
from remoteautomation import RemoteAutomation, fennecLogcatFilters
|
||||
|
@ -349,6 +349,8 @@ class RemoteReftest(RefTest):
|
|||
else:
|
||||
print " %s: %s" % (category, devinfo[category])
|
||||
print "Test root: %s" % self.device.test_root
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except Exception as e:
|
||||
print "WARNING: Error getting device information: %s" % str(e)
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ use std::os::raw::c_void;
|
|||
use std::os::unix::io::FromRawFd;
|
||||
use std::os::unix::net;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use stream;
|
||||
use tokio_core::reactor::{Handle, Remote};
|
||||
use tokio_uds::UnixStream;
|
||||
|
@ -100,25 +99,9 @@ impl ContextOps for ClientContext {
|
|||
|
||||
let (tx_rpc, rx_rpc) = mpsc::channel();
|
||||
|
||||
let params = CPUPOOL_INIT_PARAMS.with(|p| {
|
||||
p.replace(None).unwrap()
|
||||
});
|
||||
|
||||
let thread_create_callback = params.thread_create_callback;
|
||||
|
||||
let register_thread = move || {
|
||||
if let Some(func) = thread_create_callback {
|
||||
let thr = thread::current();
|
||||
let name = CString::new(thr.name().unwrap()).unwrap();
|
||||
func(name.as_ptr());
|
||||
}
|
||||
};
|
||||
|
||||
let core = t!(core::spawn_thread("AudioIPC Client RPC", move || {
|
||||
let handle = core::handle();
|
||||
|
||||
register_thread();
|
||||
|
||||
open_server_stream()
|
||||
.ok()
|
||||
.and_then(|stream| UnixStream::from_stream(stream, &handle).ok())
|
||||
|
@ -133,12 +116,14 @@ impl ContextOps for ClientContext {
|
|||
|
||||
let rpc = t!(rx_rpc.recv());
|
||||
|
||||
let cpupool = futures_cpupool::Builder::new()
|
||||
let cpupool = CPUPOOL_INIT_PARAMS.with(|p| {
|
||||
let params = p.replace(None).unwrap();
|
||||
futures_cpupool::Builder::new()
|
||||
.name_prefix("AudioIPC")
|
||||
.after_start(register_thread)
|
||||
.pool_size(params.pool_size)
|
||||
.stack_size(params.stack_size)
|
||||
.create();
|
||||
.create()
|
||||
});
|
||||
|
||||
let ctx = Box::new(ClientContext {
|
||||
_ops: &CLIENT_OPS as *const _,
|
||||
|
|
|
@ -37,14 +37,12 @@ pub struct AudioIpcInitParams {
|
|||
pub server_connection: c_int,
|
||||
pub pool_size: usize,
|
||||
pub stack_size: usize,
|
||||
pub thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct CpuPoolInitParams {
|
||||
pub pool_size: usize,
|
||||
pub stack_size: usize,
|
||||
pub thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
|
||||
}
|
||||
|
||||
impl CpuPoolInitParams {
|
||||
|
@ -52,7 +50,6 @@ impl CpuPoolInitParams {
|
|||
CpuPoolInitParams {
|
||||
pool_size: params.pool_size,
|
||||
stack_size: params.stack_size,
|
||||
thread_create_callback: params.thread_create_callback,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,6 +207,7 @@
|
|||
# Windows, clang-cl build
|
||||
[ 'clang_cl == 1', {
|
||||
'cflags_mozilla': [
|
||||
'-Xclang',
|
||||
'-Wall',
|
||||
'-Wno-parentheses',
|
||||
'-Wno-strict-prototypes',
|
||||
|
|
|
@ -204,6 +204,7 @@
|
|||
# Windows, clang-cl build
|
||||
[ 'clang_cl == 1', {
|
||||
'cflags_mozilla': [
|
||||
'-Xclang',
|
||||
'-Wall',
|
||||
'-Wno-parentheses',
|
||||
'-Wno-strict-prototypes',
|
||||
|
|
|
@ -26,7 +26,6 @@ from .timeout import Timeouts
|
|||
|
||||
CHROME_ELEMENT_KEY = "chromeelement-9fc5-4b51-a3c8-01716eedeb04"
|
||||
FRAME_KEY = "frame-075b-4da1-b6ba-e579c2d3230a"
|
||||
LEGACY_ELEMENT_KEY = "ELEMENT"
|
||||
WEB_ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf"
|
||||
WINDOW_KEY = "window-fcc6-11e5-b4f8-330a88ab9d7f"
|
||||
|
||||
|
@ -34,8 +33,7 @@ WINDOW_KEY = "window-fcc6-11e5-b4f8-330a88ab9d7f"
|
|||
class HTMLElement(object):
|
||||
"""Represents a DOM Element."""
|
||||
|
||||
identifiers = (CHROME_ELEMENT_KEY, FRAME_KEY, WINDOW_KEY,
|
||||
LEGACY_ELEMENT_KEY, WEB_ELEMENT_KEY)
|
||||
identifiers = (CHROME_ELEMENT_KEY, FRAME_KEY, WINDOW_KEY, WEB_ELEMENT_KEY)
|
||||
|
||||
def __init__(self, marionette, id):
|
||||
self.marionette = marionette
|
||||
|
@ -187,8 +185,6 @@ class HTMLElement(object):
|
|||
if isinstance(json, dict):
|
||||
if WEB_ELEMENT_KEY in json:
|
||||
return cls(marionette, json[WEB_ELEMENT_KEY])
|
||||
elif LEGACY_ELEMENT_KEY in json:
|
||||
return cls(marionette, json[LEGACY_ELEMENT_KEY])
|
||||
elif CHROME_ELEMENT_KEY in json:
|
||||
return cls(marionette, json[CHROME_ELEMENT_KEY])
|
||||
elif FRAME_KEY in json:
|
||||
|
@ -1611,7 +1607,7 @@ class Marionette(object):
|
|||
wrapped[arg] = self._to_json(args[arg])
|
||||
elif type(args) == HTMLElement:
|
||||
wrapped = {WEB_ELEMENT_KEY: args.id,
|
||||
LEGACY_ELEMENT_KEY: args.id}
|
||||
CHROME_ELEMENT_KEY: args.id}
|
||||
elif (isinstance(args, bool) or isinstance(args, basestring) or
|
||||
isinstance(args, int) or isinstance(args, float) or args is None):
|
||||
wrapped = args
|
||||
|
|
|
@ -1443,7 +1443,6 @@ class WebElement {
|
|||
for (let key of keys) {
|
||||
switch (key) {
|
||||
case ContentWebElement.Identifier:
|
||||
case ContentWebElement.LegacyIdentifier:
|
||||
return ContentWebElement.fromJSON(json);
|
||||
|
||||
case ContentWebWindow.Identifier:
|
||||
|
@ -1502,8 +1501,7 @@ class WebElement {
|
|||
|
||||
/**
|
||||
* Checks if <var>ref<var> is a {@link WebElement} reference,
|
||||
* i.e. if it has {@link ContentWebElement.Identifier},
|
||||
* {@link ContentWebElement.LegacyIdentifier}, or
|
||||
* i.e. if it has {@link ContentWebElement.Identifier}, or
|
||||
* {@link ChromeWebElement.Identifier} as properties.
|
||||
*
|
||||
* @param {Object.<string, string>} obj
|
||||
|
@ -1517,7 +1515,6 @@ class WebElement {
|
|||
}
|
||||
|
||||
if ((ContentWebElement.Identifier in obj) ||
|
||||
(ContentWebElement.LegacyIdentifier in obj) ||
|
||||
(ContentWebWindow.Identifier in obj) ||
|
||||
(ContentWebFrame.Identifier in obj) ||
|
||||
(ChromeWebElement.Identifier in obj)) {
|
||||
|
@ -1545,26 +1542,22 @@ this.WebElement = WebElement;
|
|||
*/
|
||||
class ContentWebElement extends WebElement {
|
||||
toJSON() {
|
||||
return {
|
||||
[ContentWebElement.Identifier]: this.uuid,
|
||||
[ContentWebElement.LegacyIdentifier]: this.uuid,
|
||||
};
|
||||
return {[ContentWebElement.Identifier]: this.uuid};
|
||||
}
|
||||
|
||||
static fromJSON(json) {
|
||||
const {Identifier, LegacyIdentifier} = ContentWebElement;
|
||||
const {Identifier} = ContentWebElement;
|
||||
|
||||
if (!(Identifier in json) && !(LegacyIdentifier in json)) {
|
||||
if (!(Identifier in json)) {
|
||||
throw new InvalidArgumentError(
|
||||
pprint`Expected web element reference, got: ${json}`);
|
||||
}
|
||||
|
||||
let uuid = json[Identifier] || json[LegacyIdentifier];
|
||||
let uuid = json[Identifier];
|
||||
return new ContentWebElement(uuid);
|
||||
}
|
||||
}
|
||||
ContentWebElement.Identifier = "element-6066-11e4-a52e-4f735466cecf";
|
||||
ContentWebElement.LegacyIdentifier = "ELEMENT";
|
||||
this.ContentWebElement = ContentWebElement;
|
||||
|
||||
/**
|
||||
|
@ -1574,10 +1567,7 @@ this.ContentWebElement = ContentWebElement;
|
|||
*/
|
||||
class ContentWebWindow extends WebElement {
|
||||
toJSON() {
|
||||
return {
|
||||
[ContentWebWindow.Identifier]: this.uuid,
|
||||
[ContentWebElement.LegacyIdentifier]: this.uuid,
|
||||
};
|
||||
return {[ContentWebWindow.Identifier]: this.uuid};
|
||||
}
|
||||
|
||||
static fromJSON(json) {
|
||||
|
@ -1599,10 +1589,7 @@ this.ContentWebWindow = ContentWebWindow;
|
|||
*/
|
||||
class ContentWebFrame extends WebElement {
|
||||
toJSON() {
|
||||
return {
|
||||
[ContentWebFrame.Identifier]: this.uuid,
|
||||
[ContentWebElement.LegacyIdentifier]: this.uuid,
|
||||
};
|
||||
return {[ContentWebFrame.Identifier]: this.uuid};
|
||||
}
|
||||
|
||||
static fromJSON(json) {
|
||||
|
@ -1623,10 +1610,7 @@ this.ContentWebFrame = ContentWebFrame;
|
|||
*/
|
||||
class ChromeWebElement extends WebElement {
|
||||
toJSON() {
|
||||
return {
|
||||
[ChromeWebElement.Identifier]: this.uuid,
|
||||
[ContentWebElement.LegacyIdentifier]: this.uuid,
|
||||
};
|
||||
return {[ChromeWebElement.Identifier]: this.uuid};
|
||||
}
|
||||
|
||||
static fromJSON(json) {
|
||||
|
|
|
@ -358,37 +358,15 @@ add_test(function test_WebElement_from() {
|
|||
});
|
||||
|
||||
add_test(function test_WebElement_fromJSON_ContentWebElement() {
|
||||
const {Identifier, LegacyIdentifier} = ContentWebElement;
|
||||
const {Identifier} = ContentWebElement;
|
||||
|
||||
let refNew = {[Identifier]: "foo"};
|
||||
let webElNew = WebElement.fromJSON(refNew);
|
||||
ok(webElNew instanceof ContentWebElement);
|
||||
equal(webElNew.uuid, "foo");
|
||||
|
||||
let refOld = {[LegacyIdentifier]: "foo"};
|
||||
let webElOld = WebElement.fromJSON(refOld);
|
||||
ok(webElOld instanceof ContentWebElement);
|
||||
equal(webElOld.uuid, "foo");
|
||||
|
||||
ok(webElNew.is(webElOld));
|
||||
ok(webElOld.is(webElNew));
|
||||
|
||||
let refBoth = {
|
||||
[Identifier]: "foo",
|
||||
[LegacyIdentifier]: "foo",
|
||||
};
|
||||
let webElBoth = WebElement.fromJSON(refBoth);
|
||||
ok(webElBoth instanceof ContentWebElement);
|
||||
equal(webElBoth.uuid, "foo");
|
||||
|
||||
ok(webElBoth.is(webElNew));
|
||||
ok(webElBoth.is(webElOld));
|
||||
ok(webElNew.is(webElBoth));
|
||||
ok(webElOld.is(webElBoth));
|
||||
let ref = {[Identifier]: "foo"};
|
||||
let webEl = WebElement.fromJSON(ref);
|
||||
ok(webEl instanceof ContentWebElement);
|
||||
equal(webEl.uuid, "foo");
|
||||
|
||||
let identifierPrecedence = {
|
||||
[Identifier]: "identifier-uuid",
|
||||
[LegacyIdentifier]: "legacyidentifier-uuid",
|
||||
};
|
||||
let precedenceEl = WebElement.fromJSON(identifierPrecedence);
|
||||
ok(precedenceEl instanceof ContentWebElement);
|
||||
|
@ -450,7 +428,6 @@ add_test(function test_WebElement_isReference() {
|
|||
}
|
||||
|
||||
ok(WebElement.isReference({[ContentWebElement.Identifier]: "foo"}));
|
||||
ok(WebElement.isReference({[ContentWebElement.LegacyIdentifier]: "foo"}));
|
||||
ok(WebElement.isReference({[ContentWebWindow.Identifier]: "foo"}));
|
||||
ok(WebElement.isReference({[ContentWebFrame.Identifier]: "foo"}));
|
||||
ok(WebElement.isReference({[ChromeWebElement.Identifier]: "foo"}));
|
||||
|
@ -464,37 +441,23 @@ add_test(function test_WebElement_generateUUID() {
|
|||
});
|
||||
|
||||
add_test(function test_ContentWebElement_toJSON() {
|
||||
const {Identifier, LegacyIdentifier} = ContentWebElement;
|
||||
const {Identifier} = ContentWebElement;
|
||||
|
||||
let el = new ContentWebElement("foo");
|
||||
let json = el.toJSON();
|
||||
|
||||
ok(Identifier in json);
|
||||
ok(LegacyIdentifier in json);
|
||||
equal(json[Identifier], "foo");
|
||||
equal(json[LegacyIdentifier], "foo");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_ContentWebElement_fromJSON() {
|
||||
const {Identifier, LegacyIdentifier} = ContentWebElement;
|
||||
const {Identifier} = ContentWebElement;
|
||||
|
||||
let newEl = ContentWebElement.fromJSON({[Identifier]: "foo"});
|
||||
ok(newEl instanceof ContentWebElement);
|
||||
equal(newEl.uuid, "foo");
|
||||
|
||||
let oldEl = ContentWebElement.fromJSON({[LegacyIdentifier]: "foo"});
|
||||
ok(oldEl instanceof ContentWebElement);
|
||||
equal(oldEl.uuid, "foo");
|
||||
|
||||
let bothRef = {
|
||||
[Identifier]: "identifier-uuid",
|
||||
[LegacyIdentifier]: "legacyidentifier-foo",
|
||||
};
|
||||
let bothEl = ContentWebElement.fromJSON(bothRef);
|
||||
ok(bothEl instanceof ContentWebElement);
|
||||
equal(bothEl.uuid, "identifier-uuid");
|
||||
let el = ContentWebElement.fromJSON({[Identifier]: "foo"});
|
||||
ok(el instanceof ContentWebElement);
|
||||
equal(el.uuid, "foo");
|
||||
|
||||
Assert.throws(() => ContentWebElement.fromJSON({}), InvalidArgumentError);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ from mochitest_options import MochitestArgumentParser
|
|||
|
||||
from manifestparser import TestManifest
|
||||
from manifestparser.filters import chunk_by_slice
|
||||
from mozdevice import ADBAndroid
|
||||
from mozdevice import ADBAndroid, ADBTimeoutError
|
||||
import mozfile
|
||||
import mozinfo
|
||||
|
||||
|
@ -341,6 +341,8 @@ class RobocopTestRunner(MochitestDesktop):
|
|||
else:
|
||||
self.log.info(" %s: %s" % (category, devinfo[category]))
|
||||
self.log.info("Test root: %s" % self.device.test_root)
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except Exception as e:
|
||||
self.log.warning("Error getting device information: %s" % str(e))
|
||||
|
||||
|
|
|
@ -2813,10 +2813,13 @@ toolbar#nav-bar {
|
|||
except KeyboardInterrupt:
|
||||
self.log.info("runtests.py | Received keyboard interrupt.\n")
|
||||
status = -1
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
self.log.error(
|
||||
"Automation Error: Received unexpected exception while running application\n")
|
||||
if 'ADBTimeoutError' in repr(e):
|
||||
self.log.info("runtests.py | Device disconnected. Aborting test.\n")
|
||||
raise
|
||||
status = 1
|
||||
finally:
|
||||
self.stopServers()
|
||||
|
|
|
@ -17,7 +17,7 @@ from remoteautomation import RemoteAutomation, fennecLogcatFilters
|
|||
from runtests import MochitestDesktop, MessageLogger
|
||||
from mochitest_options import MochitestArgumentParser
|
||||
|
||||
from mozdevice import ADBAndroid
|
||||
from mozdevice import ADBAndroid, ADBTimeoutError
|
||||
import mozinfo
|
||||
|
||||
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
|
||||
|
@ -283,6 +283,8 @@ class MochiRemote(MochitestDesktop):
|
|||
else:
|
||||
self.log.info(" %s: %s" % (category, devinfo[category]))
|
||||
self.log.info("Test root: %s" % self.device.test_root)
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except Exception as e:
|
||||
self.log.warning("Error getting device information: %s" % str(e))
|
||||
|
||||
|
@ -344,21 +346,26 @@ def run_test_harness(parser, options):
|
|||
mochitest.printDeviceInfo()
|
||||
|
||||
try:
|
||||
device_exception = False
|
||||
if options.verify:
|
||||
retVal = mochitest.verifyTests(options)
|
||||
else:
|
||||
retVal = mochitest.runTests(options)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
mochitest.log.error("Automation Error: Exception caught while running tests")
|
||||
traceback.print_exc()
|
||||
try:
|
||||
mochitest.cleanup(options)
|
||||
except Exception:
|
||||
# device error cleaning up... oh well!
|
||||
traceback.print_exc()
|
||||
if isinstance(e, ADBTimeoutError):
|
||||
mochitest.log.info("Device disconnected. Will not run mochitest.cleanup().")
|
||||
device_exception = True
|
||||
else:
|
||||
try:
|
||||
mochitest.cleanup(options)
|
||||
except Exception:
|
||||
# device error cleaning up... oh well!
|
||||
traceback.print_exc()
|
||||
retVal = 1
|
||||
|
||||
if options.log_mach is None and not options.verify:
|
||||
if not device_exception and options.log_mach is None and not options.verify:
|
||||
mochitest.printDeviceInfo(printLogcat=True)
|
||||
|
||||
mochitest.message_logger.finish()
|
||||
|
|
|
@ -14,7 +14,7 @@ import mozfile
|
|||
import mozinfo
|
||||
import mozlog
|
||||
import posixpath
|
||||
from mozdevice import ADBAndroid, ADBProcessError
|
||||
from mozdevice import ADBAndroid, ADBProcessError, ADBTimeoutError
|
||||
|
||||
try:
|
||||
from mozbuild.base import MozbuildObject
|
||||
|
@ -139,6 +139,8 @@ class RemoteCPPUnitTests(cppunittests.CPPUnitTests):
|
|||
cwd=self.remote_home_dir,
|
||||
timeout=test_timeout)
|
||||
returncode = 0
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except ADBProcessError as e:
|
||||
output = e.adb_process.stdout
|
||||
returncode = e.adb_process.exitcode
|
||||
|
|
|
@ -16,7 +16,7 @@ import runxpcshelltests as xpcshell
|
|||
import tempfile
|
||||
from zipfile import ZipFile
|
||||
|
||||
from mozdevice import ADBAndroid, ADBDevice
|
||||
from mozdevice import ADBAndroid, ADBDevice, ADBTimeoutError
|
||||
import mozfile
|
||||
import mozinfo
|
||||
from mozlog import commandline
|
||||
|
@ -156,6 +156,8 @@ class RemoteXPCShellTestThread(xpcshell.XPCShellTestThread):
|
|||
adb_process = self.device.shell(cmd, timeout=timeout+10, root=True)
|
||||
output_file = adb_process.stdout_file
|
||||
self.shellReturnCode = adb_process.exitcode
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except Exception as e:
|
||||
if self.timedout:
|
||||
# If the test timed out, there is a good chance the shell
|
||||
|
@ -214,6 +216,8 @@ class RemoteXPCShellTestThread(xpcshell.XPCShellTestThread):
|
|||
def removeDir(self, dirname):
|
||||
try:
|
||||
self.device.rm(dirname, recursive=True, root=True)
|
||||
except ADBTimeoutError:
|
||||
raise
|
||||
except Exception as e:
|
||||
self.log.warning(str(e))
|
||||
|
||||
|
|
|
@ -268,8 +268,7 @@ BackgroundHangManager::BackgroundHangManager()
|
|||
|
||||
mHangMonitorThread = PR_CreateThread(
|
||||
PR_USER_THREAD, MonitorThread, this,
|
||||
PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
|
||||
nsIThreadManager::DEFAULT_STACK_SIZE);
|
||||
PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
|
||||
|
||||
MOZ_ASSERT(mHangMonitorThread, "Failed to create BHR monitor thread");
|
||||
|
||||
|
|
|
@ -3298,11 +3298,6 @@ profiler_register_thread(const char* aName, void* aGuessStackTop)
|
|||
MOZ_ASSERT_IF(NS_IsMainThread(), Scheduler::IsCooperativeThread());
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
// Make sure we have a nsThread wrapper for the current thread, and that NSPR
|
||||
// knows its name.
|
||||
(void) NS_GetCurrentThread();
|
||||
NS_SetCurrentThreadName(aName);
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
void* stackTop = GetStackTop(aGuessStackTop);
|
||||
|
|
|
@ -405,13 +405,6 @@ nsThread::ThreadList()
|
|||
return sList;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsThread::ClearThreadList()
|
||||
{
|
||||
OffTheBooksMutexAutoLock mal(ThreadListMutex());
|
||||
while (ThreadList().popFirst()) {}
|
||||
}
|
||||
|
||||
/* static */ nsThreadEnumerator
|
||||
nsThread::Enumerate()
|
||||
{
|
||||
|
@ -427,6 +420,7 @@ nsThread::ThreadFunc(void* aArg)
|
|||
nsThread* self = initData->thread; // strong reference
|
||||
|
||||
self->mThread = PR_GetCurrentThread();
|
||||
self->mThreadId = uint32_t(PlatformThread::CurrentId());
|
||||
self->mVirtualThread = GetCurrentVirtualThread();
|
||||
self->mEventTarget->SetCurrentThread();
|
||||
SetupCurrentThreadForChaosMode();
|
||||
|
@ -435,7 +429,71 @@ nsThread::ThreadFunc(void* aArg)
|
|||
NS_SetCurrentThreadName(initData->name.BeginReading());
|
||||
}
|
||||
|
||||
self->InitCommon();
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_getattr_np(pthread_self(), &attr);
|
||||
|
||||
size_t stackSize;
|
||||
pthread_attr_getstack(&attr, &self->mStackBase, &stackSize);
|
||||
|
||||
// Glibc prior to 2.27 reports the stack size and base including the guard
|
||||
// region, so we need to compensate for it to get accurate accounting.
|
||||
// Also, this behavior difference isn't guarded by a versioned symbol, so we
|
||||
// actually need to check the runtime glibc version, not the version we were
|
||||
// compiled against.
|
||||
static bool sAdjustForGuardSize = ({
|
||||
#ifdef __GLIBC__
|
||||
unsigned major, minor;
|
||||
sscanf(gnu_get_libc_version(), "%u.%u", &major, &minor) < 2 ||
|
||||
major < 2 || (major == 2 && minor < 27);
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
});
|
||||
if (sAdjustForGuardSize) {
|
||||
size_t guardSize;
|
||||
pthread_attr_getguardsize(&attr, &guardSize);
|
||||
|
||||
// Note: This assumes that the stack grows down, as is the case on all of
|
||||
// our tier 1 platforms. On platforms where the stack grows up, the
|
||||
// mStackBase adjustment is unnecessary, but doesn't cause any harm other
|
||||
// than under-counting stack memory usage by one page.
|
||||
self->mStackBase = reinterpret_cast<char*>(self->mStackBase) + guardSize;
|
||||
stackSize -= guardSize;
|
||||
}
|
||||
|
||||
self->mStackSize = stackSize;
|
||||
|
||||
// This is a bit of a hack.
|
||||
//
|
||||
// We really do want the NOHUGEPAGE flag on our thread stacks, since we
|
||||
// don't expect any of them to need anywhere near 2MB of space. But setting
|
||||
// it here is too late to have an effect, since the first stack page has
|
||||
// already been faulted in existence, and NSPR doesn't give us a way to set
|
||||
// it beforehand.
|
||||
//
|
||||
// What this does get us, however, is a different set of VM flags on our
|
||||
// thread stacks compared to normal heap memory. Which makes the Linux
|
||||
// kernel report them as separate regions, even when they are adjacent to
|
||||
// heap memory. This allows us to accurately track the actual memory
|
||||
// consumption of our allocated stacks.
|
||||
madvise(self->mStackBase, stackSize, MADV_NOHUGEPAGE);
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
#elif defined(XP_WIN)
|
||||
static const DynamicallyLinkedFunctionPtr<GetCurrentThreadStackLimitsFn>
|
||||
sGetStackLimits(L"kernel32.dll", "GetCurrentThreadStackLimits");
|
||||
|
||||
if (sGetStackLimits) {
|
||||
ULONG_PTR stackBottom, stackTop;
|
||||
sGetStackLimits(&stackBottom, &stackTop);
|
||||
self->mStackBase = reinterpret_cast<void*>(stackBottom);
|
||||
self->mStackSize = stackTop - stackBottom;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Inform the ThreadManager
|
||||
nsThreadManager::get().RegisterCurrentThread(*self);
|
||||
|
@ -516,81 +574,6 @@ nsThread::ThreadFunc(void* aArg)
|
|||
NS_RELEASE(self);
|
||||
}
|
||||
|
||||
void
|
||||
nsThread::InitCommon()
|
||||
{
|
||||
mThreadId = uint32_t(PlatformThread::CurrentId());
|
||||
|
||||
{
|
||||
#if defined(XP_LINUX)
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_getattr_np(pthread_self(), &attr);
|
||||
|
||||
size_t stackSize;
|
||||
pthread_attr_getstack(&attr, &mStackBase, &stackSize);
|
||||
|
||||
// Glibc prior to 2.27 reports the stack size and base including the guard
|
||||
// region, so we need to compensate for it to get accurate accounting.
|
||||
// Also, this behavior difference isn't guarded by a versioned symbol, so we
|
||||
// actually need to check the runtime glibc version, not the version we were
|
||||
// compiled against.
|
||||
static bool sAdjustForGuardSize = ({
|
||||
#ifdef __GLIBC__
|
||||
unsigned major, minor;
|
||||
sscanf(gnu_get_libc_version(), "%u.%u", &major, &minor) < 2 ||
|
||||
major < 2 || (major == 2 && minor < 27);
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
});
|
||||
if (sAdjustForGuardSize) {
|
||||
size_t guardSize;
|
||||
pthread_attr_getguardsize(&attr, &guardSize);
|
||||
|
||||
// Note: This assumes that the stack grows down, as is the case on all of
|
||||
// our tier 1 platforms. On platforms where the stack grows up, the
|
||||
// mStackBase adjustment is unnecessary, but doesn't cause any harm other
|
||||
// than under-counting stack memory usage by one page.
|
||||
mStackBase = reinterpret_cast<char*>(mStackBase) + guardSize;
|
||||
stackSize -= guardSize;
|
||||
}
|
||||
|
||||
mStackSize = stackSize;
|
||||
|
||||
// This is a bit of a hack.
|
||||
//
|
||||
// We really do want the NOHUGEPAGE flag on our thread stacks, since we
|
||||
// don't expect any of them to need anywhere near 2MB of space. But setting
|
||||
// it here is too late to have an effect, since the first stack page has
|
||||
// already been faulted in existence, and NSPR doesn't give us a way to set
|
||||
// it beforehand.
|
||||
//
|
||||
// What this does get us, however, is a different set of VM flags on our
|
||||
// thread stacks compared to normal heap memory. Which makes the Linux
|
||||
// kernel report them as separate regions, even when they are adjacent to
|
||||
// heap memory. This allows us to accurately track the actual memory
|
||||
// consumption of our allocated stacks.
|
||||
madvise(mStackBase, stackSize, MADV_NOHUGEPAGE);
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
#elif defined(XP_WIN)
|
||||
static const DynamicallyLinkedFunctionPtr<GetCurrentThreadStackLimitsFn>
|
||||
sGetStackLimits(L"kernel32.dll", "GetCurrentThreadStackLimits");
|
||||
|
||||
if (sGetStackLimits) {
|
||||
ULONG_PTR stackBottom, stackTop;
|
||||
sGetStackLimits(&stackBottom, &stackTop);
|
||||
mStackBase = reinterpret_cast<void*>(stackBottom);
|
||||
mStackSize = stackTop - stackBottom;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
OffTheBooksMutexAutoLock mal(ThreadListMutex());
|
||||
ThreadList().insertBack(this);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Tell the crash reporter to save a memory report if our heuristics determine
|
||||
|
@ -687,17 +670,7 @@ nsThread::~nsThread()
|
|||
{
|
||||
NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
|
||||
"shouldn't be waiting on other threads to shutdown");
|
||||
|
||||
// We shouldn't need to lock before checking isInList at this point. We're
|
||||
// destroying the last reference to this object, so there's no way for anyone
|
||||
// else to remove it in the middle of our check. And the not-in-list state is
|
||||
// determined the element's next and previous members pointing to itself, so a
|
||||
// non-atomic update to an adjacent member won't affect the outcome either.
|
||||
if (isInList()) {
|
||||
OffTheBooksMutexAutoLock mal(ThreadListMutex());
|
||||
removeFrom(ThreadList());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!isInList());
|
||||
#ifdef DEBUG
|
||||
// We deliberately leak these so they can be tracked by the leak checker.
|
||||
// If you're having nsThreadShutdownContext leaks, you can set:
|
||||
|
@ -731,6 +704,11 @@ nsThread::Init(const nsACString& aName)
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
{
|
||||
OffTheBooksMutexAutoLock mal(ThreadListMutex());
|
||||
ThreadList().insertBack(this);
|
||||
}
|
||||
|
||||
// ThreadFunc will wait for this event to be run before it tries to access
|
||||
// mThread. By delaying insertion of this event into the queue, we ensure
|
||||
// that mThread is set properly.
|
||||
|
@ -750,7 +728,6 @@ nsThread::InitCurrentThread()
|
|||
mThread = PR_GetCurrentThread();
|
||||
mVirtualThread = GetCurrentVirtualThread();
|
||||
SetupCurrentThreadForChaosMode();
|
||||
InitCommon();
|
||||
|
||||
nsThreadManager::get().RegisterCurrentThread(*this);
|
||||
return NS_OK;
|
||||
|
@ -879,13 +856,6 @@ nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext)
|
|||
MOZ_ASSERT(mThread);
|
||||
MOZ_ASSERT(aContext->mTerminatingThread == this);
|
||||
|
||||
{
|
||||
OffTheBooksMutexAutoLock mal(ThreadListMutex());
|
||||
if (isInList()) {
|
||||
removeFrom(ThreadList());
|
||||
}
|
||||
}
|
||||
|
||||
if (aContext->mAwaitingShutdownAck) {
|
||||
// We're in a synchronous shutdown, so tell whatever is up the stack that
|
||||
// we're done and unwind the stack so it can call us again.
|
||||
|
|
|
@ -66,12 +66,6 @@ public:
|
|||
// Initialize this as a wrapper for the current PRThread.
|
||||
nsresult InitCurrentThread();
|
||||
|
||||
private:
|
||||
// Initializes the mThreadId and stack base/size members, and adds the thread
|
||||
// to the ThreadList().
|
||||
void InitCommon();
|
||||
|
||||
public:
|
||||
// The PRThread corresponding to this thread.
|
||||
PRThread* GetPRThread()
|
||||
{
|
||||
|
@ -176,11 +170,8 @@ protected:
|
|||
|
||||
struct nsThreadShutdownContext* ShutdownInternal(bool aSync);
|
||||
|
||||
friend class nsThreadManager;
|
||||
|
||||
static mozilla::OffTheBooksMutex& ThreadListMutex();
|
||||
static mozilla::LinkedList<nsThread>& ThreadList();
|
||||
static void ClearThreadList();
|
||||
|
||||
RefPtr<mozilla::SynchronizedEventQueue> mEvents;
|
||||
RefPtr<mozilla::ThreadEventTarget> mEventTarget;
|
||||
|
@ -214,8 +205,6 @@ protected:
|
|||
// Set to true if this thread creates a JSRuntime.
|
||||
bool mCanInvokeJS;
|
||||
|
||||
bool mHasTLSEntry = false;
|
||||
|
||||
// Used to track which event is being executed by ProcessNextEvent
|
||||
nsCOMPtr<nsIRunnable> mCurrentEvent;
|
||||
|
||||
|
|
|
@ -94,27 +94,12 @@ AssertIsOnMainThread()
|
|||
|
||||
typedef nsTArray<NotNull<RefPtr<nsThread>>> nsThreadArray;
|
||||
|
||||
static bool sShutdownComplete;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/* static */ void
|
||||
nsThreadManager::ReleaseThread(void* aData)
|
||||
static void
|
||||
ReleaseObject(void* aData)
|
||||
{
|
||||
if (sShutdownComplete) {
|
||||
// We've already completed shutdown and released the references to all or
|
||||
// our TLS wrappers. Don't try to release them again.
|
||||
return;
|
||||
}
|
||||
|
||||
auto* thread = static_cast<nsThread*>(aData);
|
||||
|
||||
get().UnregisterCurrentThread(*thread, true);
|
||||
|
||||
if (thread->mHasTLSEntry) {
|
||||
thread->mHasTLSEntry = false;
|
||||
thread->Release();
|
||||
}
|
||||
static_cast<nsISupports*>(aData)->Release();
|
||||
}
|
||||
|
||||
// statically allocated instance
|
||||
|
@ -251,7 +236,7 @@ nsThreadManager::Init()
|
|||
|
||||
Scheduler::EventLoopActivation::Init();
|
||||
|
||||
if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseThread) == PR_FAILURE) {
|
||||
if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -319,34 +304,32 @@ nsThreadManager::Shutdown()
|
|||
// Empty the main thread event queue before we begin shutting down threads.
|
||||
NS_ProcessPendingEvents(mMainThread);
|
||||
|
||||
// We gather the threads from the hashtable into a list, so that we avoid
|
||||
// holding the hashtable lock while calling nsIThread::Shutdown.
|
||||
nsThreadArray threads;
|
||||
{
|
||||
// We gather the threads from the hashtable into a list, so that we avoid
|
||||
// holding the hashtable lock while calling nsIThread::Shutdown.
|
||||
nsThreadArray threads;
|
||||
{
|
||||
OffTheBooksMutexAutoLock lock(mLock);
|
||||
for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
|
||||
RefPtr<nsThread>& thread = iter.Data();
|
||||
threads.AppendElement(WrapNotNull(thread));
|
||||
iter.Remove();
|
||||
}
|
||||
OffTheBooksMutexAutoLock lock(mLock);
|
||||
for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
|
||||
RefPtr<nsThread>& thread = iter.Data();
|
||||
threads.AppendElement(WrapNotNull(thread));
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
// It's tempting to walk the list of threads here and tell them each to stop
|
||||
// accepting new events, but that could lead to badness if one of those
|
||||
// threads is stuck waiting for a response from another thread. To do it
|
||||
// right, we'd need some way to interrupt the threads.
|
||||
//
|
||||
// Instead, we process events on the current thread while waiting for threads
|
||||
// to shutdown. This means that we have to preserve a mostly functioning
|
||||
// world until such time as the threads exit.
|
||||
// It's tempting to walk the list of threads here and tell them each to stop
|
||||
// accepting new events, but that could lead to badness if one of those
|
||||
// threads is stuck waiting for a response from another thread. To do it
|
||||
// right, we'd need some way to interrupt the threads.
|
||||
//
|
||||
// Instead, we process events on the current thread while waiting for threads
|
||||
// to shutdown. This means that we have to preserve a mostly functioning
|
||||
// world until such time as the threads exit.
|
||||
|
||||
// Shutdown all threads that require it (join with threads that we created).
|
||||
for (uint32_t i = 0; i < threads.Length(); ++i) {
|
||||
NotNull<nsThread*> thread = threads[i];
|
||||
if (thread->ShutdownRequired()) {
|
||||
thread->Shutdown();
|
||||
}
|
||||
// Shutdown all threads that require it (join with threads that we created).
|
||||
for (uint32_t i = 0; i < threads.Length(); ++i) {
|
||||
NotNull<nsThread*> thread = threads[i];
|
||||
if (thread->ShutdownRequired()) {
|
||||
thread->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,24 +360,6 @@ nsThreadManager::Shutdown()
|
|||
|
||||
// Remove the TLS entry for the main thread.
|
||||
PR_SetThreadPrivate(mCurThreadIndex, nullptr);
|
||||
|
||||
{
|
||||
// Cleanup the last references to any threads which haven't shut down yet.
|
||||
nsTArray<RefPtr<nsThread>> threads;
|
||||
for (auto* thread : nsThread::Enumerate()) {
|
||||
if (thread->mHasTLSEntry) {
|
||||
threads.AppendElement(dont_AddRef(thread));
|
||||
thread->mHasTLSEntry = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// xpcshell tests sometimes leak the main thread. They don't enable leak
|
||||
// checking, so that doesn't cause the test to fail, but leaving the entry in
|
||||
// the thread list triggers an assertion, which does.
|
||||
nsThread::ClearThreadList();
|
||||
|
||||
sShutdownComplete = true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -412,28 +377,21 @@ nsThreadManager::RegisterCurrentThread(nsThread& aThread)
|
|||
mThreadsByPRThread.Put(aThread.GetPRThread(), &aThread); // XXX check OOM?
|
||||
|
||||
aThread.AddRef(); // for TLS entry
|
||||
aThread.mHasTLSEntry = true;
|
||||
PR_SetThreadPrivate(mCurThreadIndex, &aThread);
|
||||
}
|
||||
|
||||
void
|
||||
nsThreadManager::UnregisterCurrentThread(nsThread& aThread, bool aIfExists)
|
||||
nsThreadManager::UnregisterCurrentThread(nsThread& aThread)
|
||||
{
|
||||
MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
|
||||
|
||||
{
|
||||
OffTheBooksMutexAutoLock lock(mLock);
|
||||
OffTheBooksMutexAutoLock lock(mLock);
|
||||
|
||||
if (aIfExists && !mThreadsByPRThread.GetWeak(aThread.GetPRThread())) {
|
||||
return;
|
||||
}
|
||||
|
||||
--mCurrentNumberOfThreads;
|
||||
mThreadsByPRThread.Remove(aThread.GetPRThread());
|
||||
}
|
||||
--mCurrentNumberOfThreads;
|
||||
mThreadsByPRThread.Remove(aThread.GetPRThread());
|
||||
|
||||
PR_SetThreadPrivate(mCurThreadIndex, nullptr);
|
||||
// Ref-count balanced via ReleaseThread
|
||||
// Ref-count balanced via ReleaseObject
|
||||
}
|
||||
|
||||
nsThread*
|
||||
|
@ -483,13 +441,7 @@ nsThreadManager::GetCurrentThread()
|
|||
bool
|
||||
nsThreadManager::IsNSThread() const
|
||||
{
|
||||
if (!mInitialized) {
|
||||
return false;
|
||||
}
|
||||
if (auto* thread = (nsThread*)PR_GetThreadPrivate(mCurThreadIndex)) {
|
||||
return thread->mShutdownRequired;
|
||||
}
|
||||
return false;
|
||||
return mInitialized && !!PR_GetThreadPrivate(mCurThreadIndex);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
|
||||
// Called by nsThread to inform the ThreadManager it is going away. This
|
||||
// method must be called when the given thread is the current thread.
|
||||
void UnregisterCurrentThread(nsThread& aThread, bool aIfExists = false);
|
||||
void UnregisterCurrentThread(nsThread& aThread);
|
||||
|
||||
// Returns the current thread. Returns null if OOM or if ThreadManager isn't
|
||||
// initialized. Creates the nsThread if one does not exist yet.
|
||||
|
@ -85,8 +85,6 @@ private:
|
|||
SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition,
|
||||
bool aCheckingShutdown);
|
||||
|
||||
static void ReleaseThread(void* aData);
|
||||
|
||||
nsRefPtrHashtable<nsPtrHashKey<PRThread>, nsThread> mThreadsByPRThread;
|
||||
unsigned mCurThreadIndex; // thread-local-storage index
|
||||
RefPtr<nsThread> mMainThread;
|
||||
|
|
Загрузка…
Ссылка в новой задаче