void set_next_rel (cli_infos_t *infos, gint offset) { xmmsc_result_t *res; res = xmmsc_playlist_set_next_rel (infos->sync, offset); xmmsc_result_wait (res); tickle (res, infos); }
void Scheduler::schedule(boost::function<void ()> dg, tid_t thread) { bool tickleMe; { boost::mutex::scoped_lock lock(m_mutex); tickleMe = scheduleNoLock(dg, thread); } if (tickleMe && Scheduler::getThis() != this) tickle(); }
void Scheduler::schedule(Fiber::ptr f, tid_t thread) { bool tickleMe; { boost::mutex::scoped_lock lock(m_mutex); tickleMe = scheduleNoLock(f, thread); } if (tickleMe && Scheduler::getThis() != this) tickle(); }
void position_jump (cli_infos_t *infos, playlist_positions_t *positions) { xmmsc_result_t *jumpres; int pos; if (playlist_positions_get_single (positions, &pos)) { jumpres = xmmsc_playlist_set_next (infos->sync, pos); xmmsc_result_wait (jumpres); tickle (jumpres, infos); } else { g_printf (_("Cannot jump to several positions!\n")); } cli_infos_loop_resume (infos); }
void Scheduler::stop() { // Already stopped if (m_rootFiber && m_threadCount == 0 && (m_rootFiber->state() == Fiber::TERM || m_rootFiber->state() == Fiber::INIT)) { MORDOR_LOG_VERBOSE(g_log) << this << " stopped"; m_stopping = true; // A derived class may inhibit stopping while it has things to do in // its idle loop, so we can't break early if (stopping()) return; } bool exitOnThisFiber = false; if (m_rootThread != emptytid()) { // A thread-hijacking scheduler must be stopped // from within itself to return control to the // original thread MORDOR_ASSERT(Scheduler::getThis() == this); if (Fiber::getThis() == m_callingFiber) { exitOnThisFiber = true; // First switch to the correct thread MORDOR_LOG_DEBUG(g_log) << this << " switching to root thread to stop"; switchTo(m_rootThread); } if (!m_callingFiber) exitOnThisFiber = true; } else { // A spawned-threads only scheduler cannot be stopped from within // itself... who would get control? MORDOR_ASSERT(Scheduler::getThis() != this); } m_stopping = true; for (size_t i = 0; i < m_threadCount; ++i) tickle(); if (m_rootFiber && (m_threadCount != 0u || Scheduler::getThis() != this)) tickle(); // Wait for all work to stop on this thread if (exitOnThisFiber) { while (!stopping()) { // Give this thread's run fiber a chance to kill itself off MORDOR_LOG_DEBUG(g_log) << this << " yielding to this thread to stop"; yieldTo(true); } } // Wait for other threads to stop if (exitOnThisFiber || Scheduler::getThis() != this) { MORDOR_LOG_DEBUG(g_log) << this << " waiting for other threads to stop"; std::vector<boost::shared_ptr<Thread> > threads; { boost::mutex::scoped_lock lock(m_mutex); threads.swap(m_threads); } for (std::vector<boost::shared_ptr<Thread> >::const_iterator it (threads.begin()); it != threads.end(); ++it) { (*it)->join(); } } MORDOR_LOG_VERBOSE(g_log) << this << " stopped"; }
void Scheduler::run() { t_scheduler = this; if (gettid() != m_rootThread) { // Running in own thread t_fiber = Fiber::getThis().get(); } else { // Hijacked a thread MORDOR_ASSERT(t_fiber.get() == Fiber::getThis().get()); } Fiber::ptr idleFiber(new Fiber(boost::bind(&Scheduler::idle, this))); MORDOR_LOG_VERBOSE(g_log) << this << " starting thread with idle fiber " << idleFiber; Fiber::ptr dgFiber; // use a vector for O(1) .size() std::vector<FiberAndThread> batch(m_batchSize); bool isActive = false; while (true) { batch.clear(); bool dontIdle = false; bool tickleMe = false; { boost::mutex::scoped_lock lock(m_mutex); // Kill ourselves off if needed if (m_threads.size() > m_threadCount && gettid() != m_rootThread) { // Accounting if (isActive) --m_activeThreadCount; // Kill off the idle fiber try { throw boost::enable_current_exception( OperationAbortedException()); } catch(...) { idleFiber->inject(boost::current_exception()); } // Detach our thread for (std::vector<boost::shared_ptr<Thread> > ::iterator it = m_threads.begin(); it != m_threads.end(); ++it) if ((*it)->tid() == gettid()) { m_threads.erase(it); if (m_threads.size() > m_threadCount) tickle(); return; } MORDOR_NOTREACHED(); } std::list<FiberAndThread>::iterator it(m_fibers.begin()); while (it != m_fibers.end()) { // If we've met our batch size, and we're not checking to see // if we need to tickle another thread, then break if ( (tickleMe || m_activeThreadCount == threadCount()) && batch.size() == m_batchSize) break; if (it->thread != emptytid() && it->thread != gettid()) { MORDOR_LOG_DEBUG(g_log) << this << " skipping item scheduled for thread " << it->thread; // Wake up another thread to hopefully service this tickleMe = true; dontIdle = true; ++it; continue; } MORDOR_ASSERT(it->fiber || it->dg); // This fiber is still executing; probably just some race // race condition that it needs to yield on one thread // before running on another thread if (it->fiber && it->fiber->state() == Fiber::EXEC) { MORDOR_LOG_DEBUG(g_log) << this << " skipping executing fiber " << it->fiber; ++it; dontIdle = true; continue; } // We were just checking if there is more work; there is, so // set the flag and don't actually take this piece of work if (batch.size() == m_batchSize) { tickleMe = true; break; } batch.push_back(*it); it = m_fibers.erase(it); if (!isActive) { ++m_activeThreadCount; isActive = true; } } if (batch.empty() && isActive) { --m_activeThreadCount; isActive = false; } } if (tickleMe) tickle(); MORDOR_LOG_DEBUG(g_log) << this << " got " << batch.size() << " fiber/dgs to process (max: " << m_batchSize << ", active: " << isActive << ")"; MORDOR_ASSERT(isActive == !batch.empty()); if (!batch.empty()) { std::vector<FiberAndThread>::iterator it; for (it = batch.begin(); it != batch.end(); ++it) { Fiber::ptr f = it->fiber; boost::function<void ()> dg = it->dg; try { if (f && f->state() != Fiber::TERM) { MORDOR_LOG_DEBUG(g_log) << this << " running " << f; f->yieldTo(); } else if (dg) { if (!dgFiber) dgFiber.reset(new Fiber(dg)); dgFiber->reset(dg); MORDOR_LOG_DEBUG(g_log) << this << " running " << dg; dgFiber->yieldTo(); if (dgFiber->state() != Fiber::TERM) dgFiber.reset(); else dgFiber->reset(NULL); } } catch (...) { MORDOR_LOG_FATAL(Log::root()) << boost::current_exception_diagnostic_information(); throw; } } continue; } if (dontIdle) continue; if (idleFiber->state() == Fiber::TERM) { MORDOR_LOG_DEBUG(g_log) << this << " idle fiber terminated"; if (gettid() == m_rootThread) m_callingFiber.reset(); // Unblock the next thread if (threadCount() > 1) tickle(); return; } MORDOR_LOG_DEBUG(g_log) << this << " idling"; idleFiber->call(); } }
/* Abstract jump, use inc to choose the direction. */ static void list_jump_rel (xmmsc_result_t *res, cli_infos_t *infos, gint inc) { guint i; guint id; xmmsc_result_t *jumpres = NULL; xmmsv_t *val; const gchar *err; gint currpos; gint plsize; GArray *playlist; currpos = infos->cache->currpos; plsize = infos->cache->active_playlist->len; playlist = infos->cache->active_playlist; /* If no currpos, start jump from beginning */ if (currpos < 0) { currpos = 0; } val = xmmsc_result_get_value (res); if (!xmmsv_get_error (val, &err) && xmmsv_is_list (val)) { xmmsv_list_iter_t *it; xmmsv_get_list_iter (val, &it); inc += plsize; /* magic trick so we can loop in either direction */ /* Loop on the playlist */ for (i = (currpos + inc) % plsize; i != currpos; i = (i + inc) % plsize) { /* Loop on the matched media */ for (xmmsv_list_iter_first (it); xmmsv_list_iter_valid (it); xmmsv_list_iter_next (it)) { xmmsv_t *entry; xmmsv_list_iter_entry (it, &entry); /* If both match, jump! */ if (xmmsv_get_uint (entry, &id) && g_array_index (playlist, guint, i) == id) { jumpres = xmmsc_playlist_set_next (infos->sync, i); xmmsc_result_wait (jumpres); tickle (jumpres, infos); goto finish; } } } } finish: /* No matching media found, don't jump */ if (!jumpres) { g_printf (_("No media matching the pattern in the playlist!\n")); cli_infos_loop_resume (infos); } xmmsc_result_unref (res); }