|
@@ -76,8 +76,11 @@ tor_event_free_(struct event *ev)
|
|
|
event_free(ev);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-static struct event_base *the_event_base = NULL;
|
|
|
+
|
|
|
+static tor_thread_t **threads = NULL;
|
|
|
+static struct event_base **eventloops = NULL;
|
|
|
+static tor_threadlocal_t eventloop_index;
|
|
|
+static int num_eventloops = -1;
|
|
|
|
|
|
|
|
|
* @defgroup postloop post-loop event helpers
|
|
@@ -95,11 +98,11 @@ static struct event_base *the_event_base = NULL;
|
|
|
* @{ */
|
|
|
|
|
|
|
|
|
- * An event that stops Libevent from running any more events on the current
|
|
|
+ * Events that stop Libevent from running any more events on the current
|
|
|
* iteration of its loop, until it has re-checked for socket events, signal
|
|
|
* events, timeouts, etc.
|
|
|
*/
|
|
|
-static struct event *rescan_mainloop_ev = NULL;
|
|
|
+struct event **rescan_eventloop_events = NULL;
|
|
|
|
|
|
|
|
|
* Callback to implement rescan_mainloop_ev: it simply exits the mainloop,
|
|
@@ -116,6 +119,40 @@ rescan_mainloop_cb(evutil_socket_t fd, short events, void *arg)
|
|
|
|
|
|
|
|
|
|
|
|
+struct event_base *
|
|
|
+get_eventloop(int index)
|
|
|
+{
|
|
|
+ tor_assert(index >= 0 && index < num_eventloops);
|
|
|
+ tor_assert(eventloops != NULL);
|
|
|
+ tor_assert(eventloops[index] != NULL);
|
|
|
+ return eventloops[index];
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+get_local_eventloop_index(void) {
|
|
|
+ int *index = tor_threadlocal_get(&eventloop_index);
|
|
|
+ tor_assert(index != NULL);
|
|
|
+ return *index;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+get_num_eventloops(void)
|
|
|
+{
|
|
|
+ tor_assert(num_eventloops != -1);
|
|
|
+ return num_eventloops;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+rescan_eventloops(void)
|
|
|
+{
|
|
|
+ tor_assert(rescan_eventloop_events != NULL);
|
|
|
+ for (int i=0; i<num_eventloops; i++) {
|
|
|
+ tor_assert(rescan_eventloop_events[i] != NULL);
|
|
|
+ event_active(rescan_eventloop_events[i], EV_READ, 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* MACOSX_KQUEUE_IS_BROKEN to true iff we're on a version of OSX before
|
|
|
* 10.4.0 (aka 1040). */
|
|
@@ -128,26 +165,123 @@ rescan_mainloop_cb(evutil_socket_t fd, short events, void *arg)
|
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
+
|
|
|
+void
|
|
|
+tor_evloop_init_threadlocals(void)
|
|
|
+{
|
|
|
+ tor_assert(tor_threadlocal_init(&eventloop_index) == 0);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+tor_evloop_destroy_threadlocals(void)
|
|
|
+{
|
|
|
+ tor_threadlocal_destroy(&eventloop_index);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void
|
|
|
+init_eventloop_thread(int index)
|
|
|
+{
|
|
|
+ tor_assert(index >= 0 && index < num_eventloops);
|
|
|
+ tor_assert(tor_threadlocal_get(&eventloop_index) == NULL);
|
|
|
+
|
|
|
+ int* this_thread_index = tor_malloc(sizeof(int));
|
|
|
+ *this_thread_index = index;
|
|
|
+ tor_threadlocal_set(&eventloop_index, (void *)this_thread_index);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void
|
|
|
+destroy_eventloop_thread(void)
|
|
|
+{
|
|
|
+ int *this_thread_index = (int *)tor_threadlocal_get(&eventloop_index);
|
|
|
+ if (this_thread_index != NULL) {
|
|
|
+ tor_threadlocal_set(&eventloop_index, NULL);
|
|
|
+ tor_free(this_thread_index);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct thread_data {
|
|
|
+ void (*func)(void);
|
|
|
+ int index;
|
|
|
+};
|
|
|
+
|
|
|
+static void
|
|
|
+thread_wrapper(void *data_void)
|
|
|
+{
|
|
|
+ tor_assert(data_void != NULL);
|
|
|
+ struct thread_data *data = (struct thread_data *)data_void;
|
|
|
+ void (*func)(void) = data->func;
|
|
|
+ int index = data->index;
|
|
|
+ tor_free(data_void);
|
|
|
+
|
|
|
+ init_eventloop_thread(index);
|
|
|
+ func();
|
|
|
+ destroy_eventloop_thread();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ be started using 'spawn_fn'. */
|
|
|
+void
|
|
|
+start_eventloop_threads(void (*func)(void),
|
|
|
+ tor_thread_t *(*spawn_fn)(void (*func)(void *),
|
|
|
+ void *data))
|
|
|
+{
|
|
|
+
|
|
|
+ for (int i=1; i<num_eventloops; i++) {
|
|
|
+ struct thread_data *data = tor_malloc(sizeof(struct thread_data));
|
|
|
+ data->func = func;
|
|
|
+ data->index = i;
|
|
|
+ tor_thread_t *thread = spawn_fn(thread_wrapper, (void *)data);
|
|
|
+ tor_assert(thread != NULL);
|
|
|
+ threads[i] = thread;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+join_eventloop_threads(void)
|
|
|
+{
|
|
|
+ for (int i=1; i<num_eventloops; i++) {
|
|
|
+ if (threads[i] != NULL) {
|
|
|
+ tor_assert(join_thread(threads[i]) == 0);
|
|
|
+ free_thread(threads[i]);
|
|
|
+ threads[i] = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
void
|
|
|
-tor_libevent_initialize(tor_libevent_cfg *torcfg)
|
|
|
+tor_libevent_initialize(tor_libevent_cfg *torcfg,
|
|
|
+ int num_additional_eventloops)
|
|
|
{
|
|
|
- tor_assert(the_event_base == NULL);
|
|
|
|
|
|
(void)torcfg;
|
|
|
|
|
|
+ tor_assert(num_eventloops == -1);
|
|
|
+ num_eventloops = num_additional_eventloops+1;
|
|
|
+ eventloops = tor_malloc_zero(num_eventloops*sizeof(struct event_base *));
|
|
|
+ threads = tor_malloc_zero(num_eventloops*sizeof(tor_thread_t *));
|
|
|
+ rescan_eventloop_events = tor_malloc_zero(num_eventloops*sizeof(tor_thread_t *));
|
|
|
+
|
|
|
+
|
|
|
+ be NULL from tor_malloc_zero, but we do this explicitly anyways. */
|
|
|
+ threads[0] = NULL;
|
|
|
+
|
|
|
+ int libevent_started_correctly = 1;
|
|
|
+
|
|
|
{
|
|
|
int attempts = 0;
|
|
|
struct event_config *cfg;
|
|
|
|
|
|
+ tor_assert(evthread_use_pthreads() == 0);
|
|
|
+
|
|
|
++attempts;
|
|
|
cfg = event_config_new();
|
|
|
tor_assert(cfg);
|
|
|
|
|
|
-
|
|
|
- * socketpair() attempt. */
|
|
|
- event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK);
|
|
|
-
|
|
|
if (torcfg->num_cpus > 0)
|
|
|
event_config_set_num_cpus_hint(cfg, torcfg->num_cpus);
|
|
|
|
|
@@ -155,26 +289,28 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
|
|
|
* Libevent any dup'd fds. This lets us avoid some syscalls. */
|
|
|
event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);
|
|
|
|
|
|
- the_event_base = event_base_new_with_config(cfg);
|
|
|
+ for (int i=0; i<num_eventloops; i++) {
|
|
|
+ eventloops[i] = event_base_new_with_config(cfg);
|
|
|
+ rescan_eventloop_events[i] = event_new(eventloops[i], -1, 0,
|
|
|
+ rescan_mainloop_cb,
|
|
|
+ eventloops[i]);
|
|
|
+ libevent_started_correctly = (libevent_started_correctly &&
|
|
|
+ eventloops[i] != NULL &&
|
|
|
+ rescan_eventloop_events[i] != NULL);
|
|
|
+ }
|
|
|
|
|
|
event_config_free(cfg);
|
|
|
}
|
|
|
|
|
|
- if (!the_event_base) {
|
|
|
+ if (libevent_started_correctly == 0) {
|
|
|
|
|
|
log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue.");
|
|
|
exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
- rescan_mainloop_ev = event_new(the_event_base, -1, 0,
|
|
|
- rescan_mainloop_cb, the_event_base);
|
|
|
- if (!rescan_mainloop_ev) {
|
|
|
-
|
|
|
- log_err(LD_GENERAL, "Unable to create rescan event: cannot continue.");
|
|
|
- exit(1);
|
|
|
-
|
|
|
- }
|
|
|
+
|
|
|
+ init_eventloop_thread(0);
|
|
|
|
|
|
log_info(LD_GENERAL,
|
|
|
"Initialized libevent version %s using method %s. Good.",
|
|
@@ -188,22 +324,26 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
|
|
|
bool
|
|
|
tor_libevent_is_initialized(void)
|
|
|
{
|
|
|
- return the_event_base != NULL;
|
|
|
+ return num_eventloops != -1;
|
|
|
}
|
|
|
|
|
|
|
|
|
MOCK_IMPL(struct event_base *,
|
|
|
tor_libevent_get_base, (void))
|
|
|
{
|
|
|
- tor_assert(the_event_base != NULL);
|
|
|
- return the_event_base;
|
|
|
+ int *index = tor_threadlocal_get(&eventloop_index);
|
|
|
+ tor_assert(index != NULL);
|
|
|
+ struct event_base *event_base = eventloops[*index];
|
|
|
+ tor_assert(event_base != NULL);
|
|
|
+ return event_base;
|
|
|
}
|
|
|
|
|
|
|
|
|
const char *
|
|
|
tor_libevent_get_method(void)
|
|
|
{
|
|
|
- return event_base_get_method(the_event_base);
|
|
|
+ struct event_base *event_base = tor_libevent_get_base();
|
|
|
+ return event_base_get_method(event_base);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -354,7 +494,12 @@ mainloop_event_postloop_cb(evutil_socket_t fd, short what, void *arg)
|
|
|
* callback run after rescan_mainloop_cb is called -- that is, on the
|
|
|
* next iteration of the loop.
|
|
|
*/
|
|
|
- event_active(rescan_mainloop_ev, EV_READ, 1);
|
|
|
+
|
|
|
+ int *index = tor_threadlocal_get(&eventloop_index);
|
|
|
+ tor_assert(index != NULL);
|
|
|
+ struct event *rescan_event = rescan_eventloop_events[*index];
|
|
|
+ tor_assert(rescan_event != NULL);
|
|
|
+ event_active(rescan_event, EV_READ, 1);
|
|
|
|
|
|
mainloop_event_t *mev = arg;
|
|
|
mev->cb(mev, mev->userdata);
|
|
@@ -495,10 +640,29 @@ tor_init_libevent_rng(void)
|
|
|
void
|
|
|
tor_libevent_free_all(void)
|
|
|
{
|
|
|
- tor_event_free(rescan_mainloop_ev);
|
|
|
- if (the_event_base)
|
|
|
- event_base_free(the_event_base);
|
|
|
- the_event_base = NULL;
|
|
|
+
|
|
|
+ join_eventloop_threads();
|
|
|
+
|
|
|
+
|
|
|
+ called this locally. */
|
|
|
+ destroy_eventloop_thread();
|
|
|
+
|
|
|
+
|
|
|
+ for (int i=0; i<num_eventloops; i++) {
|
|
|
+ event_free(rescan_eventloop_events[i]);
|
|
|
+ rescan_eventloop_events[i] = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ for (int i=0; i<num_eventloops; i++) {
|
|
|
+ event_base_free(eventloops[i]);
|
|
|
+ eventloops[i] = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ tor_free(eventloops);
|
|
|
+ tor_free(threads);
|
|
|
+ tor_free(rescan_eventloop_events);
|
|
|
+ num_eventloops = -1;
|
|
|
}
|
|
|
|
|
|
|