summaryrefslogtreecommitdiffstats
path: root/src/common/thread.h
blob: eaf1ba00ccd6efc6c86d81e44cdb47213198ca7f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

// Don't include common.h here as it will break LogManager
#include "common/common_types.h"
#include <cstdio>
#include <cstring>
#include <thread>
#include <condition_variable>
#include <mutex>

// This may not be defined outside _WIN32
#ifndef _WIN32
#ifndef INFINITE
#define INFINITE 0xffffffff
#endif

//for gettimeofday and struct time(spec|val)
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#endif

namespace Common
{

int CurrentThreadId();

void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
void SetCurrentThreadAffinity(u32 mask);

class Event
{
public:
    Event()
        : is_set(false)
    {}

    void Set()
    {
        std::lock_guard<std::mutex> lk(m_mutex);
        if (!is_set)
        {
            is_set = true;
            m_condvar.notify_one();
        }
    }

    void Wait()
    {
        std::unique_lock<std::mutex> lk(m_mutex);
        m_condvar.wait(lk, IsSet(this));
        is_set = false;
    }

    void Reset()
    {
        std::unique_lock<std::mutex> lk(m_mutex);
        // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration
        is_set = false;
    }

private:
    class IsSet
    {
    public:
        IsSet(const Event* ev)
            : m_event(ev)
        {}

        bool operator()()
        {
            return m_event->is_set;
        }

    private:
        const Event* const m_event;
    };

    volatile bool is_set;
    std::condition_variable m_condvar;
    std::mutex m_mutex;
};

// TODO: doesn't work on windows with (count > 2)
class Barrier
{
public:
    Barrier(size_t count)
        : m_count(count), m_waiting(0)
    {}

    // block until "count" threads call Sync()
    bool Sync()
    {
        std::unique_lock<std::mutex> lk(m_mutex);

        // TODO: broken when next round of Sync()s
        // is entered before all waiting threads return from the notify_all

        if (m_count == ++m_waiting)
        {
            m_waiting = 0;
            m_condvar.notify_all();
            return true;
        }
        else
        {
            m_condvar.wait(lk, IsDoneWating(this));
            return false;
        }
    }

private:
    class IsDoneWating
    {
    public:
        IsDoneWating(const Barrier* bar)
            : m_bar(bar)
        {}

        bool operator()()
        {
            return (0 == m_bar->m_waiting);
        }

    private:
        const Barrier* const m_bar;
    };

    std::condition_variable m_condvar;
    std::mutex m_mutex;
    const size_t m_count;
    volatile size_t m_waiting;
};

void SleepCurrentThread(int ms);
void SwitchCurrentThread();    // On Linux, this is equal to sleep 1ms

// Use this function during a spin-wait to make the current thread
// relax while another thread is working. This may be more efficient
// than using events because event functions use kernel calls.
inline void YieldCPU()
{
    std::this_thread::yield();
}

void SetCurrentThreadName(const char *name);

} // namespace Common