#include "pch_msc.hpp" #include "threading.h" #include "libaquery.h" #include #include #include #include using namespace std; using namespace chrono_literals; #define A_TP_HAVE_PAYLOAD(x) ((x) & 0b1) #define A_TP_SET_PAYLOAD(x) ((x) |= 0b1) #define A_TP_UNSET_PAYLOAD(x) ((x) &= 0xfe) #define A_TP_IS_RUNNING(x) ((x) & 0b10) void ThreadPool::daemon_proc(uint32_t id){ auto tf = static_cast*>(this->thread_flags); auto ticking = static_cast*>(this->ticking); for(; tf[id]; this_thread::sleep_for(*ticking? 0ns:100ms)) { if (A_TP_HAVE_PAYLOAD(tf[id])) { // A_TP_SET_PAYLOAD(tf[id]); current_payload[id](); //current_payload[id].empty(); A_TP_UNSET_PAYLOAD(tf[id]); } } } void ThreadPool::tick(){ auto pq_lock = static_cast(payload_queue_lock); auto pq = static_cast *>(payload_queue); auto tf = static_cast*>(this->thread_flags); auto ticking = static_cast*>(this->ticking); auto th = static_cast(this->thread_handles); auto n_threads = (uint8_t) this->n_threads; for(; !this->terminate; this_thread::sleep_for(5ms)){ if(*ticking) { size_t sz = pq->size(); while(!pq->empty()){ while(!pq->empty() && sz < (n_threads<<10)){ for(uint8_t i = 0; i < n_threads; ++i){ if(!A_TP_HAVE_PAYLOAD(tf[i])){ pq_lock->lock(); current_payload[i] = pq->front(); A_TP_SET_PAYLOAD(tf[i]); pq->pop_front(); sz = pq->size(); pq_lock->unlock(); if (sz>=(n_threads<<10) || sz == 0) break; } } } pq_lock->lock(); while(!pq->empty()){ for(uint8_t i = 0; i < n_threads; ++i){ if(!A_TP_HAVE_PAYLOAD(tf[i])){ current_payload[i] = pq->front(); A_TP_SET_PAYLOAD(tf[i]); pq->pop_front(); if(pq->empty()) break; } } } pq_lock->unlock(); } // puts("done"); *ticking = false; } } for (uint8_t i = 0; i < n_threads; ++i) tf[i] &= 0xfd; for (uint8_t i = 0; i < n_threads; ++i) th[i].join(); delete[] th; delete[] tf; delete pq; delete pq_lock; delete ticking; auto cp = static_cast(current_payload); delete[] cp; } ThreadPool::ThreadPool(uint32_t n_threads) : n_threads(n_threads) { if (n_threads <= 0){ n_threads = thread::hardware_concurrency(); this->n_threads = n_threads; } printf("Thread pool started with %u threads;\n", n_threads); fflush(stdout); this->terminate = false; payload_queue = new deque; auto th = new thread[n_threads]; auto tf = new atomic[n_threads]; thread_handles = th; thread_flags = tf; ticking = static_cast(new atomic(false)); payload_queue_lock = new mutex(); tick_handle = new thread(&ThreadPool::tick, this); current_payload = new payload_t[n_threads]; for (uint32_t i = 0; i < n_threads; ++i){ // atomic_init(tf + i, static_cast(0b10)); tf[i] = static_cast(0b10); th[i] = thread(&ThreadPool::daemon_proc, this, i); } } void ThreadPool::enqueue_task(const payload_t& payload){ auto pq_lock = static_cast(payload_queue_lock); auto pq = static_cast *>(payload_queue); auto tf = static_cast*>(this->thread_flags); auto& ticking = *static_cast*>(this->ticking); if (!ticking){ for (uint32_t i = 0; i < n_threads; ++i){ if(!A_TP_HAVE_PAYLOAD(tf[i])){ current_payload[i] = payload; A_TP_SET_PAYLOAD(tf[i]); return; } } } pq_lock->lock(); pq->push_back(payload); ticking = true; pq_lock->unlock(); } ThreadPool::~ThreadPool() { this->terminate = true; auto tick = static_cast (tick_handle); tick->join(); delete tick; puts("Thread pool terminated."); } bool ThreadPool::busy(){ if (!*(atomic*)ticking) { for (int i = 0; i < n_threads; ++i) if (A_TP_HAVE_PAYLOAD(((atomic*)thread_flags)[i])) return true; return false; } return true; } IntervalBasedTriggerHost::IntervalBasedTriggerHost(ThreadPool* tp, Context* cxt){ this->cxt = cxt; this->tp = tp; this->triggers = new aq_map; trigger_queue_lock = new mutex(); this->now = std::chrono::high_resolution_clock::now().time_since_epoch().count(); this->running = true; this->handle = new thread(&IntervalBasedTriggerHost::tick, this); } void IntervalBasedTriggerHost::add_trigger(const char* name, StoredProcedure *p, uint32_t interval) { auto tr = IntervalBasedTrigger{.interval = interval, .time_remaining = 0, .sp = p}; auto vt_triggers = static_cast *>(this->triggers); trigger_queue_lock->lock(); vt_triggers->emplace(name, tr); //(*vt_triggers)[name] = tr; trigger_queue_lock->unlock(); } void IntervalBasedTriggerHost::tick() { auto current_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); auto delta_t = static_cast((current_time - now) / 1000000); // miliseconds precision now = current_time; auto vt_triggers = static_cast *>(this->triggers); fflush(stdout); while(this->running) { std::this_thread::sleep_for(50ms); current_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); delta_t = static_cast((current_time - now) / 1000000); // miliseconds precision now = current_time; trigger_queue_lock->lock(); for(auto& [_, t] : vt_triggers->values()) { if(t.tick(delta_t)) { payload_t payload; payload.f = execTriggerPayload; payload.args = static_cast(new StoredProcedurePayload {t.sp, cxt}); tp->enqueue_task(payload); } } trigger_queue_lock->unlock(); } } void IntervalBasedTriggerHost::remove_trigger(const char* name) { auto vt_triggers = static_cast *>(this->triggers); vt_triggers->erase(name); } void IntervalBasedTrigger::reset() { time_remaining = interval; } bool IntervalBasedTrigger::tick(uint32_t delta_t) { bool ret = false; // printf("%d %d\n",delta_t, time_remaining); if (time_remaining <= delta_t) ret = true; if (auto curr_dt = delta_t % interval; time_remaining <= curr_dt) time_remaining = interval + time_remaining - curr_dt; else time_remaining = time_remaining - curr_dt; return ret; } CallbackBasedTriggerHost::CallbackBasedTriggerHost(ThreadPool *tp, Context *cxt) { this->tp = tp; this->cxt = cxt; this->triggers = new aq_map; } void CallbackBasedTriggerHost::execute_trigger(StoredProcedure* query, StoredProcedure* action) { payload_t payload; payload.f = execTriggerPayloadCond; payload.args = static_cast( new StoredProcedurePayloadCond {query, action, cxt} ); this->tp->enqueue_task(payload); } void CallbackBasedTriggerHost::execute_trigger(const char* trigger_name) { auto vt_triggers = static_cast *>(this->triggers); auto ptr = vt_triggers->find(trigger_name); if (ptr != vt_triggers->end()) { auto& tr = ptr->second; if (!tr.materialized) { tr.query = new StoredProcedure(get_procedure(cxt, tr.query_name)); tr.action = new StoredProcedure(get_procedure(cxt, tr.action_name)); tr.materialized = true; } this->execute_trigger(AQ_DupObject(tr.query), AQ_DupObject(tr.action)); } } void CallbackBasedTriggerHost::add_trigger( const char* trigger_name, const char* table_name, const char* query_name, const char* action_name ) { auto vt_triggers = static_cast *>(this->triggers); auto tr = CallbackBasedTrigger { .trigger_name = trigger_name, .table_name = table_name, .query_name = query_name, .action_name = action_name, .materialized = false }; vt_triggers->emplace(trigger_name, tr); } void CallbackBasedTriggerHost::tick() {}