Skip to content
Snippets Groups Projects
sched.cc 4.89 KiB
Newer Older
  • Learn to ignore specific revisions
  • Avi Kivity's avatar
    Avi Kivity committed
    #include "sched.hh"
    #include <list>
    #include "mutex.hh"
    #include <mutex>
    #include "debug.hh"
    
    Avi Kivity's avatar
    Avi Kivity committed
    #include "drivers/clockevent.hh"
    
    #include "irqlock.hh"
    
    #include "drivers/clock.hh"
    
    Avi Kivity's avatar
    Avi Kivity committed
    
    namespace sched {
    
    
    std::vector<cpu*> cpus;
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    thread __thread * s_current;
    
    elf::tls_data tls;
    
    }
    
    #include "arch-switch.hh"
    
    namespace sched {
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    void schedule_force();
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    void cpu::schedule(bool yield)
    
    Avi Kivity's avatar
    Avi Kivity committed
        // FIXME: drive by IPI
        handle_incoming_wakeups();
    
    Avi Kivity's avatar
    Avi Kivity committed
        thread* p = thread::current();
    
    Avi Kivity's avatar
    Avi Kivity committed
        if (!p->_waiting && !yield) {
    
    Avi Kivity's avatar
    Avi Kivity committed
            return;
        }
    
    Avi Kivity's avatar
    Avi Kivity committed
        // FIXME: a proper idle mechanism
        while (runqueue.empty()) {
            barrier();
    
    Avi Kivity's avatar
    Avi Kivity committed
            handle_incoming_wakeups();
    
    Avi Kivity's avatar
    Avi Kivity committed
        }
    
    Avi Kivity's avatar
    Avi Kivity committed
        thread* n = with_lock(irq_lock, [this] {
    
            auto n = &runqueue.front();
    
            runqueue.pop_front();
            return n;
        });
    
    Avi Kivity's avatar
    Avi Kivity committed
        assert(!n->_waiting);
        n->_on_runqueue = false;
    
        if (n != thread::current()) {
            n->switch_to();
        }
    
    Avi Kivity's avatar
    Avi Kivity committed
    void cpu::handle_incoming_wakeups()
    {
        for (unsigned i = 0; i < cpus.size(); ++i) {
            incoming_wakeup_queue q;
            incoming_wakeups[i].copy_and_clear(q);
            while (!q.empty()) {
                runqueue.push_back(q.front());
                q.pop_front_nonatomic();
            }
        }
    }
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    void schedule(bool yield)
    {
        cpu::current()->schedule(yield);
    }
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    void thread::yield()
    {
    
    Avi Kivity's avatar
    Avi Kivity committed
        auto t = current();
        // FIXME: what about other cpus?
        if (t->_cpu->runqueue.empty()) {
    
    Avi Kivity's avatar
    Avi Kivity committed
            return;
        }
    
        t->_cpu->runqueue.push_back(*t);
    
    Avi Kivity's avatar
    Avi Kivity committed
        t->_on_runqueue = true;
        assert(!t->_waiting);
    
    Avi Kivity's avatar
    Avi Kivity committed
        t->_cpu->schedule(true);
    
    thread::stack_info::stack_info(void* _begin, size_t _size)
        : begin(_begin), size(_size)
    {
        auto end = align_down(begin + size, 16);
        size = static_cast<char*>(end) - static_cast<char*>(begin);
    }
    
    
    mutex thread_list_mutex;
    typedef bi::list<thread,
                     bi::member_hook<thread,
                                     bi::list_member_hook<>,
                                     &thread::_thread_list_link>
                    > thread_list_type;
    thread_list_type thread_list;
    
    
    thread::thread(std::function<void ()> func, stack_info stack, bool main)
    
    Avi Kivity's avatar
    Avi Kivity committed
        : _func(func)
    
        , _on_runqueue(!main)
    
    Avi Kivity's avatar
    Avi Kivity committed
        , _waiting(false)
    
        , _terminated(false)
        , _joiner()
    
        with_lock(thread_list_mutex, [this] {
            thread_list.push_back(*this);
        });
    
        setup_tcb();
        init_stack();
    
    Avi Kivity's avatar
    Avi Kivity committed
        if (!main) {
    
            _cpu = current()->tcpu(); // inherit creator's cpu
    
            _cpu->runqueue.push_back(*this);
    
    Avi Kivity's avatar
    Avi Kivity committed
        }
    }
    
    thread::~thread()
    {
    
        with_lock(thread_list_mutex, [this] {
            thread_list.erase(thread_list.iterator_to(*this));
        });
    
    Avi Kivity's avatar
    Avi Kivity committed
        debug("thread dtor");
    }
    
    void thread::prepare_wait()
    {
        _waiting = true;
    }
    
    void thread::wake()
    {
    
        // prevent two concurrent wakeups
        if (!_waiting.exchange(false)) {
            return;
        }
    
    Avi Kivity's avatar
    Avi Kivity committed
        _cpu->incoming_wakeups[cpu::current()->id].push_front(*this);
        // FIXME: IPI
    
    Avi Kivity's avatar
    Avi Kivity committed
    }
    
    void thread::main()
    {
        _func();
    }
    
    thread* thread::current()
    {
        return sched::s_current;
    }
    
    void thread::wait()
    {
        if (!_waiting) {
            return;
        }
        schedule();
    }
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    void thread::sleep_until(u64 abstime)
    {
        timer t(*current());
        t.set(abstime);
        wait_until([&] { return t.expired(); });
    }
    
    
    Avi Kivity's avatar
    Avi Kivity committed
    void thread::stop_wait()
    {
        _waiting = false;
    }
    
    
    void thread::complete()
    {
        _waiting = true;
        _terminated = true;
        if (_joiner) {
            _joiner->wake();
        }
        while (true) {
            schedule();
        }
    }
    
    void thread::join()
    {
        _joiner = current();
        wait_until([this] { return _terminated; });
    }
    
    
    thread::stack_info thread::get_stack_info()
    {
    
    Avi Kivity's avatar
    Avi Kivity committed
    timer_list timers;
    
    timer_list::timer_list()
    {
        clock_event->set_callback(this);
    }
    
    void timer_list::fired()
    {
    
        auto now = clock::get()->time();
        auto i = _list.begin();
        while (i != _list.end() && i->_time < now) {
            auto j = i++;
            j->_expired = true;
            j->_t.wake();
            _list.erase(j);
        }
        if (!_list.empty()) {
            clock_event->set(_list.begin()->_time);
        }
    
    Avi Kivity's avatar
    Avi Kivity committed
    }
    
    timer::timer(thread& t)
        : _t(t)
        , _expired()
    {
    }
    
    timer::~timer()
    {
        cancel();
    }
    
    void timer::set(u64 time)
    {
        _time = time;
        // FIXME: locking
        timers._list.insert(*this);
        if (timers._list.iterator_to(*this) == timers._list.begin()) {
            clock_event->set(time);
        }
    };
    
    void timer::cancel()
    {
        // FIXME: locking
        timers._list.erase(*this);
        _expired = false;
        // even if we remove the first timer, allow it to expire rather than
        // reprogramming the timer
    }
    
    bool timer::expired() const
    {
        return _expired;
    }
    
    bool operator<(const timer& t1, const timer& t2)
    {
        if (t1._time < t2._time) {
            return true;
        } else if (t1._time == t2._time) {
            return &t1 < &t2;
        } else {
            return false;
        }
    }
    
    
    void init(elf::tls_data tls_data, std::function<void ()> cont)
    
        tls = tls_data;
    
        thread::stack_info stack { new char[4096*10], 4096*10 };
        thread t{cont, stack, true};
    
        t.switch_to_first();