diff options
| -rw-r--r-- | pkev.h | 112 | ||||
| -rw-r--r-- | test/pkev.cpp | 76 |
2 files changed, 165 insertions, 23 deletions
@@ -9,6 +9,10 @@ typedef uint64_t pk_ev_mgr_id_T; typedef uint64_t pk_ev_id_T; typedef uint64_t pk_ev_cb_id_T; +const pk_ev_mgr_id_T pk_ev_mgr_id_T_MAX = 0xFFFFFFFFFFFFFFFF; +const pk_ev_id_T pk_ev_id_T_MAX = 0xFFFFFFFFFFFFFFFF; +const pk_ev_cb_id_T pk_ev_cb_id_T_MAX = 0xFFFFFFFFFFFFFFFF; + // TODO re-think threading // note: pk_ev_init() is NOT thread-safe @@ -25,6 +29,7 @@ pk_ev_id_T pk_ev_register_ev(pk_ev_mgr_id_T evmgr, void *user_ev_data); pk_ev_cb_id_T pk_ev_register_cb(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, pk_ev_cb_fn *cb, void *user_cb_data); void pk_ev_emit(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, void *user_emit_data); +void pk_ev_unregister_ev(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid); void pk_ev_unregister_cb(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, pk_ev_cb_id_T cbid); #endif /* PK_EV_H */ @@ -37,9 +42,7 @@ void pk_ev_unregister_cb(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, pk_ev_cb_id_T cb #include <stdatomic.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <threads.h> -#include <string.h> #ifndef PK_EV_INIT_MGR_COUNT # define PK_EV_INIT_MGR_COUNT 1 @@ -79,7 +82,9 @@ struct pk_ev { struct pk_ev_mgr { struct pk_ev *ev; - atomic_uint_fast64_t n_ev; + atomic_uint_fast64_t left_evs; + atomic_uint_fast64_t right_evs; + atomic_uint_fast64_t unused_evs; // reserved length of `pk_ev`s on this struct atomic_uint_fast64_t rn_ev; // on any given `pk_ev`, the number of callbacks reserved @@ -119,6 +124,8 @@ pk_ev_inner_calc_sz(uint64_t ev_count, uint64_t cb_count, size_t *sz_ev_list, si // base sizes size_t l_sz_ev_list = sizeof(struct pk_ev) * ev_count; size_t l_sz_ev_cb_list = sizeof(struct pk_ev_cb) * cb_count; + l_sz_ev_list += ((size_t)64 - alignof(struct pk_ev)) % (size_t)64; + l_sz_ev_cb_list += ((size_t)64 - alignof(struct pk_ev_cb)) % (size_t)64; if (sz_ev_list != nullptr) *sz_ev_list = l_sz_ev_list; if (sz_ev_cb_list != nullptr) *sz_ev_cb_list = l_sz_ev_cb_list; @@ -157,29 +164,43 @@ pk_ev_inner_ev_mgr_create(uint64_t ev_count, uint64_t cb_count) assert(ev_count < 0x100); assert(cb_count < 0x100); uint64_t i; + char *ptr; struct pk_ev *ev; size_t sz_ev_list; size_t sz_ev_cb_list; - size_t sz = pk_ev_inner_calc_sz(ev_count, cb_count, &sz_ev_list, &sz_ev_cb_list); size_t sz_offset; + size_t sz = pk_ev_inner_calc_sz(ev_count, cb_count, &sz_ev_list, &sz_ev_cb_list); struct pk_ev_mgr *mgr = (struct pk_ev_mgr*)PK_EV_MEM_ALLOC(sz, alignof(struct pk_ev_mgr), pk_ev_mstr.bkt); if (mgr == NULL) goto early_exit; - mgr->ev = (struct pk_ev*)(((char *)mgr) + sizeof(struct pk_ev_mgr)); + ptr = ((char *)mgr) + sizeof(struct pk_ev_mgr); + sz_offset = (size_t)ptr % alignof(struct pk_ev); + ptr += ((size_t)64 - sz_offset) % (size_t)64; + mgr->ev = (struct pk_ev*)ptr; atomic_init(&mgr->rn_ev, ev_count); atomic_init(&mgr->rn_cb, cb_count); - atomic_init(&mgr->n_ev, 0); + atomic_init(&mgr->left_evs, 0); + atomic_init(&mgr->right_evs, 0); + atomic_init(&mgr->unused_evs, 0xFFFFFFFFFFFFFFFF); + // find mem-aligned beginning of cb array + ptr += sz_ev_list; + sz_offset = (size_t)ptr % alignof(struct pk_ev_cb); + ptr += ((size_t)64 - sz_offset) % (size_t)64; for (i = 0; i < ev_count; ++i) { ev = &mgr->ev[i]; atomic_init(&ev->left_ev_cbs, 0); atomic_init(&ev->right_ev_cbs, 0); - sz_offset = sizeof(struct pk_ev_mgr); - sz_offset += sz_ev_list; - sz_offset += sz_ev_cb_list * i; - ev->ev_cbs = (struct pk_ev_cb*)(((char *)mgr) + sz_offset); + sz_offset = sz_ev_cb_list * i; + ev->ev_cbs = (struct pk_ev_cb*)(ptr + sz_offset); } + /* debug + fprintf(stdout, "[%s] mgr: sz: %lu, ev_count: %lu, cb_count: %lu \n", __FILE__, sz, ev_count, cb_count); + fprintf(stdout, "\t%p - ptr\n", (void*)mgr); + fprintf(stdout, "\t%p - evs (+%lu)\n", (void*)mgr->ev, (char*)mgr->ev - (char*)mgr); + fprintf(stdout, "\t%p - cbs (+%lu)\n", (void*)mgr->ev[0].ev_cbs, (char*)mgr->ev[0].ev_cbs - (char*)mgr); + */ early_exit: return mgr; } @@ -187,17 +208,23 @@ early_exit: static void pk_ev_inner_ev_mgr_clone(struct pk_ev_mgr *old, struct pk_ev_mgr *mgr) { - uint64_t i; + uint64_t i, ii; + uint64_t u, uu; struct pk_ev *ev_old; struct pk_ev *ev; - atomic_store(&mgr->n_ev, atomic_load(&old->n_ev)); - size_t old_sz_ev_cb_list; - for (i = 0; i < old->n_ev; ++i) { + ii = atomic_load(&old->right_evs); + atomic_store(&mgr->left_evs, atomic_load(&old->left_evs)); + atomic_store(&mgr->right_evs, ii); + atomic_store(&mgr->unused_evs, atomic_load(&old->unused_evs)); + for (i = 0; i < ii; ++i) { ev_old = &old->ev[i]; ev = &mgr->ev[i]; - pk_ev_inner_calc_sz(0, atomic_load(&ev_old->right_ev_cbs), nullptr, &old_sz_ev_cb_list); ev->user_ev_data = ev_old->user_ev_data; - memcpy(ev->ev_cbs, ev_old->ev_cbs, old_sz_ev_cb_list); + uu = atomic_load(&ev_old->right_ev_cbs); + for (u = 0; u <= uu; ++u) { + ev->ev_cbs[u].cb = ev_old->ev_cbs[u].cb; + ev->ev_cbs[u].user_cb_data = ev_old->ev_cbs[u].user_cb_data; + } atomic_store(&ev->left_ev_cbs, atomic_load(&ev_old->left_ev_cbs)); atomic_store(&ev->right_ev_cbs, atomic_load(&ev_old->right_ev_cbs)); } @@ -264,12 +291,15 @@ pk_ev_register_ev(pk_ev_mgr_id_T evmgr, void *user_ev_data) { assert(evmgr < 64); uint64_t new_size; + uint64_t i, ii, flg; pk_ev_id_T id; + bool found = false; struct pk_ev_mgr *mgr = nullptr; mtx_lock(&pk_ev_mstr.mtxs[evmgr]); - if (pk_ev_mstr.mgrs[evmgr]->n_ev == pk_ev_mstr.mgrs[evmgr]->rn_ev) { - new_size = PK_MAX(2, PK_MIN(255, pk_ev_mstr.mgrs[evmgr]->rn_ev * PK_EV_GROW_RATIO)); - if (new_size == pk_ev_mstr.mgrs[evmgr]->rn_ev) { + mgr = pk_ev_mstr.mgrs[evmgr]; + if (mgr->left_evs == mgr->right_evs && mgr->right_evs == mgr->rn_ev) { + new_size = PK_MAX(2, PK_MIN(255, mgr->rn_ev * PK_EV_GROW_RATIO)); + if (new_size == mgr->rn_ev) { PK_LOG_ERR("[pkev.h] need more room, but failed to grow ev count.\n"); mtx_unlock(&pk_ev_mstr.mtxs[evmgr]); exit(1); @@ -280,9 +310,29 @@ pk_ev_register_ev(pk_ev_mgr_id_T evmgr, void *user_ev_data) PK_EV_MEM_FREE(pk_ev_mstr.mgrs[evmgr], old_sz, pk_ev_mstr.bkt); pk_ev_mstr.mgrs[evmgr] = mgr; } - id = pk_ev_mstr.mgrs[evmgr]->n_ev++; + id = atomic_load(&mgr->left_evs); + if (mgr->left_evs != mgr->right_evs) { + i = atomic_load(&mgr->left_evs); + ii = atomic_load(&mgr->rn_ev); + flg = atomic_load(&mgr->unused_evs); + for (; i < ii; ++i) { + if (flg & (1lu << i)) { + if (!found) { + found = true; + flg &= ~(1lu << i); + continue; + } + break; + } + } + atomic_store(&mgr->left_evs, i); + atomic_store(&mgr->unused_evs, flg); + } else { + atomic_store(&mgr->left_evs, atomic_load(&mgr->left_evs) + 1); + atomic_store(&mgr->right_evs, atomic_load(&mgr->right_evs) + 1); + } mtx_unlock(&pk_ev_mstr.mtxs[evmgr]); - pk_ev_mstr.mgrs[evmgr]->ev[id].user_ev_data = user_ev_data; + mgr->ev[id].user_ev_data = user_ev_data; return id; } @@ -353,6 +403,26 @@ pk_ev_emit(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, void *user_emit_data) } void +pk_ev_unregister_ev(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid) +{ + assert(evmgr <= pk_ev_mstr.rn_mgrs); + struct pk_ev_mgr *mgr = pk_ev_mstr.mgrs[evmgr]; + assert(evid <= mgr->right_evs); + if (mgr == nullptr) return; + mgr->ev[evid].user_ev_data = NULL; + atomic_store(&mgr->ev[evid].left_ev_cbs, 0); + atomic_store(&mgr->ev[evid].right_ev_cbs, 0); + for (uint64_t u = 0; u < mgr->rn_cb; ++u) { + mgr->ev[evid].ev_cbs[u].cb = NULL; + mgr->ev[evid].ev_cbs[u].user_cb_data = NULL; + } + atomic_store(&mgr->unused_evs, atomic_load(&mgr->unused_evs) | (1lu << evid)); + if (evid < atomic_load(&mgr->left_evs)) { + atomic_store(&mgr->left_evs, evid); + } +} + +void pk_ev_unregister_cb(pk_ev_mgr_id_T evmgr, pk_ev_id_T evid, pk_ev_cb_id_T cbid) { struct pk_ev_mgr *mgr = pk_ev_mstr.mgrs[evmgr]; diff --git a/test/pkev.cpp b/test/pkev.cpp index 0d39336..61835df 100644 --- a/test/pkev.cpp +++ b/test/pkev.cpp @@ -90,9 +90,30 @@ int main(int argc, char *argv[]) (void)stdout; size_t i, ii; + // register, emit + { + std::packaged_task<void()> handle_ev_one([](){ ev_one.handled = true; }); + std::packaged_task<void()> handle_ev_two([](){ ev_two.handled = true; }); + pk_ev_mgr_id_T evmgr = test_setup(); + ev_one.evid = pk_ev_register_ev(evmgr, NULL); + ev_two.evid = pk_ev_register_ev(evmgr, NULL); + pk_ev_register_cb(evmgr, ev_one.evid, &invoke_packged_task, NULL); + pk_ev_register_cb(evmgr, ev_two.evid, &invoke_packged_task, NULL); + pk_ev_emit(evmgr, ev_one.evid, &handle_ev_one); + pk_ev_emit(evmgr, ev_two.evid, &handle_ev_two); + PK_LOGV_INF("%s: ev_one: %lu, ev_two: %lu\n", __FILE__, ev_one.evid, ev_two.evid); + PK_LOGV_INF("%s: ev_one: %s, ev_two: %s\n", __FILE__, ev_one.handled ? "true" : "false", ev_two.handled ? "true" : "false"); + pk_ev_teardown(); + fflush(stdout); + fflush(stderr); + if (ev_one.evid != 0 || ev_two.evid != 1) exit(1); + if (ev_one.handled == false || ev_two.handled == false) exit(1); + } - // register, emit, catch + // register, emit, clear { + ev_one.handled = false; + ev_two.handled = false; std::packaged_task<void()> handle_ev_one([](){ ev_one.handled = true; }); std::packaged_task<void()> handle_ev_two([](){ ev_two.handled = true; }); pk_ev_mgr_id_T evmgr = test_setup(); @@ -100,16 +121,65 @@ int main(int argc, char *argv[]) ev_two.evid = pk_ev_register_ev(evmgr, NULL); pk_ev_register_cb(evmgr, ev_one.evid, &invoke_packged_task, NULL); pk_ev_register_cb(evmgr, ev_two.evid, &invoke_packged_task, NULL); + pk_ev_unregister_ev(ev_one.evmgr, ev_one.evid); pk_ev_emit(evmgr, ev_one.evid, &handle_ev_one); pk_ev_emit(evmgr, ev_two.evid, &handle_ev_two); PK_LOGV_INF("%s: ev_one: %s, ev_two: %s\n", __FILE__, ev_one.handled ? "true" : "false", ev_two.handled ? "true" : "false"); pk_ev_teardown(); fflush(stdout); fflush(stderr); + if (ev_one.handled == true || ev_two.handled == false) exit(1); + } + + // register, emit, clear, register + { + ev_one.handled = false; + ev_two.handled = false; + std::packaged_task<void()> handle_ev_one([](){ ev_one.handled = true; }); + std::packaged_task<void()> handle_ev_two([](){ ev_two.handled = true; }); + pk_ev_mgr_id_T evmgr = test_setup(); + ev_one.evid = pk_ev_register_ev(evmgr, NULL); + ev_two.evid = pk_ev_register_ev(evmgr, NULL); + pk_ev_register_cb(evmgr, ev_one.evid, &invoke_packged_task, NULL); + pk_ev_register_cb(evmgr, ev_two.evid, &invoke_packged_task, NULL); + PK_LOGV_INF("%s: ev_one: %lu, ev_two: %lu\n", __FILE__, ev_one.evid, ev_two.evid); + fflush(stdout); + fflush(stderr); + if (ev_one.evid != 0 || ev_two.evid != 1) exit(1); + pk_ev_unregister_ev(ev_one.evmgr, ev_one.evid); + + pk_ev_emit(evmgr, ev_one.evid, &handle_ev_one); + pk_ev_emit(evmgr, ev_two.evid, &handle_ev_two); + PK_LOGV_INF("%s: ev_one: %s, ev_two: %s\n", __FILE__, ev_one.handled ? "true" : "false", ev_two.handled ? "true" : "false"); + fflush(stdout); + fflush(stderr); + if (ev_one.handled == true || ev_two.handled == false) exit(1); + pk_ev_unregister_ev(ev_two.evmgr, ev_two.evid); + + std::packaged_task<void()> handle_ev_thr([](){ ev_one.handled = true; }); + std::packaged_task<void()> handle_ev_for([](){ ev_two.handled = true; }); + ev_one.handled = false; + ev_two.handled = false; + ev_one.evid = pk_ev_register_ev(evmgr, NULL); + ev_two.evid = pk_ev_register_ev(evmgr, NULL); + pk_ev_register_cb(evmgr, ev_one.evid, &invoke_packged_task, NULL); + pk_ev_register_cb(evmgr, ev_two.evid, &invoke_packged_task, NULL); + PK_LOGV_INF("%s: ev_one: %lu, ev_two: %lu\n", __FILE__, ev_one.evid, ev_two.evid); + fflush(stdout); + fflush(stderr); + if (ev_one.evid != 0 || ev_two.evid != 1) exit(1); + + pk_ev_emit(evmgr, ev_one.evid, &handle_ev_thr); + pk_ev_emit(evmgr, ev_two.evid, &handle_ev_for); + PK_LOGV_INF("%s: ev_one: %s, ev_two: %s\n", __FILE__, ev_one.handled ? "true" : "false", ev_two.handled ? "true" : "false"); + fflush(stdout); + fflush(stderr); if (ev_one.handled == false || ev_two.handled == false) exit(1); + + pk_ev_teardown(); } - // threaded register, emit, catch + // threaded register, emit { std::packaged_task<void()> handle_ev_one([](){ ev_one.handled = true; }); std::packaged_task<void()> handle_ev_two([](){ ev_two.handled = true; }); @@ -126,10 +196,12 @@ int main(int argc, char *argv[]) }); t1.join(); t2.join(); + PK_LOGV_INF("%s: ev_one: %lu, ev_two: %lu\n", __FILE__, ev_one.evid, ev_two.evid); PK_LOGV_INF("%s: ev_one: %s, ev_two: %s\n", __FILE__, ev_one.handled ? "true" : "false", ev_two.handled ? "true" : "false"); pk_ev_teardown(); fflush(stdout); fflush(stderr); + if (ev_one.evid != 0 || ev_two.evid != 1) exit(1); if (ev_one.handled == false || ev_two.handled == false) exit(1); } |
