Add SafeThread to fix thread JNI shutdown races.

During JVM shutdown, some JNI calls may not return, so it's not possible to
reliably perform a join() during static variable destruction (which occurs
as the JVM unloads the JNI module).

Also, due to static variable destruction, it's not safe to use any members
of a static class instance from a separate thread of execution.

SafeThread is a templated thread class and a related owner class that's
designed for safe operation and shutdown of threads in the presence of
callbacks that may not return.  It also passes ownership of variables from
the static instance to the thread, so the thread can safely operate until
it exits (the last operation of the thread being to destroy its instance).

Notifiers, RpcServer, and Logger now use SafeThread to ensure race-free
destruction in both C++ and Java.

All Java callback threads are now marked as Java daemon threads so they
don't keep the JVM running after main() terminates.

All Java callback threads are now named so their purpose is more easily
identified in a debugger.

Add SetRpcServerOnStart and SetRpcServerOnExit (similar to Listener).
This commit is contained in:
Peter Johnson
2015-12-28 08:28:24 -08:00
parent d8de5e4f19
commit fef8f933d9
13 changed files with 343 additions and 287 deletions

View File

@@ -8,16 +8,11 @@
#ifndef NT_NOTIFIER_H_
#define NT_NOTIFIER_H_
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
#include <vector>
#include <functional>
#include "atomic_static.h"
#include "ntcore_cpp.h"
#include "SafeThread.h"
namespace nt {
@@ -33,7 +28,6 @@ class Notifier {
void Start();
void Stop();
bool active() const { return m_active; }
bool local_notifiers() const { return m_local_notifiers; }
static bool destroyed() { return s_destroyed; }
@@ -57,57 +51,11 @@ class Notifier {
private:
Notifier();
void ThreadMain();
class Thread;
SafeThreadOwner<Thread> m_owner;
std::atomic_bool m_active;
std::atomic_bool m_local_notifiers;
std::mutex m_mutex;
std::condition_variable m_cond;
struct EntryListener {
EntryListener(StringRef prefix_, EntryListenerCallback callback_,
unsigned int flags_)
: prefix(prefix_), callback(callback_), flags(flags_) {}
std::string prefix;
EntryListenerCallback callback;
unsigned int flags;
};
std::vector<EntryListener> m_entry_listeners;
std::vector<ConnectionListenerCallback> m_conn_listeners;
struct EntryNotification {
EntryNotification(StringRef name_, std::shared_ptr<Value> value_,
unsigned int flags_, EntryListenerCallback only_)
: name(name_),
value(value_),
flags(flags_),
only(only_) {}
std::string name;
std::shared_ptr<Value> value;
unsigned int flags;
EntryListenerCallback only;
};
std::queue<EntryNotification> m_entry_notifications;
struct ConnectionNotification {
ConnectionNotification(bool connected_, const ConnectionInfo& conn_info_,
ConnectionListenerCallback only_)
: connected(connected_), conn_info(conn_info_), only(only_) {}
bool connected;
ConnectionInfo conn_info;
ConnectionListenerCallback only;
};
std::queue<ConnectionNotification> m_conn_notifications;
std::thread m_thread;
std::mutex m_shutdown_mutex;
std::condition_variable m_shutdown_cv;
bool m_shutdown = false;
std::function<void()> m_on_start;
std::function<void()> m_on_exit;