Moving src/webrtc into src/.
In order to eliminate the WebRTC Subtree mirror in Chromium, WebRTC is moving the content of the src/webrtc directory up to the src/ directory. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true TBR=tommi@webrtc.org Bug: chromium:611808 Change-Id: Iac59c5b51b950f174119565bac87955a7994bc38 Reviewed-on: https://webrtc-review.googlesource.com/1560 Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Henrik Kjellander <kjellander@webrtc.org> Cr-Commit-Position: refs/heads/master@{#19845}
This commit is contained in:
committed by
Commit Bot
parent
6674846b4a
commit
bb547203bf
78
modules/utility/BUILD.gn
Normal file
78
modules/utility/BUILD.gn
Normal file
@ -0,0 +1,78 @@
|
||||
# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style license
|
||||
# that can be found in the LICENSE file in the root of the source
|
||||
# tree. An additional intellectual property rights grant can be found
|
||||
# in the file PATENTS. All contributing project authors may
|
||||
# be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
import("../../webrtc.gni")
|
||||
|
||||
rtc_static_library("utility") {
|
||||
sources = [
|
||||
"include/audio_frame_operations.h",
|
||||
"include/helpers_android.h",
|
||||
"include/jvm_android.h",
|
||||
"include/process_thread.h",
|
||||
"source/helpers_android.cc",
|
||||
"source/jvm_android.cc",
|
||||
"source/process_thread_impl.cc",
|
||||
"source/process_thread_impl.h",
|
||||
]
|
||||
|
||||
if (!build_with_chromium && is_clang) {
|
||||
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
|
||||
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
|
||||
}
|
||||
|
||||
if (is_ios) {
|
||||
libs = [ "AVFoundation.framework" ]
|
||||
}
|
||||
|
||||
deps = [
|
||||
"..:module_api",
|
||||
"../..:webrtc_common",
|
||||
"../../audio/utility:audio_frame_operations",
|
||||
"../../common_audio",
|
||||
"../../rtc_base:rtc_base_approved",
|
||||
"../../rtc_base:rtc_task_queue",
|
||||
"../../system_wrappers",
|
||||
"../media_file",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_source_set("mock_process_thread") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"include/mock/mock_process_thread.h",
|
||||
]
|
||||
deps = [
|
||||
":utility",
|
||||
"../../rtc_base:rtc_base_approved",
|
||||
"../../test:test_support",
|
||||
]
|
||||
}
|
||||
|
||||
if (rtc_include_tests) {
|
||||
rtc_source_set("utility_unittests") {
|
||||
testonly = true
|
||||
|
||||
# Skip restricting visibility on mobile platforms since the tests on those
|
||||
# gets additional generated targets which would require many lines here to
|
||||
# cover (which would be confusing to read and hard to maintain).
|
||||
if (!is_android && !is_ios) {
|
||||
visibility = [ "..:modules_unittests" ]
|
||||
}
|
||||
sources = [
|
||||
"source/process_thread_impl_unittest.cc",
|
||||
]
|
||||
deps = [
|
||||
":utility",
|
||||
"..:module_api",
|
||||
"../../rtc_base:rtc_base_approved",
|
||||
"../../rtc_base:rtc_task_queue",
|
||||
"../../test:test_support",
|
||||
"//testing/gmock",
|
||||
]
|
||||
}
|
||||
}
|
||||
8
modules/utility/DEPS
Normal file
8
modules/utility/DEPS
Normal file
@ -0,0 +1,8 @@
|
||||
include_rules = [
|
||||
# TODO(aleloi): remove when clients update. See
|
||||
# bugs.webrtc.org/6548.
|
||||
"+webrtc/audio/utility/audio_frame_operations.h",
|
||||
"+webrtc/common_audio",
|
||||
"+webrtc/common_video",
|
||||
"+webrtc/system_wrappers",
|
||||
]
|
||||
6
modules/utility/OWNERS
Normal file
6
modules/utility/OWNERS
Normal file
@ -0,0 +1,6 @@
|
||||
perkj@webrtc.org
|
||||
|
||||
# These are for the common case of adding or renaming files. If you're doing
|
||||
# structural changes, please get a review from a reviewer in this file.
|
||||
per-file *.gn=*
|
||||
per-file *.gni=*
|
||||
20
modules/utility/include/audio_frame_operations.h
Normal file
20
modules/utility/include/audio_frame_operations.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_UTILITY_INCLUDE_AUDIO_FRAME_OPERATIONS_H_
|
||||
#define WEBRTC_MODULES_UTILITY_INCLUDE_AUDIO_FRAME_OPERATIONS_H_
|
||||
// The contents of this file have moved to
|
||||
// //webrtc/audio/utility. This file is deprecated.
|
||||
|
||||
// TODO(aleloi): Remove this file when clients have updated their
|
||||
// includes. See bugs.webrtc.org/6548.
|
||||
#include "webrtc/audio/utility/audio_frame_operations.h"
|
||||
|
||||
#endif // #ifndef WEBRTC_MODULES_UTILITY_INCLUDE_AUDIO_FRAME_OPERATIONS_H_
|
||||
87
modules/utility/include/helpers_android.h
Normal file
87
modules/utility/include/helpers_android.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_UTILITY_INCLUDE_HELPERS_ANDROID_H_
|
||||
#define WEBRTC_MODULES_UTILITY_INCLUDE_HELPERS_ANDROID_H_
|
||||
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
// Abort the process if |jni| has a Java exception pending.
|
||||
// TODO(henrika): merge with CHECK_JNI_EXCEPTION() in jni_helpers.h.
|
||||
#define CHECK_EXCEPTION(jni) \
|
||||
RTC_CHECK(!jni->ExceptionCheck()) \
|
||||
<< (jni->ExceptionDescribe(), jni->ExceptionClear(), "")
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached.
|
||||
JNIEnv* GetEnv(JavaVM* jvm);
|
||||
|
||||
// Return a |jlong| that will correctly convert back to |ptr|. This is needed
|
||||
// because the alternative (of silently passing a 32-bit pointer to a vararg
|
||||
// function expecting a 64-bit param) picks up garbage in the high 32 bits.
|
||||
jlong PointerTojlong(void* ptr);
|
||||
|
||||
// JNIEnv-helper methods that wraps the API which uses the JNI interface
|
||||
// pointer (JNIEnv*). It allows us to RTC_CHECK success and that no Java
|
||||
// exception is thrown while calling the method.
|
||||
jmethodID GetMethodID(
|
||||
JNIEnv* jni, jclass c, const char* name, const char* signature);
|
||||
|
||||
jmethodID GetStaticMethodID(
|
||||
JNIEnv* jni, jclass c, const char* name, const char* signature);
|
||||
|
||||
jclass FindClass(JNIEnv* jni, const char* name);
|
||||
|
||||
jobject NewGlobalRef(JNIEnv* jni, jobject o);
|
||||
|
||||
void DeleteGlobalRef(JNIEnv* jni, jobject o);
|
||||
|
||||
// Return thread ID as a string.
|
||||
std::string GetThreadId();
|
||||
|
||||
// Return thread ID as string suitable for debug logging.
|
||||
std::string GetThreadInfo();
|
||||
|
||||
// Attach thread to JVM if necessary and detach at scope end if originally
|
||||
// attached.
|
||||
class AttachThreadScoped {
|
||||
public:
|
||||
explicit AttachThreadScoped(JavaVM* jvm);
|
||||
~AttachThreadScoped();
|
||||
JNIEnv* env();
|
||||
|
||||
private:
|
||||
bool attached_;
|
||||
JavaVM* jvm_;
|
||||
JNIEnv* env_;
|
||||
};
|
||||
|
||||
// Scoped holder for global Java refs.
|
||||
template<class T> // T is jclass, jobject, jintArray, etc.
|
||||
class ScopedGlobalRef {
|
||||
public:
|
||||
ScopedGlobalRef(JNIEnv* jni, T obj)
|
||||
: jni_(jni), obj_(static_cast<T>(NewGlobalRef(jni, obj))) {}
|
||||
~ScopedGlobalRef() {
|
||||
DeleteGlobalRef(jni_, obj_);
|
||||
}
|
||||
T operator*() const {
|
||||
return obj_;
|
||||
}
|
||||
private:
|
||||
JNIEnv* jni_;
|
||||
T obj_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_UTILITY_INCLUDE_HELPERS_ANDROID_H_
|
||||
188
modules/utility/include/jvm_android.h
Normal file
188
modules/utility/include/jvm_android.h
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_UTILITY_INCLUDE_JVM_ANDROID_H_
|
||||
#define WEBRTC_MODULES_UTILITY_INCLUDE_JVM_ANDROID_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "webrtc/modules/utility/include/helpers_android.h"
|
||||
#include "webrtc/rtc_base/thread_checker.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// The JNI interface pointer (JNIEnv) is valid only in the current thread.
|
||||
// Should another thread need to access the Java VM, it must first call
|
||||
// AttachCurrentThread() to attach itself to the VM and obtain a JNI interface
|
||||
// pointer. The native thread remains attached to the VM until it calls
|
||||
// DetachCurrentThread() to detach.
|
||||
class AttachCurrentThreadIfNeeded {
|
||||
public:
|
||||
AttachCurrentThreadIfNeeded();
|
||||
~AttachCurrentThreadIfNeeded();
|
||||
|
||||
private:
|
||||
rtc::ThreadChecker thread_checker_;
|
||||
bool attached_;
|
||||
};
|
||||
|
||||
// This class is created by the NativeRegistration class and is used to wrap
|
||||
// the actual Java object handle (jobject) on which we can call methods from
|
||||
// C++ in to Java. See example in JVM for more details.
|
||||
// TODO(henrika): extend support for type of function calls.
|
||||
class GlobalRef {
|
||||
public:
|
||||
GlobalRef(JNIEnv* jni, jobject object);
|
||||
~GlobalRef();
|
||||
|
||||
jboolean CallBooleanMethod(jmethodID methodID, ...);
|
||||
jint CallIntMethod(jmethodID methodID, ...);
|
||||
void CallVoidMethod(jmethodID methodID, ...);
|
||||
|
||||
private:
|
||||
JNIEnv* const jni_;
|
||||
const jobject j_object_;
|
||||
};
|
||||
|
||||
// Wraps the jclass object on which we can call GetMethodId() functions to
|
||||
// query method IDs.
|
||||
class JavaClass {
|
||||
public:
|
||||
JavaClass(JNIEnv* jni, jclass clazz) : jni_(jni), j_class_(clazz) {}
|
||||
~JavaClass() {}
|
||||
|
||||
jmethodID GetMethodId(const char* name, const char* signature);
|
||||
jmethodID GetStaticMethodId(const char* name, const char* signature);
|
||||
jobject CallStaticObjectMethod(jmethodID methodID, ...);
|
||||
jint CallStaticIntMethod(jmethodID methodID, ...);
|
||||
|
||||
protected:
|
||||
JNIEnv* const jni_;
|
||||
jclass const j_class_;
|
||||
};
|
||||
|
||||
// Adds support of the NewObject factory method to the JavaClass class.
|
||||
// See example in JVM for more details on how to use it.
|
||||
class NativeRegistration : public JavaClass {
|
||||
public:
|
||||
NativeRegistration(JNIEnv* jni, jclass clazz);
|
||||
~NativeRegistration();
|
||||
|
||||
std::unique_ptr<GlobalRef> NewObject(
|
||||
const char* name, const char* signature, ...);
|
||||
|
||||
private:
|
||||
JNIEnv* const jni_;
|
||||
};
|
||||
|
||||
// This class is created by the JVM class and is used to expose methods that
|
||||
// needs the JNI interface pointer but its main purpose is to create a
|
||||
// NativeRegistration object given name of a Java class and a list of native
|
||||
// methods. See example in JVM for more details.
|
||||
class JNIEnvironment {
|
||||
public:
|
||||
explicit JNIEnvironment(JNIEnv* jni);
|
||||
~JNIEnvironment();
|
||||
|
||||
// Registers native methods with the Java class specified by |name|.
|
||||
// Note that the class name must be one of the names in the static
|
||||
// |loaded_classes| array defined in jvm_android.cc.
|
||||
// This method must be called on the construction thread.
|
||||
std::unique_ptr<NativeRegistration> RegisterNatives(
|
||||
const char* name, const JNINativeMethod *methods, int num_methods);
|
||||
|
||||
// Converts from Java string to std::string.
|
||||
// This method must be called on the construction thread.
|
||||
std::string JavaToStdString(const jstring& j_string);
|
||||
|
||||
private:
|
||||
rtc::ThreadChecker thread_checker_;
|
||||
JNIEnv* const jni_;
|
||||
};
|
||||
|
||||
// Main class for working with Java from C++ using JNI in WebRTC.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// // At initialization (e.g. in JNI_OnLoad), call JVM::Initialize.
|
||||
// JNIEnv* jni = ::base::android::AttachCurrentThread();
|
||||
// JavaVM* jvm = NULL;
|
||||
// jni->GetJavaVM(&jvm);
|
||||
// webrtc::JVM::Initialize(jvm);
|
||||
//
|
||||
// // Header (.h) file of example class called User.
|
||||
// std::unique_ptr<JNIEnvironment> env;
|
||||
// std::unique_ptr<NativeRegistration> reg;
|
||||
// std::unique_ptr<GlobalRef> obj;
|
||||
//
|
||||
// // Construction (in .cc file) of User class.
|
||||
// User::User() {
|
||||
// // Calling thread must be attached to the JVM.
|
||||
// env = JVM::GetInstance()->environment();
|
||||
// reg = env->RegisterNatives("org/webrtc/WebRtcTest", ,);
|
||||
// obj = reg->NewObject("<init>", ,);
|
||||
// }
|
||||
//
|
||||
// // Each User method can now use |reg| and |obj| and call Java functions
|
||||
// // in WebRtcTest.java, e.g. boolean init() {}.
|
||||
// bool User::Foo() {
|
||||
// jmethodID id = reg->GetMethodId("init", "()Z");
|
||||
// return obj->CallBooleanMethod(id);
|
||||
// }
|
||||
//
|
||||
// // And finally, e.g. in JNI_OnUnLoad, call JVM::Uninitialize.
|
||||
// JVM::Uninitialize();
|
||||
class JVM {
|
||||
public:
|
||||
// Stores global handles to the Java VM interface.
|
||||
// Should be called once on a thread that is attached to the JVM.
|
||||
static void Initialize(JavaVM* jvm);
|
||||
// Like the method above but also passes the context to the ContextUtils
|
||||
// class. This method should be used by pure-C++ Android users that can't call
|
||||
// ContextUtils.initialize directly.
|
||||
static void Initialize(JavaVM* jvm, jobject context);
|
||||
// Clears handles stored in Initialize(). Must be called on same thread as
|
||||
// Initialize().
|
||||
static void Uninitialize();
|
||||
// Gives access to the global Java VM interface pointer, which then can be
|
||||
// used to create a valid JNIEnvironment object or to get a JavaClass object.
|
||||
static JVM* GetInstance();
|
||||
|
||||
// Creates a JNIEnvironment object.
|
||||
// This method returns a NULL pointer if AttachCurrentThread() has not been
|
||||
// called successfully. Use the AttachCurrentThreadIfNeeded class if needed.
|
||||
std::unique_ptr<JNIEnvironment> environment();
|
||||
|
||||
// Returns a JavaClass object given class |name|.
|
||||
// Note that the class name must be one of the names in the static
|
||||
// |loaded_classes| array defined in jvm_android.cc.
|
||||
// This method must be called on the construction thread.
|
||||
JavaClass GetClass(const char* name);
|
||||
|
||||
// TODO(henrika): can we make these private?
|
||||
JavaVM* jvm() const { return jvm_; }
|
||||
|
||||
protected:
|
||||
JVM(JavaVM* jvm);
|
||||
~JVM();
|
||||
|
||||
private:
|
||||
JNIEnv* jni() const { return GetEnv(jvm_); }
|
||||
|
||||
rtc::ThreadChecker thread_checker_;
|
||||
JavaVM* const jvm_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_UTILITY_INCLUDE_JVM_ANDROID_H_
|
||||
44
modules/utility/include/mock/mock_process_thread.h
Normal file
44
modules/utility/include/mock/mock_process_thread.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_UTILITY_INCLUDE_MOCK_MOCK_PROCESS_THREAD_H_
|
||||
#define WEBRTC_MODULES_UTILITY_INCLUDE_MOCK_MOCK_PROCESS_THREAD_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/modules/utility/include/process_thread.h"
|
||||
#include "webrtc/rtc_base/location.h"
|
||||
#include "webrtc/test/gmock.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class MockProcessThread : public ProcessThread {
|
||||
public:
|
||||
// TODO(nisse): Valid overrides commented out, because the gmock
|
||||
// methods don't use any override declarations, and we want to avoid
|
||||
// warnings from -Winconsistent-missing-override. See
|
||||
// http://crbug.com/428099.
|
||||
MOCK_METHOD0(Start, void());
|
||||
MOCK_METHOD0(Stop, void());
|
||||
MOCK_METHOD1(WakeUp, void(Module* module));
|
||||
MOCK_METHOD1(PostTask, void(rtc::QueuedTask* task));
|
||||
MOCK_METHOD2(RegisterModule, void(Module* module, const rtc::Location&));
|
||||
MOCK_METHOD1(DeRegisterModule, void(Module* module));
|
||||
|
||||
// MOCK_METHOD1 gets confused with mocking this method, so we work around it
|
||||
// by overriding the method from the interface and forwarding the call to a
|
||||
// mocked, simpler method.
|
||||
void PostTask(std::unique_ptr<rtc::QueuedTask> task) /*override*/ {
|
||||
PostTask(task.get());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // WEBRTC_MODULES_UTILITY_INCLUDE_MOCK_MOCK_PROCESS_THREAD_H_
|
||||
77
modules/utility/include/process_thread.h
Normal file
77
modules/utility/include/process_thread.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_UTILITY_INCLUDE_PROCESS_THREAD_H_
|
||||
#define WEBRTC_MODULES_UTILITY_INCLUDE_PROCESS_THREAD_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
#if defined(WEBRTC_WIN)
|
||||
// Due to a bug in the std::unique_ptr implementation that ships with MSVS,
|
||||
// we need the full definition of QueuedTask, on Windows.
|
||||
#include "webrtc/rtc_base/task_queue.h"
|
||||
#else
|
||||
namespace rtc {
|
||||
class QueuedTask;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace rtc {
|
||||
class Location;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
class Module;
|
||||
|
||||
// TODO(tommi): ProcessThread probably doesn't need to be a virtual
|
||||
// interface. There exists one override besides ProcessThreadImpl,
|
||||
// MockProcessThread, but when looking at how it is used, it seems
|
||||
// a nullptr might suffice (or simply an actual ProcessThread instance).
|
||||
class ProcessThread {
|
||||
public:
|
||||
virtual ~ProcessThread();
|
||||
|
||||
static std::unique_ptr<ProcessThread> Create(const char* thread_name);
|
||||
|
||||
// Starts the worker thread. Must be called from the construction thread.
|
||||
virtual void Start() = 0;
|
||||
|
||||
// Stops the worker thread. Must be called from the construction thread.
|
||||
virtual void Stop() = 0;
|
||||
|
||||
// Wakes the thread up to give a module a chance to do processing right
|
||||
// away. This causes the worker thread to wake up and requery the specified
|
||||
// module for when it should be called back. (Typically the module should
|
||||
// return 0 from TimeUntilNextProcess on the worker thread at that point).
|
||||
// Can be called on any thread.
|
||||
virtual void WakeUp(Module* module) = 0;
|
||||
|
||||
// Queues a task object to run on the worker thread. Ownership of the
|
||||
// task object is transferred to the ProcessThread and the object will
|
||||
// either be deleted after running on the worker thread, or on the
|
||||
// construction thread of the ProcessThread instance, if the task did not
|
||||
// get a chance to run (e.g. posting the task while shutting down or when
|
||||
// the thread never runs).
|
||||
virtual void PostTask(std::unique_ptr<rtc::QueuedTask> task) = 0;
|
||||
|
||||
// Adds a module that will start to receive callbacks on the worker thread.
|
||||
// Can be called from any thread.
|
||||
virtual void RegisterModule(Module* module, const rtc::Location& from) = 0;
|
||||
|
||||
// Removes a previously registered module.
|
||||
// Can be called from any thread.
|
||||
virtual void DeRegisterModule(Module* module) = 0;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_UTILITY_INCLUDE_PROCESS_THREAD_H_
|
||||
123
modules/utility/source/helpers_android.cc
Normal file
123
modules/utility/source/helpers_android.cc
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/utility/include/helpers_android.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define TAG "HelpersAndroid"
|
||||
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
JNIEnv* GetEnv(JavaVM* jvm) {
|
||||
void* env = NULL;
|
||||
jint status = jvm->GetEnv(&env, JNI_VERSION_1_6);
|
||||
RTC_CHECK(((env != NULL) && (status == JNI_OK)) ||
|
||||
((env == NULL) && (status == JNI_EDETACHED)))
|
||||
<< "Unexpected GetEnv return: " << status << ":" << env;
|
||||
return reinterpret_cast<JNIEnv*>(env);
|
||||
}
|
||||
|
||||
// Return a |jlong| that will correctly convert back to |ptr|. This is needed
|
||||
// because the alternative (of silently passing a 32-bit pointer to a vararg
|
||||
// function expecting a 64-bit param) picks up garbage in the high 32 bits.
|
||||
jlong PointerTojlong(void* ptr) {
|
||||
static_assert(sizeof(intptr_t) <= sizeof(jlong),
|
||||
"Time to rethink the use of jlongs");
|
||||
// Going through intptr_t to be obvious about the definedness of the
|
||||
// conversion from pointer to integral type. intptr_t to jlong is a standard
|
||||
// widening by the static_assert above.
|
||||
jlong ret = reinterpret_cast<intptr_t>(ptr);
|
||||
RTC_DCHECK(reinterpret_cast<void*>(ret) == ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
jmethodID GetMethodID (
|
||||
JNIEnv* jni, jclass c, const char* name, const char* signature) {
|
||||
jmethodID m = jni->GetMethodID(c, name, signature);
|
||||
CHECK_EXCEPTION(jni) << "Error during GetMethodID: " << name << ", "
|
||||
<< signature;
|
||||
RTC_CHECK(m) << name << ", " << signature;
|
||||
return m;
|
||||
}
|
||||
|
||||
jmethodID GetStaticMethodID (
|
||||
JNIEnv* jni, jclass c, const char* name, const char* signature) {
|
||||
jmethodID m = jni->GetStaticMethodID(c, name, signature);
|
||||
CHECK_EXCEPTION(jni) << "Error during GetStaticMethodID: " << name << ", "
|
||||
<< signature;
|
||||
RTC_CHECK(m) << name << ", " << signature;
|
||||
return m;
|
||||
}
|
||||
|
||||
jclass FindClass(JNIEnv* jni, const char* name) {
|
||||
jclass c = jni->FindClass(name);
|
||||
CHECK_EXCEPTION(jni) << "Error during FindClass: " << name;
|
||||
RTC_CHECK(c) << name;
|
||||
return c;
|
||||
}
|
||||
|
||||
jobject NewGlobalRef(JNIEnv* jni, jobject o) {
|
||||
jobject ret = jni->NewGlobalRef(o);
|
||||
CHECK_EXCEPTION(jni) << "Error during NewGlobalRef";
|
||||
RTC_CHECK(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DeleteGlobalRef(JNIEnv* jni, jobject o) {
|
||||
jni->DeleteGlobalRef(o);
|
||||
CHECK_EXCEPTION(jni) << "Error during DeleteGlobalRef";
|
||||
}
|
||||
|
||||
std::string GetThreadId() {
|
||||
char buf[21]; // Big enough to hold a kuint64max plus terminating NULL.
|
||||
int thread_id = gettid();
|
||||
RTC_CHECK_LT(snprintf(buf, sizeof(buf), "%i", thread_id),
|
||||
static_cast<int>(sizeof(buf)))
|
||||
<< "Thread id is bigger than uint64??";
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
std::string GetThreadInfo() {
|
||||
return "@[tid=" + GetThreadId() + "]";
|
||||
}
|
||||
|
||||
AttachThreadScoped::AttachThreadScoped(JavaVM* jvm)
|
||||
: attached_(false), jvm_(jvm), env_(NULL) {
|
||||
env_ = GetEnv(jvm);
|
||||
if (!env_) {
|
||||
// Adding debug log here so we can track down potential leaks and figure
|
||||
// out why we sometimes see "Native thread exiting without having called
|
||||
// DetachCurrentThread" in logcat outputs.
|
||||
ALOGD("Attaching thread to JVM%s", GetThreadInfo().c_str());
|
||||
jint res = jvm->AttachCurrentThread(&env_, NULL);
|
||||
attached_ = (res == JNI_OK);
|
||||
RTC_CHECK(attached_) << "AttachCurrentThread failed: " << res;
|
||||
}
|
||||
}
|
||||
|
||||
AttachThreadScoped::~AttachThreadScoped() {
|
||||
if (attached_) {
|
||||
ALOGD("Detaching thread from JVM%s", GetThreadInfo().c_str());
|
||||
jint res = jvm_->DetachCurrentThread();
|
||||
RTC_CHECK(res == JNI_OK) << "DetachCurrentThread failed: " << res;
|
||||
RTC_CHECK(!GetEnv(jvm_));
|
||||
}
|
||||
}
|
||||
|
||||
JNIEnv* AttachThreadScoped::env() { return env_; }
|
||||
|
||||
} // namespace webrtc
|
||||
284
modules/utility/source/jvm_android.cc
Normal file
284
modules/utility/source/jvm_android.cc
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/modules/utility/include/jvm_android.h"
|
||||
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
|
||||
#define TAG "JVM"
|
||||
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
|
||||
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
JVM* g_jvm;
|
||||
|
||||
// TODO(henrika): add more clases here if needed.
|
||||
struct {
|
||||
const char* name;
|
||||
jclass clazz;
|
||||
} loaded_classes[] = {
|
||||
{"org/webrtc/voiceengine/BuildInfo", nullptr},
|
||||
{"org/webrtc/voiceengine/WebRtcAudioManager", nullptr},
|
||||
{"org/webrtc/voiceengine/WebRtcAudioRecord", nullptr},
|
||||
{"org/webrtc/voiceengine/WebRtcAudioTrack", nullptr},
|
||||
};
|
||||
|
||||
// Android's FindClass() is trickier than usual because the app-specific
|
||||
// ClassLoader is not consulted when there is no app-specific frame on the
|
||||
// stack. Consequently, we only look up all classes once in native WebRTC.
|
||||
// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
|
||||
void LoadClasses(JNIEnv* jni) {
|
||||
ALOGD("LoadClasses");
|
||||
for (auto& c : loaded_classes) {
|
||||
jclass localRef = FindClass(jni, c.name);
|
||||
ALOGD("name: %s", c.name);
|
||||
CHECK_EXCEPTION(jni) << "Error during FindClass: " << c.name;
|
||||
RTC_CHECK(localRef) << c.name;
|
||||
jclass globalRef = reinterpret_cast<jclass>(jni->NewGlobalRef(localRef));
|
||||
CHECK_EXCEPTION(jni) << "Error during NewGlobalRef: " << c.name;
|
||||
RTC_CHECK(globalRef) << c.name;
|
||||
c.clazz = globalRef;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeClassReferences(JNIEnv* jni) {
|
||||
for (auto& c : loaded_classes) {
|
||||
jni->DeleteGlobalRef(c.clazz);
|
||||
c.clazz = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
jclass LookUpClass(const char* name) {
|
||||
for (auto& c : loaded_classes) {
|
||||
if (strcmp(c.name, name) == 0)
|
||||
return c.clazz;
|
||||
}
|
||||
RTC_CHECK(false) << "Unable to find class in lookup table";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// AttachCurrentThreadIfNeeded implementation.
|
||||
AttachCurrentThreadIfNeeded::AttachCurrentThreadIfNeeded()
|
||||
: attached_(false) {
|
||||
ALOGD("AttachCurrentThreadIfNeeded::ctor%s", GetThreadInfo().c_str());
|
||||
JavaVM* jvm = JVM::GetInstance()->jvm();
|
||||
RTC_CHECK(jvm);
|
||||
JNIEnv* jni = GetEnv(jvm);
|
||||
if (!jni) {
|
||||
ALOGD("Attaching thread to JVM");
|
||||
JNIEnv* env = nullptr;
|
||||
jint ret = jvm->AttachCurrentThread(&env, nullptr);
|
||||
attached_ = (ret == JNI_OK);
|
||||
}
|
||||
}
|
||||
|
||||
AttachCurrentThreadIfNeeded::~AttachCurrentThreadIfNeeded() {
|
||||
ALOGD("AttachCurrentThreadIfNeeded::dtor%s", GetThreadInfo().c_str());
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
if (attached_) {
|
||||
ALOGD("Detaching thread from JVM");
|
||||
jint res = JVM::GetInstance()->jvm()->DetachCurrentThread();
|
||||
RTC_CHECK(res == JNI_OK) << "DetachCurrentThread failed: " << res;
|
||||
}
|
||||
}
|
||||
|
||||
// GlobalRef implementation.
|
||||
GlobalRef::GlobalRef(JNIEnv* jni, jobject object)
|
||||
: jni_(jni), j_object_(NewGlobalRef(jni, object)) {
|
||||
ALOGD("GlobalRef::ctor%s", GetThreadInfo().c_str());
|
||||
}
|
||||
|
||||
GlobalRef::~GlobalRef() {
|
||||
ALOGD("GlobalRef::dtor%s", GetThreadInfo().c_str());
|
||||
DeleteGlobalRef(jni_, j_object_);
|
||||
}
|
||||
|
||||
jboolean GlobalRef::CallBooleanMethod(jmethodID methodID, ...) {
|
||||
va_list args;
|
||||
va_start(args, methodID);
|
||||
jboolean res = jni_->CallBooleanMethodV(j_object_, methodID, args);
|
||||
CHECK_EXCEPTION(jni_) << "Error during CallBooleanMethod";
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
jint GlobalRef::CallIntMethod(jmethodID methodID, ...) {
|
||||
va_list args;
|
||||
va_start(args, methodID);
|
||||
jint res = jni_->CallIntMethodV(j_object_, methodID, args);
|
||||
CHECK_EXCEPTION(jni_) << "Error during CallIntMethod";
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
void GlobalRef::CallVoidMethod(jmethodID methodID, ...) {
|
||||
va_list args;
|
||||
va_start(args, methodID);
|
||||
jni_->CallVoidMethodV(j_object_, methodID, args);
|
||||
CHECK_EXCEPTION(jni_) << "Error during CallVoidMethod";
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// NativeRegistration implementation.
|
||||
NativeRegistration::NativeRegistration(JNIEnv* jni, jclass clazz)
|
||||
: JavaClass(jni, clazz), jni_(jni) {
|
||||
ALOGD("NativeRegistration::ctor%s", GetThreadInfo().c_str());
|
||||
}
|
||||
|
||||
NativeRegistration::~NativeRegistration() {
|
||||
ALOGD("NativeRegistration::dtor%s", GetThreadInfo().c_str());
|
||||
jni_->UnregisterNatives(j_class_);
|
||||
CHECK_EXCEPTION(jni_) << "Error during UnregisterNatives";
|
||||
}
|
||||
|
||||
std::unique_ptr<GlobalRef> NativeRegistration::NewObject(
|
||||
const char* name, const char* signature, ...) {
|
||||
ALOGD("NativeRegistration::NewObject%s", GetThreadInfo().c_str());
|
||||
va_list args;
|
||||
va_start(args, signature);
|
||||
jobject obj = jni_->NewObjectV(j_class_,
|
||||
GetMethodID(jni_, j_class_, name, signature),
|
||||
args);
|
||||
CHECK_EXCEPTION(jni_) << "Error during NewObjectV";
|
||||
va_end(args);
|
||||
return std::unique_ptr<GlobalRef>(new GlobalRef(jni_, obj));
|
||||
}
|
||||
|
||||
// JavaClass implementation.
|
||||
jmethodID JavaClass::GetMethodId(
|
||||
const char* name, const char* signature) {
|
||||
return GetMethodID(jni_, j_class_, name, signature);
|
||||
}
|
||||
|
||||
jmethodID JavaClass::GetStaticMethodId(
|
||||
const char* name, const char* signature) {
|
||||
return GetStaticMethodID(jni_, j_class_, name, signature);
|
||||
}
|
||||
|
||||
jobject JavaClass::CallStaticObjectMethod(jmethodID methodID, ...) {
|
||||
va_list args;
|
||||
va_start(args, methodID);
|
||||
jobject res = jni_->CallStaticObjectMethodV(j_class_, methodID, args);
|
||||
CHECK_EXCEPTION(jni_) << "Error during CallStaticObjectMethod";
|
||||
return res;
|
||||
}
|
||||
|
||||
jint JavaClass::CallStaticIntMethod(jmethodID methodID, ...) {
|
||||
va_list args;
|
||||
va_start(args, methodID);
|
||||
jint res = jni_->CallStaticIntMethodV(j_class_, methodID, args);
|
||||
CHECK_EXCEPTION(jni_) << "Error during CallStaticIntMethod";
|
||||
return res;
|
||||
}
|
||||
|
||||
// JNIEnvironment implementation.
|
||||
JNIEnvironment::JNIEnvironment(JNIEnv* jni) : jni_(jni) {
|
||||
ALOGD("JNIEnvironment::ctor%s", GetThreadInfo().c_str());
|
||||
}
|
||||
|
||||
JNIEnvironment::~JNIEnvironment() {
|
||||
ALOGD("JNIEnvironment::dtor%s", GetThreadInfo().c_str());
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeRegistration> JNIEnvironment::RegisterNatives(
|
||||
const char* name, const JNINativeMethod *methods, int num_methods) {
|
||||
ALOGD("JNIEnvironment::RegisterNatives(%s)", name);
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
jclass clazz = LookUpClass(name);
|
||||
jni_->RegisterNatives(clazz, methods, num_methods);
|
||||
CHECK_EXCEPTION(jni_) << "Error during RegisterNatives";
|
||||
return std::unique_ptr<NativeRegistration>(
|
||||
new NativeRegistration(jni_, clazz));
|
||||
}
|
||||
|
||||
std::string JNIEnvironment::JavaToStdString(const jstring& j_string) {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
const char* jchars = jni_->GetStringUTFChars(j_string, nullptr);
|
||||
CHECK_EXCEPTION(jni_);
|
||||
const int size = jni_->GetStringUTFLength(j_string);
|
||||
CHECK_EXCEPTION(jni_);
|
||||
std::string ret(jchars, size);
|
||||
jni_->ReleaseStringUTFChars(j_string, jchars);
|
||||
CHECK_EXCEPTION(jni_);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// static
|
||||
void JVM::Initialize(JavaVM* jvm) {
|
||||
ALOGD("JVM::Initialize%s", GetThreadInfo().c_str());
|
||||
RTC_CHECK(!g_jvm);
|
||||
g_jvm = new JVM(jvm);
|
||||
}
|
||||
|
||||
void JVM::Initialize(JavaVM* jvm, jobject context) {
|
||||
Initialize(jvm);
|
||||
|
||||
// Pass in the context to the new ContextUtils class.
|
||||
JNIEnv* jni = g_jvm->jni();
|
||||
jclass context_utils = FindClass(jni, "org/webrtc/ContextUtils");
|
||||
jmethodID initialize_method = jni->GetStaticMethodID(
|
||||
context_utils, "initialize", "(Landroid/content/Context;)V");
|
||||
jni->CallStaticVoidMethod(context_utils, initialize_method, context);
|
||||
}
|
||||
|
||||
// static
|
||||
void JVM::Uninitialize() {
|
||||
ALOGD("JVM::Uninitialize%s", GetThreadInfo().c_str());
|
||||
RTC_DCHECK(g_jvm);
|
||||
delete g_jvm;
|
||||
g_jvm = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
JVM* JVM::GetInstance() {
|
||||
RTC_DCHECK(g_jvm);
|
||||
return g_jvm;
|
||||
}
|
||||
|
||||
JVM::JVM(JavaVM* jvm) : jvm_(jvm) {
|
||||
ALOGD("JVM::JVM%s", GetThreadInfo().c_str());
|
||||
RTC_CHECK(jni()) << "AttachCurrentThread() must be called on this thread.";
|
||||
LoadClasses(jni());
|
||||
}
|
||||
|
||||
JVM::~JVM() {
|
||||
ALOGD("JVM::~JVM%s", GetThreadInfo().c_str());
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
FreeClassReferences(jni());
|
||||
}
|
||||
|
||||
std::unique_ptr<JNIEnvironment> JVM::environment() {
|
||||
ALOGD("JVM::environment%s", GetThreadInfo().c_str());
|
||||
// The JNIEnv is used for thread-local storage. For this reason, we cannot
|
||||
// share a JNIEnv between threads. If a piece of code has no other way to get
|
||||
// its JNIEnv, we should share the JavaVM, and use GetEnv to discover the
|
||||
// thread's JNIEnv. (Assuming it has one, if not, use AttachCurrentThread).
|
||||
// See // http://developer.android.com/training/articles/perf-jni.html.
|
||||
JNIEnv* jni = GetEnv(jvm_);
|
||||
if (!jni) {
|
||||
ALOGE("AttachCurrentThread() has not been called on this thread.");
|
||||
return std::unique_ptr<JNIEnvironment>();
|
||||
}
|
||||
return std::unique_ptr<JNIEnvironment>(new JNIEnvironment(jni));
|
||||
}
|
||||
|
||||
JavaClass JVM::GetClass(const char* name) {
|
||||
ALOGD("JVM::GetClass(%s)%s", name, GetThreadInfo().c_str());
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
return JavaClass(jni(), LookUpClass(name));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
224
modules/utility/source/process_thread_impl.cc
Normal file
224
modules/utility/source/process_thread_impl.cc
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/utility/source/process_thread_impl.h"
|
||||
|
||||
#include "webrtc/modules/include/module.h"
|
||||
#include "webrtc/rtc_base/checks.h"
|
||||
#include "webrtc/rtc_base/task_queue.h"
|
||||
#include "webrtc/rtc_base/timeutils.h"
|
||||
#include "webrtc/rtc_base/trace_event.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
// We use this constant internally to signal that a module has requested
|
||||
// a callback right away. When this is set, no call to TimeUntilNextProcess
|
||||
// should be made, but Process() should be called directly.
|
||||
const int64_t kCallProcessImmediately = -1;
|
||||
|
||||
int64_t GetNextCallbackTime(Module* module, int64_t time_now) {
|
||||
int64_t interval = module->TimeUntilNextProcess();
|
||||
if (interval < 0) {
|
||||
// Falling behind, we should call the callback now.
|
||||
return time_now;
|
||||
}
|
||||
return time_now + interval;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessThread::~ProcessThread() {}
|
||||
|
||||
// static
|
||||
std::unique_ptr<ProcessThread> ProcessThread::Create(
|
||||
const char* thread_name) {
|
||||
return std::unique_ptr<ProcessThread>(new ProcessThreadImpl(thread_name));
|
||||
}
|
||||
|
||||
ProcessThreadImpl::ProcessThreadImpl(const char* thread_name)
|
||||
: wake_up_(EventWrapper::Create()),
|
||||
stop_(false),
|
||||
thread_name_(thread_name) {}
|
||||
|
||||
ProcessThreadImpl::~ProcessThreadImpl() {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
RTC_DCHECK(!thread_.get());
|
||||
RTC_DCHECK(!stop_);
|
||||
|
||||
while (!queue_.empty()) {
|
||||
delete queue_.front();
|
||||
queue_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessThreadImpl::Start() {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
RTC_DCHECK(!thread_.get());
|
||||
if (thread_.get())
|
||||
return;
|
||||
|
||||
RTC_DCHECK(!stop_);
|
||||
|
||||
for (ModuleCallback& m : modules_)
|
||||
m.module->ProcessThreadAttached(this);
|
||||
|
||||
thread_.reset(
|
||||
new rtc::PlatformThread(&ProcessThreadImpl::Run, this, thread_name_));
|
||||
thread_->Start();
|
||||
}
|
||||
|
||||
void ProcessThreadImpl::Stop() {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
if(!thread_.get())
|
||||
return;
|
||||
|
||||
{
|
||||
rtc::CritScope lock(&lock_);
|
||||
stop_ = true;
|
||||
}
|
||||
|
||||
wake_up_->Set();
|
||||
|
||||
thread_->Stop();
|
||||
stop_ = false;
|
||||
|
||||
thread_.reset();
|
||||
for (ModuleCallback& m : modules_)
|
||||
m.module->ProcessThreadAttached(nullptr);
|
||||
}
|
||||
|
||||
void ProcessThreadImpl::WakeUp(Module* module) {
|
||||
// Allowed to be called on any thread.
|
||||
{
|
||||
rtc::CritScope lock(&lock_);
|
||||
for (ModuleCallback& m : modules_) {
|
||||
if (m.module == module)
|
||||
m.next_callback = kCallProcessImmediately;
|
||||
}
|
||||
}
|
||||
wake_up_->Set();
|
||||
}
|
||||
|
||||
void ProcessThreadImpl::PostTask(std::unique_ptr<rtc::QueuedTask> task) {
|
||||
// Allowed to be called on any thread.
|
||||
{
|
||||
rtc::CritScope lock(&lock_);
|
||||
queue_.push(task.release());
|
||||
}
|
||||
wake_up_->Set();
|
||||
}
|
||||
|
||||
void ProcessThreadImpl::RegisterModule(Module* module,
|
||||
const rtc::Location& from) {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
RTC_DCHECK(module) << from.ToString();
|
||||
|
||||
#if RTC_DCHECK_IS_ON
|
||||
{
|
||||
// Catch programmer error.
|
||||
rtc::CritScope lock(&lock_);
|
||||
for (const ModuleCallback& mc : modules_) {
|
||||
RTC_DCHECK(mc.module != module)
|
||||
<< "Already registered here: " << mc.location.ToString() << "\n"
|
||||
<< "Now attempting from here: " << from.ToString();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Now that we know the module isn't in the list, we'll call out to notify
|
||||
// the module that it's attached to the worker thread. We don't hold
|
||||
// the lock while we make this call.
|
||||
if (thread_.get())
|
||||
module->ProcessThreadAttached(this);
|
||||
|
||||
{
|
||||
rtc::CritScope lock(&lock_);
|
||||
modules_.push_back(ModuleCallback(module, from));
|
||||
}
|
||||
|
||||
// Wake the thread calling ProcessThreadImpl::Process() to update the
|
||||
// waiting time. The waiting time for the just registered module may be
|
||||
// shorter than all other registered modules.
|
||||
wake_up_->Set();
|
||||
}
|
||||
|
||||
void ProcessThreadImpl::DeRegisterModule(Module* module) {
|
||||
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
||||
RTC_DCHECK(module);
|
||||
|
||||
{
|
||||
rtc::CritScope lock(&lock_);
|
||||
modules_.remove_if([&module](const ModuleCallback& m) {
|
||||
return m.module == module;
|
||||
});
|
||||
}
|
||||
|
||||
// Notify the module that it's been detached.
|
||||
module->ProcessThreadAttached(nullptr);
|
||||
}
|
||||
|
||||
// static
|
||||
bool ProcessThreadImpl::Run(void* obj) {
|
||||
return static_cast<ProcessThreadImpl*>(obj)->Process();
|
||||
}
|
||||
|
||||
bool ProcessThreadImpl::Process() {
|
||||
TRACE_EVENT1("webrtc", "ProcessThreadImpl", "name", thread_name_);
|
||||
int64_t now = rtc::TimeMillis();
|
||||
int64_t next_checkpoint = now + (1000 * 60);
|
||||
|
||||
{
|
||||
rtc::CritScope lock(&lock_);
|
||||
if (stop_)
|
||||
return false;
|
||||
for (ModuleCallback& m : modules_) {
|
||||
// TODO(tommi): Would be good to measure the time TimeUntilNextProcess
|
||||
// takes and dcheck if it takes too long (e.g. >=10ms). Ideally this
|
||||
// operation should not require taking a lock, so querying all modules
|
||||
// should run in a matter of nanoseconds.
|
||||
if (m.next_callback == 0)
|
||||
m.next_callback = GetNextCallbackTime(m.module, now);
|
||||
|
||||
if (m.next_callback <= now ||
|
||||
m.next_callback == kCallProcessImmediately) {
|
||||
{
|
||||
TRACE_EVENT2("webrtc", "ModuleProcess", "function",
|
||||
m.location.function_name(), "file",
|
||||
m.location.file_and_line());
|
||||
m.module->Process();
|
||||
}
|
||||
// Use a new 'now' reference to calculate when the next callback
|
||||
// should occur. We'll continue to use 'now' above for the baseline
|
||||
// of calculating how long we should wait, to reduce variance.
|
||||
int64_t new_now = rtc::TimeMillis();
|
||||
m.next_callback = GetNextCallbackTime(m.module, new_now);
|
||||
}
|
||||
|
||||
if (m.next_callback < next_checkpoint)
|
||||
next_checkpoint = m.next_callback;
|
||||
}
|
||||
|
||||
while (!queue_.empty()) {
|
||||
rtc::QueuedTask* task = queue_.front();
|
||||
queue_.pop();
|
||||
lock_.Leave();
|
||||
task->Run();
|
||||
delete task;
|
||||
lock_.Enter();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t time_to_wait = next_checkpoint - rtc::TimeMillis();
|
||||
if (time_to_wait > 0)
|
||||
wake_up_->Wait(static_cast<unsigned long>(time_to_wait));
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace webrtc
|
||||
88
modules/utility/source/process_thread_impl.h
Normal file
88
modules/utility/source/process_thread_impl.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_
|
||||
#define WEBRTC_MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "webrtc/modules/utility/include/process_thread.h"
|
||||
#include "webrtc/rtc_base/criticalsection.h"
|
||||
#include "webrtc/rtc_base/location.h"
|
||||
#include "webrtc/rtc_base/platform_thread.h"
|
||||
#include "webrtc/rtc_base/thread_checker.h"
|
||||
#include "webrtc/system_wrappers/include/event_wrapper.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class ProcessThreadImpl : public ProcessThread {
|
||||
public:
|
||||
explicit ProcessThreadImpl(const char* thread_name);
|
||||
~ProcessThreadImpl() override;
|
||||
|
||||
void Start() override;
|
||||
void Stop() override;
|
||||
|
||||
void WakeUp(Module* module) override;
|
||||
void PostTask(std::unique_ptr<rtc::QueuedTask> task) override;
|
||||
|
||||
void RegisterModule(Module* module, const rtc::Location& from) override;
|
||||
void DeRegisterModule(Module* module) override;
|
||||
|
||||
protected:
|
||||
static bool Run(void* obj);
|
||||
bool Process();
|
||||
|
||||
private:
|
||||
struct ModuleCallback {
|
||||
ModuleCallback() = delete;
|
||||
ModuleCallback(ModuleCallback&& cb) = default;
|
||||
ModuleCallback(const ModuleCallback& cb) = default;
|
||||
ModuleCallback(Module* module, const rtc::Location& location)
|
||||
: module(module), location(location) {}
|
||||
bool operator==(const ModuleCallback& cb) const {
|
||||
return cb.module == module;
|
||||
}
|
||||
|
||||
Module* const module;
|
||||
int64_t next_callback = 0; // Absolute timestamp.
|
||||
const rtc::Location location;
|
||||
|
||||
private:
|
||||
ModuleCallback& operator=(ModuleCallback&);
|
||||
};
|
||||
|
||||
typedef std::list<ModuleCallback> ModuleList;
|
||||
|
||||
// Warning: For some reason, if |lock_| comes immediately before |modules_|
|
||||
// with the current class layout, we will start to have mysterious crashes
|
||||
// on Mac 10.9 debug. I (Tommi) suspect we're hitting some obscure alignemnt
|
||||
// issues, but I haven't figured out what they are, if there are alignment
|
||||
// requirements for mutexes on Mac or if there's something else to it.
|
||||
// So be careful with changing the layout.
|
||||
rtc::CriticalSection lock_; // Used to guard modules_, tasks_ and stop_.
|
||||
|
||||
rtc::ThreadChecker thread_checker_;
|
||||
const std::unique_ptr<EventWrapper> wake_up_;
|
||||
// TODO(pbos): Remove unique_ptr and stop recreating the thread.
|
||||
std::unique_ptr<rtc::PlatformThread> thread_;
|
||||
|
||||
ModuleList modules_;
|
||||
std::queue<rtc::QueuedTask*> queue_;
|
||||
bool stop_;
|
||||
const char* thread_name_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_UTILITY_SOURCE_PROCESS_THREAD_IMPL_H_
|
||||
321
modules/utility/source/process_thread_impl_unittest.cc
Normal file
321
modules/utility/source/process_thread_impl_unittest.cc
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "webrtc/modules/include/module.h"
|
||||
#include "webrtc/modules/utility/source/process_thread_impl.h"
|
||||
#include "webrtc/rtc_base/location.h"
|
||||
#include "webrtc/rtc_base/task_queue.h"
|
||||
#include "webrtc/rtc_base/timeutils.h"
|
||||
#include "webrtc/test/gmock.h"
|
||||
#include "webrtc/test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::InSequence;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
|
||||
// The length of time, in milliseconds, to wait for an event to become signaled.
|
||||
// Set to a fairly large value as there is quite a bit of variation on some
|
||||
// Windows bots.
|
||||
static const int kEventWaitTimeout = 500;
|
||||
|
||||
class MockModule : public Module {
|
||||
public:
|
||||
MOCK_METHOD0(TimeUntilNextProcess, int64_t());
|
||||
MOCK_METHOD0(Process, void());
|
||||
MOCK_METHOD1(ProcessThreadAttached, void(ProcessThread*));
|
||||
};
|
||||
|
||||
class RaiseEventTask : public rtc::QueuedTask {
|
||||
public:
|
||||
RaiseEventTask(EventWrapper* event) : event_(event) {}
|
||||
bool Run() override {
|
||||
event_->Set();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
EventWrapper* event_;
|
||||
};
|
||||
|
||||
ACTION_P(SetEvent, event) {
|
||||
event->Set();
|
||||
}
|
||||
|
||||
ACTION_P(Increment, counter) {
|
||||
++(*counter);
|
||||
}
|
||||
|
||||
ACTION_P(SetTimestamp, ptr) {
|
||||
*ptr = rtc::TimeMillis();
|
||||
}
|
||||
|
||||
TEST(ProcessThreadImpl, StartStop) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
thread.Start();
|
||||
thread.Stop();
|
||||
}
|
||||
|
||||
TEST(ProcessThreadImpl, MultipleStartStop) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
thread.Start();
|
||||
thread.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that we get at least call back to Process() on the worker thread.
|
||||
TEST(ProcessThreadImpl, ProcessCall) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
thread.Start();
|
||||
|
||||
std::unique_ptr<EventWrapper> event(EventWrapper::Create());
|
||||
|
||||
MockModule module;
|
||||
EXPECT_CALL(module, TimeUntilNextProcess())
|
||||
.WillOnce(Return(0))
|
||||
.WillRepeatedly(Return(1));
|
||||
EXPECT_CALL(module, Process())
|
||||
.WillOnce(DoAll(SetEvent(event.get()), Return()))
|
||||
.WillRepeatedly(Return());
|
||||
EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1);
|
||||
|
||||
thread.RegisterModule(&module, RTC_FROM_HERE);
|
||||
EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout));
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1);
|
||||
thread.Stop();
|
||||
}
|
||||
|
||||
// Same as ProcessCall except the module is registered before the
|
||||
// call to Start().
|
||||
TEST(ProcessThreadImpl, ProcessCall2) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
std::unique_ptr<EventWrapper> event(EventWrapper::Create());
|
||||
|
||||
MockModule module;
|
||||
EXPECT_CALL(module, TimeUntilNextProcess())
|
||||
.WillOnce(Return(0))
|
||||
.WillRepeatedly(Return(1));
|
||||
EXPECT_CALL(module, Process())
|
||||
.WillOnce(DoAll(SetEvent(event.get()), Return()))
|
||||
.WillRepeatedly(Return());
|
||||
|
||||
thread.RegisterModule(&module, RTC_FROM_HERE);
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1);
|
||||
thread.Start();
|
||||
EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout));
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1);
|
||||
thread.Stop();
|
||||
}
|
||||
|
||||
// Tests setting up a module for callbacks and then unregister that module.
|
||||
// After unregistration, we should not receive any further callbacks.
|
||||
TEST(ProcessThreadImpl, Deregister) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
std::unique_ptr<EventWrapper> event(EventWrapper::Create());
|
||||
|
||||
int process_count = 0;
|
||||
MockModule module;
|
||||
EXPECT_CALL(module, TimeUntilNextProcess())
|
||||
.WillOnce(Return(0))
|
||||
.WillRepeatedly(Return(1));
|
||||
EXPECT_CALL(module, Process())
|
||||
.WillOnce(DoAll(SetEvent(event.get()),
|
||||
Increment(&process_count),
|
||||
Return()))
|
||||
.WillRepeatedly(DoAll(Increment(&process_count), Return()));
|
||||
|
||||
thread.RegisterModule(&module, RTC_FROM_HERE);
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1);
|
||||
thread.Start();
|
||||
|
||||
EXPECT_EQ(kEventSignaled, event->Wait(kEventWaitTimeout));
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1);
|
||||
thread.DeRegisterModule(&module);
|
||||
|
||||
EXPECT_GE(process_count, 1);
|
||||
int count_after_deregister = process_count;
|
||||
|
||||
// We shouldn't get any more callbacks.
|
||||
EXPECT_EQ(kEventTimeout, event->Wait(20));
|
||||
EXPECT_EQ(count_after_deregister, process_count);
|
||||
thread.Stop();
|
||||
}
|
||||
|
||||
// Helper function for testing receiving a callback after a certain amount of
|
||||
// time. There's some variance of timing built into it to reduce chance of
|
||||
// flakiness on bots.
|
||||
void ProcessCallAfterAFewMs(int64_t milliseconds) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
thread.Start();
|
||||
|
||||
std::unique_ptr<EventWrapper> event(EventWrapper::Create());
|
||||
|
||||
MockModule module;
|
||||
int64_t start_time = 0;
|
||||
int64_t called_time = 0;
|
||||
EXPECT_CALL(module, TimeUntilNextProcess())
|
||||
.WillOnce(DoAll(SetTimestamp(&start_time),
|
||||
Return(milliseconds)))
|
||||
.WillRepeatedly(Return(milliseconds));
|
||||
EXPECT_CALL(module, Process())
|
||||
.WillOnce(DoAll(SetTimestamp(&called_time),
|
||||
SetEvent(event.get()),
|
||||
Return()))
|
||||
.WillRepeatedly(Return());
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1);
|
||||
thread.RegisterModule(&module, RTC_FROM_HERE);
|
||||
|
||||
// Add a buffer of 50ms due to slowness of some trybots
|
||||
// (e.g. win_drmemory_light)
|
||||
EXPECT_EQ(kEventSignaled, event->Wait(milliseconds + 50));
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1);
|
||||
thread.Stop();
|
||||
|
||||
ASSERT_GT(start_time, 0);
|
||||
ASSERT_GT(called_time, 0);
|
||||
// Use >= instead of > since due to rounding and timer accuracy (or lack
|
||||
// thereof), can make the test run in "0"ms time.
|
||||
EXPECT_GE(called_time, start_time);
|
||||
// Check for an acceptable range.
|
||||
uint32_t diff = called_time - start_time;
|
||||
EXPECT_GE(diff, milliseconds - 15);
|
||||
EXPECT_LT(diff, milliseconds + 15);
|
||||
}
|
||||
|
||||
// DISABLED for now since the virtual build bots are too slow :(
|
||||
// TODO(tommi): Fix.
|
||||
TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter5ms) {
|
||||
ProcessCallAfterAFewMs(5);
|
||||
}
|
||||
|
||||
// DISABLED for now since the virtual build bots are too slow :(
|
||||
// TODO(tommi): Fix.
|
||||
TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter50ms) {
|
||||
ProcessCallAfterAFewMs(50);
|
||||
}
|
||||
|
||||
// DISABLED for now since the virtual build bots are too slow :(
|
||||
// TODO(tommi): Fix.
|
||||
TEST(ProcessThreadImpl, DISABLED_ProcessCallAfter200ms) {
|
||||
ProcessCallAfterAFewMs(200);
|
||||
}
|
||||
|
||||
// Runs callbacks with the goal of getting up to 50 callbacks within a second
|
||||
// (on average 1 callback every 20ms). On real hardware, we're usually pretty
|
||||
// close to that, but the test bots that run on virtual machines, will
|
||||
// typically be in the range 30-40 callbacks.
|
||||
// DISABLED for now since this can take up to 2 seconds to run on the slowest
|
||||
// build bots.
|
||||
// TODO(tommi): Fix.
|
||||
TEST(ProcessThreadImpl, DISABLED_Process50Times) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
thread.Start();
|
||||
|
||||
std::unique_ptr<EventWrapper> event(EventWrapper::Create());
|
||||
|
||||
MockModule module;
|
||||
int callback_count = 0;
|
||||
// Ask for a callback after 20ms.
|
||||
EXPECT_CALL(module, TimeUntilNextProcess())
|
||||
.WillRepeatedly(Return(20));
|
||||
EXPECT_CALL(module, Process())
|
||||
.WillRepeatedly(DoAll(Increment(&callback_count),
|
||||
Return()));
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1);
|
||||
thread.RegisterModule(&module, RTC_FROM_HERE);
|
||||
|
||||
EXPECT_EQ(kEventTimeout, event->Wait(1000));
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1);
|
||||
thread.Stop();
|
||||
|
||||
printf("Callback count: %i\n", callback_count);
|
||||
// Check that we got called back up to 50 times.
|
||||
// Some of the try bots run on slow virtual machines, so the lower bound
|
||||
// is much more relaxed to avoid flakiness.
|
||||
EXPECT_GE(callback_count, 25);
|
||||
EXPECT_LE(callback_count, 50);
|
||||
}
|
||||
|
||||
// Tests that we can wake up the worker thread to give us a callback right
|
||||
// away when we know the thread is sleeping.
|
||||
TEST(ProcessThreadImpl, WakeUp) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
thread.Start();
|
||||
|
||||
std::unique_ptr<EventWrapper> started(EventWrapper::Create());
|
||||
std::unique_ptr<EventWrapper> called(EventWrapper::Create());
|
||||
|
||||
MockModule module;
|
||||
int64_t start_time;
|
||||
int64_t called_time;
|
||||
|
||||
// Ask for a callback after 1000ms.
|
||||
// TimeUntilNextProcess will be called twice.
|
||||
// The first time we use it to get the thread into a waiting state.
|
||||
// Then we wake the thread and there should not be another call made to
|
||||
// TimeUntilNextProcess before Process() is called.
|
||||
// The second time TimeUntilNextProcess is then called, is after Process
|
||||
// has been called and we don't expect any more calls.
|
||||
EXPECT_CALL(module, TimeUntilNextProcess())
|
||||
.WillOnce(DoAll(SetTimestamp(&start_time),
|
||||
SetEvent(started.get()),
|
||||
Return(1000)))
|
||||
.WillOnce(Return(1000));
|
||||
EXPECT_CALL(module, Process())
|
||||
.WillOnce(
|
||||
DoAll(SetTimestamp(&called_time), SetEvent(called.get()), Return()))
|
||||
.WillRepeatedly(Return());
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(&thread)).Times(1);
|
||||
thread.RegisterModule(&module, RTC_FROM_HERE);
|
||||
|
||||
EXPECT_EQ(kEventSignaled, started->Wait(kEventWaitTimeout));
|
||||
thread.WakeUp(&module);
|
||||
EXPECT_EQ(kEventSignaled, called->Wait(kEventWaitTimeout));
|
||||
|
||||
EXPECT_CALL(module, ProcessThreadAttached(nullptr)).Times(1);
|
||||
thread.Stop();
|
||||
|
||||
EXPECT_GE(called_time, start_time);
|
||||
uint32_t diff = called_time - start_time;
|
||||
// We should have been called back much quicker than 1sec.
|
||||
EXPECT_LE(diff, 100u);
|
||||
}
|
||||
|
||||
// Tests that we can post a task that gets run straight away on the worker
|
||||
// thread.
|
||||
TEST(ProcessThreadImpl, PostTask) {
|
||||
ProcessThreadImpl thread("ProcessThread");
|
||||
std::unique_ptr<EventWrapper> task_ran(EventWrapper::Create());
|
||||
std::unique_ptr<RaiseEventTask> task(new RaiseEventTask(task_ran.get()));
|
||||
thread.Start();
|
||||
thread.PostTask(std::move(task));
|
||||
EXPECT_EQ(kEventSignaled, task_ran->Wait(kEventWaitTimeout));
|
||||
thread.Stop();
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
Reference in New Issue
Block a user