/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2011 InspIRCd Development Team * See: http://wiki.inspircd.org/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "cull_list.h" #include "threadengine.h" #include #include #include #ifdef HAS_EVENTFD #include class ThreadSignalSocket : public EventHandler { public: ThreadSignalSocket() { SetFd(eventfd(0, EFD_NONBLOCK)); if (fd < 0) throw CoreException("Could not create eventfd " + std::string(strerror(errno))); ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE); } ~ThreadSignalSocket() { close(fd); } void Notify() { eventfd_write(fd, 1); } void HandleEvent(EventType et, int errornum) { if (et == EVENT_READ) { eventfd_t dummy; eventfd_read(fd, &dummy); ServerInstance->Threads->result_loop(); } else { ServerInstance->Threads->job_lock.lock(); ServerInstance->Threads->result_ss = NULL; ServerInstance->Threads->job_lock.unlock(); ServerInstance->GlobalCulls->AddItem(this); } } }; #else class ThreadSignalSocket : public EventHandler { int send_fd; public: ThreadSignalSocket() { int fds[2]; if (pipe(fds)) throw CoreException("Could not create pipe " + std::string(strerror(errno))); SetFd(fds[0]); send_fd = fds[1]; ServerInstance->SE->NonBlocking(fd); ServerInstance->SE->AddFd(this, FD_WANT_FAST_READ | FD_WANT_NO_WRITE); } ~ThreadSignalSocket() { close(send_fd); close(fd); } void Notify() { static const char dummy = '*'; write(send_fd, &dummy, 1); } void HandleEvent(EventType et, int errornum) { if (et == EVENT_READ) { char dummy[128]; read(fd, dummy, 128); ServerInstance->Threads->result_loop(); } else { ServerInstance->Threads->job_lock.lock(); ServerInstance->Threads->result_ss = NULL; ServerInstance->Threads->job_lock.unlock(); ServerInstance->GlobalCulls->AddItem(this); } } }; #endif ThreadEngine::ThreadEngine() : result_ss(NULL) { } ThreadEngine::~ThreadEngine() { delete result_ss; } void* ThreadEngine::Runner::entry_point(void* parameter) { /* Recommended by nenolod, signal safety on a per-thread basis */ sigset_t set; sigemptyset(&set); sigaddset(&set, SIGPIPE); pthread_sigmask(SIG_BLOCK, &set, NULL); Runner* te = static_cast(parameter); te->main_loop(); return parameter; } ThreadEngine::Runner::Runner(ThreadEngine* t) : te(t), current(NULL) { if (pthread_create(&id, NULL, entry_point, this) != 0) throw CoreException("Unable to create new thread: " + std::string(strerror(errno))); } ThreadEngine::Runner::~Runner() { pthread_join(id, NULL); } void ThreadEngine::Submit(Job* job) { Mutex::Lock lock(job_lock); // TODO launch more threads if we have a queue and more are allowed // TODO clean up threads at garbage collection time (all but one) if (threads.empty()) threads.push_back(new Runner(this)); if (result_ss == NULL) result_ss = new ThreadSignalSocket(); submit_q.push_back(job); submit_s.signal_one(); } static class TerminateJob : public Job { public: TerminateJob() : Job(NULL) { cancel(); } void run() {} void finish() {} } terminator; void ThreadEngine::Runner::main_loop() { te->job_lock.lock(); while (1) { while (te->submit_q.empty()) te->submit_s.wait(te->job_lock); current = te->submit_q.front(); te->submit_q.pop_front(); if (!current->IsCancelled()) { te->job_lock.unlock(); try { current->run(); } catch (...) { } te->job_lock.lock(); } te->result_q.push_back(current); if (current == &terminator) break; current = NULL; te->result_sc.signal_one(); if (te->result_ss) te->result_ss->Notify(); } te->job_lock.unlock(); // current == terminator, we die } void ThreadEngine::result_loop() { job_lock.lock(); while (!result_q.empty()) { Job* job = result_q.front(); result_q.pop_front(); job_lock.unlock(); job->finish(); job_lock.lock(); } job_lock.unlock(); } void ThreadEngine::BlockForUnload(Module* mod) { job_lock.lock(); while (1) { bool found = false; for(std::list::iterator i = submit_q.begin(); i != submit_q.end(); i++) { Job* j = *i; if (j->BlocksUnload(mod)) { found = true; j->cancel(); } } for(std::vector::iterator i = threads.begin(); i != threads.end(); i++) { Runner* r = *i; if (r->current && r->current->BlocksUnload(mod)) { found = true; r->current->cancel(); } } if (!found) break; // wait for an item to finish so we can check again result_sc.wait(job_lock); } job_lock.unlock(); // clean up any references remaining in the result loop // (that's where any we were waiting for are sitting) result_loop(); }