diff --git a/.gitignore b/.gitignore index 8adf9bc89..ae1054b9a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ *.o *.plist -llarpd \ No newline at end of file +llarpd + +*.test +*.bin \ No newline at end of file diff --git a/Makefile b/Makefile index c0297e813..8597284d8 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,18 @@ STATIC_OBJ = $(STATIC_SRC_CPP:.cpp=.cpp.o) $(STATIC_SRC_C:.c=.c.o) DAEMON_SRC = $(wildcard $(REPO)/daemon/*.c) DAEMON_OBJ = $(DAEMON_SRC:.c=.c.o) +TEST_SRC_C = $(wildcard $(REPO)/test/*.c) +TEST_SRC_CPP = $(wildcard $(REPO)/test/*.cpp) + +TEST_OBJ_C = $(TEST_SRC_C:.c=.c.bin) +TEST_OBJ_CPP = $(TEST_SRC_CPP:.cpp=.cpp.bin) + +TEST_SRC = $(TEST_SRC_C) $(TEST_SRC_CPP) + +TEST_OBJ = $(TEST_OBJ_C) $(TEST_OBJ_CPP) + HDRS = $(wildcard $(REPO)/llarp/*.hpp) $(wildcard $(REPO)/include/*/*.h) -SRCS = $(DAEMON_SRC) $(STATIC_SRC_CPP) $(STATIC_SRC_C) +SRCS = $(DAEMON_SRC) $(STATIC_SRC_CPP) $(STATIC_SRC_C) $(TEST_SRC_C) $(TEST_SRC_CPP) FORMAT = clang-format SODIUM_FLAGS = $(shell pkg-config --cflags libsodium) @@ -31,7 +41,7 @@ endif REQUIRED_CFLAGS = $(LIBUV_FLAGS) $(SODIUM_FLAGS) -I$(REPO)/include -std=c99 $(CFLAGS) $(DEBUG_FLAGS) $(VER_FLAGS) REQUIRED_CXXFLAGS = $(LIBUV_FLAGS) $(SODIUM_FLAGS) -I$(REPO)/include -std=c++17 $(CXXFLAGS) $(DEBUG_FLAGS) $(VER_FLAGS) -REQUIRED_LDFLAGS = $(LDFLAGS) -ljemalloc $(SODIUM_LIBS) $(LIBUV_LIBS) +REQUIRED_LDFLAGS = $(LDFLAGS) -ljemalloc $(SODIUM_LIBS) $(LIBUV_LIBS) -lm -lstdc++ all: build @@ -40,6 +50,21 @@ format: $(HDRS) $(SRCS) build: $(EXE) +test: $(TEST_OBJ_CPP) $(TEST_OBJ_C) + + +$(TEST_SRC): $(STATIC_LIB) + +$(TEST_OBJ_CPP): $(TEST_SRC_CPP) + $(CXX) $(REQUIRED_CXXFLAGS) $< -o $<.bin $(STATIC_LIB) $(REQUIRED_LDFLAGS) + mv $<.bin $<.test + $<.test + +$(TEST_OBJ_C): $(TEST_SRC_C) + $(CC) $(REQUIRED_CFLAGS) $< -o $<.bin $(STATIC_LIB) $(REQUIRED_LDFLAGS) + mv $<.bin $<.test + $<.test + $(EXE): $(DAEMON_OBJ) $(STATIC_LIB) $(CXX) $(DAEMON_OBJ) $(STATIC_LIB) $(REQUIRED_LDFLAGS) -o $(EXE) @@ -53,4 +78,4 @@ $(STATIC_LIB): $(STATIC_OBJ) $(AR) -r $(STATIC_LIB) $(STATIC_OBJ) clean: - $(RM) $(DAEMON_OBJ) $(EXE) $(STATIC_OBJ) $(STATIC_LIB) + $(RM) $(DAEMON_OBJ) $(EXE) $(STATIC_OBJ) $(STATIC_LIB) $(TEST_OBJ) diff --git a/include/llarp/crypto.h b/include/llarp/crypto.h index 8b24ed913..aaaea01d2 100644 --- a/include/llarp/crypto.h +++ b/include/llarp/crypto.h @@ -25,16 +25,24 @@ typedef uint8_t llarp_hmacsec_t[HMACSECSIZE]; typedef uint8_t llarp_sig_t[SIGSIZE]; typedef uint8_t llarp_tunnel_nounce_t[TUNNOUNCESIZE]; +struct llarp_keypair +{ + llarp_pubkey_t pub; + llarp_seckey_t sec; +}; + +typedef bool (*llarp_dh_func)(llarp_sharedkey_t *, llarp_pubkey_t, llarp_tunnel_nounce_t, llarp_seckey_t); + struct llarp_crypto { bool (*xchacha20)(llarp_buffer_t, llarp_sharedkey_t, llarp_nounce_t); - bool (*dh_client)(llarp_sharedkey_t *, llarp_pubkey_t, llarp_tunnel_nounce_t, - llarp_seckey_t); - bool (*dh_server)(llarp_sharedkey_t *, llarp_pubkey_t, llarp_tunnel_nounce_t, - llarp_seckey_t); + llarp_dh_func dh_client; + llarp_dh_func dh_server; bool (*hash)(llarp_hash_t *, llarp_buffer_t); bool (*hmac)(llarp_hash_t *, llarp_buffer_t, llarp_hmacsec_t); bool (*sign)(llarp_sig_t *, llarp_seckey_t, llarp_buffer_t); bool (*verify)(llarp_pubkey_t, llarp_buffer_t, llarp_sig_t); + void (*randomize)(llarp_buffer_t); + void (*keygen)(struct llarp_keypair *); }; void llarp_crypto_libsodium_init(struct llarp_crypto *c); diff --git a/include/llarp/crypto_async.h b/include/llarp/crypto_async.h new file mode 100644 index 000000000..0e027a3cc --- /dev/null +++ b/include/llarp/crypto_async.h @@ -0,0 +1,37 @@ +#ifndef LLARP_CRYPTO_ASYNC_H_ +#define LLARP_CRYPTO_ASYNC_H_ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + struct llarp_async_dh; + + struct llarp_async_dh * llarp_async_dh_new(struct llarp_crypto * crypto, struct llarp_ev_loop * ev, struct llarp_threadpool * tp); + void llarp_async_dh_free(struct llarp_async_dh ** dh); + + struct llarp_dh_result; + + typedef void (*llarp_dh_complete_hook)(struct llarp_dh_result *); + + + struct llarp_dh_internal; + + struct llarp_dh_result + { + struct llarp_dh_internal * impl; + llarp_sharedkey_t result; + void * user; + llarp_dh_complete_hook hook; + }; + + void llarp_async_client_dh(struct llarp_async_dh * dh, llarp_seckey_t ourkey, llarp_pubkey_t theirkey, llarp_tunnel_nounce_t nounce, llarp_dh_complete_hook result, void * user); + void llarp_async_server_dh(struct llarp_async_dh * dh, llarp_seckey_t ourkey, llarp_pubkey_t theirkey, llarp_tunnel_nounce_t nounce, llarp_dh_complete_hook result, void * user); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/llarp/ev.h b/include/llarp/ev.h index 351c8edbe..746c8ec3a 100644 --- a/include/llarp/ev.h +++ b/include/llarp/ev.h @@ -34,21 +34,33 @@ int llarp_ev_add_udp_listener(struct llarp_ev_loop *ev, int llarp_ev_close_udp_listener(struct llarp_udp_listener *listener); -struct llarp_ev_job { + struct llarp_ev_async_call; + +typedef void (*llarp_ev_work_func)(struct llarp_ev_async_call *); + + + struct llarp_ev_caller; + +struct llarp_ev_async_call { /** the loop this job belongs to */ - struct llarp_ev_loop *loop; + const struct llarp_ev_loop *loop; + /** private implementation */ + const struct llarp_ev_caller * parent; /** user data */ - void *user; - /** work is called async when ready in the event loop thread */ - void (*work)(struct llarp_ev_job *); + const void *user; + /** + work is called async when ready in the event loop thread + must not free from inside this call as it is done elsewhere + */ + const llarp_ev_work_func work; }; -/** - call work async in event loop thread (thread safe) - return true if we queued the job otherwise return false - */ -bool llarp_ev_async(struct llarp_ev_loop *ev, struct llarp_ev_job job); + struct llarp_ev_caller * llarp_ev_prepare_async(struct llarp_ev_loop *ev, llarp_ev_work_func func); + + bool llarp_ev_call_async(struct llarp_ev_caller * c, void * user); + void llarp_ev_caller_stop(struct llarp_ev_caller * c); + #ifdef __cplusplus } #endif diff --git a/include/llarp/threadpool.h b/include/llarp/threadpool.h index 55efd3503..cf289e84c 100644 --- a/include/llarp/threadpool.h +++ b/include/llarp/threadpool.h @@ -10,16 +10,21 @@ struct llarp_threadpool; struct llarp_threadpool *llarp_init_threadpool(int workers); void llarp_free_threadpool(struct llarp_threadpool **tp); + typedef void (*llarp_thread_work_func)(void*); + + /** job to be done in worker thread */ struct llarp_thread_job { /** called async after work is executed */ - struct llarp_ev_job *result; + struct llarp_ev_caller * caller; + void * data; + /** user data to pass to work function */ void *user; /** called in threadpool worker thread */ - void (*work)(void *); + llarp_thread_work_func work; }; void llarp_threadpool_queue_job(struct llarp_threadpool *tp, diff --git a/llarp/crypto_async.c b/llarp/crypto_async.c new file mode 100644 index 000000000..54fd96e47 --- /dev/null +++ b/llarp/crypto_async.c @@ -0,0 +1,85 @@ +#include +#include +#include + +struct llarp_async_dh +{ + llarp_dh_func client; + llarp_dh_func server; + struct llarp_threadpool * tp; + struct llarp_ev_caller * caller; +}; + +struct llarp_dh_internal +{ + llarp_dh_func func; + llarp_pubkey_t theirkey; + llarp_seckey_t ourkey; + llarp_tunnel_nounce_t nounce; + struct llarp_dh_result result; +}; + +static void llarp_crypto_dh_work(void * user) +{ + struct llarp_dh_internal * impl = (struct llarp_dh_internal *)user; + impl->func(&impl->result.result, impl->theirkey, impl->nounce, impl->ourkey); +} + +static void llarp_crypto_dh_result(struct llarp_ev_async_call * call) +{ + struct llarp_dh_internal * impl = (struct llarp_dh_internal *) call->user; + impl->result.hook(&impl->result); + llarp_g_mem.free(impl); +} + +static void llarp_async_dh_exec(struct llarp_async_dh * dh, llarp_dh_func func, llarp_seckey_t ourkey, llarp_pubkey_t theirkey, llarp_tunnel_nounce_t nounce, llarp_dh_complete_hook result, void * user) +{ + struct llarp_dh_internal * impl = llarp_g_mem.alloc(sizeof(struct llarp_dh_internal), 16); + memcpy(impl->theirkey, theirkey, sizeof(llarp_pubkey_t)); + memcpy(impl->ourkey, ourkey, sizeof(llarp_seckey_t)); + memcpy(impl->nounce, nounce, sizeof(llarp_tunnel_nounce_t)); + impl->result.impl = impl; + impl->result.user = user; + impl->result.hook = result; + impl->func = func; + struct llarp_thread_job job = { + .caller = dh->caller, + .data = impl, + .user = impl, + .work = &llarp_crypto_dh_work + }; + llarp_threadpool_queue_job(dh->tp, job); +} + + +void llarp_async_client_dh(struct llarp_async_dh * dh, llarp_seckey_t ourkey, llarp_pubkey_t theirkey, llarp_tunnel_nounce_t nounce, llarp_dh_complete_hook result, void * user) +{ + llarp_async_dh_exec(dh, dh->client, ourkey, theirkey, nounce, result, user); +} + +void llarp_async_server_dh(struct llarp_async_dh * dh, llarp_seckey_t ourkey, llarp_pubkey_t theirkey, llarp_tunnel_nounce_t nounce, llarp_dh_complete_hook result, void * user) +{ + llarp_async_dh_exec(dh, dh->server, ourkey, theirkey, nounce, result, user); +} + + + +struct llarp_async_dh * llarp_async_dh_new(struct llarp_crypto * crypto, struct llarp_ev_loop * ev, struct llarp_threadpool * tp) +{ + struct llarp_async_dh * dh = llarp_g_mem.alloc(sizeof(struct llarp_async_dh), 16); + dh->client = crypto->dh_client; + dh->server = crypto->dh_server; + dh->tp = tp; + dh->caller = llarp_ev_prepare_async(ev, &llarp_crypto_dh_result); + return dh; +} + +void llarp_async_dh_free(struct llarp_async_dh ** dh) +{ + if(*dh) + { + llarp_ev_caller_stop((*dh)->caller); + llarp_g_mem.free(*dh); + *dh = NULL; + } +} diff --git a/llarp/crypto_libsodium.cpp b/llarp/crypto_libsodium.cpp index cb352e022..955ade8e2 100644 --- a/llarp/crypto_libsodium.cpp +++ b/llarp/crypto_libsodium.cpp @@ -1,17 +1,15 @@ #include -#include -#include -#include -#include +#include +#include namespace llarp { namespace sodium { -bool xchacha20(llarp_buffer_t buff, llarp_sharedkey_t k, llarp_nounce_t n) { +static bool xchacha20(llarp_buffer_t buff, llarp_sharedkey_t k, llarp_nounce_t n) { uint8_t *base = (uint8_t *)buff.base; return crypto_stream_xchacha20_xor(base, base, buff.sz, n, k) == 0; } -bool dh(llarp_sharedkey_t *shared, uint8_t *client_pk, uint8_t *server_pk, +static bool dh(llarp_sharedkey_t *shared, uint8_t *client_pk, uint8_t *server_pk, uint8_t *remote_key, uint8_t *local_key) { uint8_t *out = *shared; const size_t outsz = SHAREDKEYSIZE; @@ -26,7 +24,7 @@ bool dh(llarp_sharedkey_t *shared, uint8_t *client_pk, uint8_t *server_pk, return true; } -bool dh_client(llarp_sharedkey_t *shared, llarp_pubkey_t pk, +static bool dh_client(llarp_sharedkey_t *shared, llarp_pubkey_t pk, llarp_tunnel_nounce_t n, llarp_seckey_t sk) { llarp_pubkey_t local_pk; crypto_scalarmult_base(local_pk, sk); @@ -37,7 +35,7 @@ bool dh_client(llarp_sharedkey_t *shared, llarp_pubkey_t pk, return false; } -bool dh_server(llarp_sharedkey_t *shared, llarp_pubkey_t pk, +static bool dh_server(llarp_sharedkey_t *shared, llarp_pubkey_t pk, llarp_tunnel_nounce_t n, llarp_seckey_t sk) { llarp_pubkey_t local_pk; crypto_scalarmult_base(local_pk, sk); @@ -48,31 +46,44 @@ bool dh_server(llarp_sharedkey_t *shared, llarp_pubkey_t pk, return false; } -bool hash(llarp_hash_t *result, llarp_buffer_t buff) { +static bool hash(llarp_hash_t *result, llarp_buffer_t buff) { const uint8_t *base = (const uint8_t *)buff.base; return crypto_generichash(*result, HASHSIZE, base, buff.sz, nullptr, 0) != -1; } -bool hmac(llarp_hash_t *result, llarp_buffer_t buff, llarp_seckey_t secret) { +static bool hmac(llarp_hash_t *result, llarp_buffer_t buff, llarp_seckey_t secret) { const uint8_t *base = (const uint8_t *)buff.base; return crypto_generichash(*result, sizeof(llarp_hash_t), base, buff.sz, secret, HMACSECSIZE) != -1; } -bool sign(llarp_sig_t *result, llarp_seckey_t secret, llarp_buffer_t buff) { +static bool sign(llarp_sig_t *result, llarp_seckey_t secret, llarp_buffer_t buff) { const uint8_t *base = (const uint8_t *)buff.base; return crypto_sign_detached(*result, nullptr, base, buff.sz, secret) != -1; } -bool verify(llarp_pubkey_t pub, llarp_buffer_t buff, llarp_sig_t sig) { +static bool verify(llarp_pubkey_t pub, llarp_buffer_t buff, llarp_sig_t sig) { const uint8_t *base = (const uint8_t *)buff.base; return crypto_sign_verify_detached(sig, base, buff.sz, pub) != -1; } + + static void randomize(llarp_buffer_t buff) + { + randombytes((unsigned char *)buff.base, buff.sz); + } + + static void keygen(struct llarp_keypair * keys) + { + randombytes(keys->sec, sizeof(llarp_seckey_t)); + unsigned char sk[64]; + crypto_sign_seed_keypair(keys->pub, sk, keys->sec); + } } // namespace sodium } // namespace llarp extern "C" { void llarp_crypto_libsodium_init(struct llarp_crypto *c) { + assert(sodium_init() != -1); c->xchacha20 = llarp::sodium::xchacha20; c->dh_client = llarp::sodium::dh_client; c->dh_server = llarp::sodium::dh_server; @@ -80,5 +91,7 @@ void llarp_crypto_libsodium_init(struct llarp_crypto *c) { c->hmac = llarp::sodium::hmac; c->sign = llarp::sodium::sign; c->verify = llarp::sodium::verify; + c->randomize = llarp::sodium::randomize; + c->keygen = llarp::sodium::keygen; } } diff --git a/llarp/ev.cpp b/llarp/ev.cpp index 16ebad5db..2f07a415c 100644 --- a/llarp/ev.cpp +++ b/llarp/ev.cpp @@ -2,6 +2,65 @@ #include #include +#include +#include + +struct llarp_ev_caller +{ + static void * operator new(size_t sz) + { + return llarp::Alloc(); + } + + static void operator delete(void * ptr) + { + llarp_g_mem.free(ptr); + } + + + llarp_ev_caller(llarp_ev_loop * ev, llarp_ev_work_func func) : + loop(ev), + work(func) + { + async.data = this; + } + + ~llarp_ev_caller() + { + } + + bool appendCall(void * user) + { + std::unique_lock lock(access); + bool should = pending.size() == 0; + llarp_ev_async_call * call = new llarp_ev_async_call{ + loop, + this, + user, + this->work}; + pending.push(call); + return should; + } + + void Call() + { + std::unique_lock lock(access); + while(pending.size() > 0) + { + auto & front = pending.front(); + front->work(front); + pending.pop(); + } + } + + std::mutex access; + struct llarp_ev_loop * loop; + uv_async_t async; + std::queue pending; + llarp_ev_work_func work; +}; + + struct llarp_ev_loop { uv_loop_t _loop; @@ -61,16 +120,14 @@ static void udp_close_cb(uv_handle_t *handle) { namespace llarp { -static void ev_handle_async_closed(uv_handle_t *handle) { - struct llarp_ev_job *ev = static_cast(handle->data); - llarp_g_mem.free(ev); - llarp_g_mem.free(handle); +static void ev_caller_async_closed(uv_handle_t *handle) { + llarp_ev_caller *caller = static_cast(handle->data); + delete caller; } -static void ev_handle_async(uv_async_t *handle) { - struct llarp_ev_job *ev = static_cast(handle->data); - ev->work(ev); - uv_close((uv_handle_t *)handle, ev_handle_async_closed); +static void ev_handle_async_call(uv_async_t *handle) { + llarp_ev_caller * caller = static_cast(handle->data); + caller->Call(); } } // namespace llarp @@ -132,23 +189,29 @@ int llarp_ev_close_udp_listener(struct llarp_udp_listener *listener) { void llarp_ev_loop_stop(struct llarp_ev_loop *loop) { uv_stop(loop->loop()); } -bool llarp_ev_async(struct llarp_ev_loop *loop, struct llarp_ev_job job) { - struct llarp_ev_job *job_copy = - static_cast(llarp_g_mem.alloc( - sizeof(struct llarp_ev_job), llarp::alignment())); - job_copy->work = job.work; - job_copy->loop = loop; - job_copy->user = job.user; - uv_async_t *async = static_cast( - llarp_g_mem.alloc(sizeof(uv_async_t), llarp::alignment())); - async->data = job_copy; - if (uv_async_init(loop->loop(), async, llarp::ev_handle_async) == 0 && - uv_async_send(async)) - return true; - else { - llarp_g_mem.free(job_copy); - llarp_g_mem.free(async); - return false; + struct llarp_ev_caller * llarp_ev_prepare_async(struct llarp_ev_loop * loop, llarp_ev_work_func work) +{ + llarp_ev_caller * caller = new llarp_ev_caller(loop, work); + if(uv_async_init(loop->loop(), &caller->async, llarp::ev_handle_async_call) == 0) + return caller; + else + { + delete caller; + return nullptr; } } + +bool llarp_ev_call_async(struct llarp_ev_caller * caller, void * user) +{ + if(caller->appendCall(user)) + return uv_async_send(&caller->async) == 0; + else + return true; +} + +void llarp_ev_caller_stop(struct llarp_ev_caller * caller) +{ + uv_close((uv_handle_t*)&caller->async, llarp::ev_caller_async_closed); +} + } diff --git a/llarp/mem.hpp b/llarp/mem.hpp index 0924f5fa9..5f6f4fe38 100644 --- a/llarp/mem.hpp +++ b/llarp/mem.hpp @@ -6,6 +6,13 @@ namespace llarp { template static constexpr size_t alignment() { return std::exp2(1 + std::floor(std::log2(sizeof(T)))); } + + template + static T * Alloc(llarp_alloc * mem=&llarp_g_mem) + { + return static_cast( + mem->alloc(sizeof(T), alignment())); + } } // namespace llarp #endif diff --git a/llarp/threadpool.cpp b/llarp/threadpool.cpp index 2027c5d57..9feacfcdc 100644 --- a/llarp/threadpool.cpp +++ b/llarp/threadpool.cpp @@ -4,7 +4,7 @@ namespace llarp { namespace thread { Pool::Pool(size_t workers) { - stop.store(true); + stop.store(false); while (workers--) { threads.emplace_back([this] { for (;;) { @@ -21,10 +21,12 @@ Pool::Pool(size_t workers) { // do work job.work(job.user); // inform result if needed - if (job.result && job.result->loop) - if (!llarp_ev_async(job.result->loop, *job.result)) { + if (job.caller) + { + if (!llarp_ev_call_async(job.caller, job.data)) { std::cerr << "failed to queue result in thread worker" << std::endl; } + } } }); } @@ -73,6 +75,16 @@ struct llarp_threadpool *llarp_init_threadpool(int workers) { void llarp_threadpool_join(struct llarp_threadpool *pool) { pool->impl.Join(); } +void llarp_threadpool_start(struct llarp_threadpool * pool) +{ + /** no op */ +} + + void llarp_threadpool_queue_job(struct llarp_threadpool * pool, struct llarp_thread_job job) + { + pool->impl.QueueJob(job); + } + void llarp_free_threadpool(struct llarp_threadpool **pool) { delete *pool; *pool = nullptr; diff --git a/test/test_async_dh.c b/test/test_async_dh.c new file mode 100644 index 000000000..601543fd4 --- /dev/null +++ b/test/test_async_dh.c @@ -0,0 +1,74 @@ +#include +#include + +#include + +struct dh_bench_main +{ + size_t completed; + size_t num; + struct llarp_ev_loop * ev; + struct llarp_async_dh * dh; +}; + +static void handle_dh_complete(struct llarp_dh_result * res) +{ + struct dh_bench_main * m = (struct dh_bench_main *) res->user; + m->completed++; + if(m->completed % 1000 == 0) + printf("completed %ld\n", m->completed); + if(m->completed == m->num) + { + printf("we done\n"); + llarp_ev_loop_stop(m->ev); + } +} + +int main(int argc, char * argv[]) +{ + struct dh_bench_main dh_main; + struct llarp_crypto crypto; + struct llarp_threadpool * tp; + + llarp_mem_jemalloc(); + llarp_crypto_libsodium_init(&crypto); + llarp_ev_loop_alloc(&dh_main.ev); + + tp = llarp_init_threadpool(8); + dh_main.dh = llarp_async_dh_new(&crypto, dh_main.ev, tp); + llarp_threadpool_start(tp); + + /* do work here */ + dh_main.num = 100000; + dh_main.completed = 0; + struct llarp_keypair ourkey; + struct llarp_keypair theirkey; + + crypto.keygen(&ourkey); + crypto.keygen(&theirkey); + + + llarp_tunnel_nounce_t nounce; + llarp_buffer_t n_buff; + n_buff.base = nounce; + n_buff.cur = n_buff.base; + n_buff.sz = sizeof(llarp_tunnel_nounce_t); + + size_t sz = dh_main.num; + printf("starting %ld dh jobs\n", sz); + while(sz--) + { + crypto.randomize(n_buff); + llarp_async_client_dh(dh_main.dh, ourkey.sec, theirkey.pub, nounce, handle_dh_complete, &dh_main); + } + printf("started %ld dh jobs\n", dh_main.num); + llarp_ev_loop_run(dh_main.ev); + + llarp_threadpool_join(tp); + llarp_async_dh_free(&dh_main.dh); + + llarp_ev_loop_free(&dh_main.ev); + llarp_free_threadpool(&tp); + printf("did %ld of %ld work\n", dh_main.completed, dh_main.num); + return 0; +} diff --git a/test/test_cpp.cpp b/test/test_cpp.cpp new file mode 100644 index 000000000..70b510c6e --- /dev/null +++ b/test/test_cpp.cpp @@ -0,0 +1,6 @@ + + +int main(int argc, char * argv[]) +{ + return 0; +}