summaryrefslogtreecommitdiff
path: root/src/greenlet/greenlet_thread_support.hpp
blob: 747ae4773286353d22f02aa2eddd174404b2204d (plain)
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
#ifndef GREENLET_THREAD_SUPPORT_HPP
#define GREENLET_THREAD_SUPPORT_HPP

/**
 * Defines various utility functions to help greenlet integrate well
 * with threads. When possible, we use portable C++ 11 threading; when
 * not possible, we will use platform specific APIs if needed and
 * available. (Currently, this is only for Python 2.7 on Windows.)
 */

#include <stdexcept>
#include "greenlet_compiler_compat.hpp"

// Allow setting this to 0 on the command line so that we
// can test these code paths on compilers that otherwise support
// standard threads.
#ifndef G_USE_STANDARD_THREADING
#if __cplusplus >= 201103
// Cool. We should have standard support
#    define G_USE_STANDARD_THREADING 1
#elif defined(_MSC_VER)
// MSVC doesn't use a modern version of __cplusplus automatically, you
// have to opt-in to update it with /Zc:__cplusplus, but that's not
// available on our old version of visual studio for Python 2.7
#    if _MSC_VER <= 1500
// Python 2.7 on Windows. Use the Python thread state and native Win32 APIs.
#        define G_USE_STANDARD_THREADING 0
#    else
// Assume we have a compiler that supports it. The Appveyor compilers
// we use all do have standard support
#        define G_USE_STANDARD_THREADING 1
#    endif
#elif defined(__GNUC__) || defined(__clang__)
// All tested versions either do, or can with the right --std argument, support what we need
#    define G_USE_STANDARD_THREADING 1
#else
#    define G_USE_STANDARD_THREADING 0
#endif
#endif /* G_USE_STANDARD_THREADING */

namespace greenlet {
    class LockInitError : public std::runtime_error
    {
    public:
        LockInitError(const char* what) : std::runtime_error(what)
        {};
    };
};


#if G_USE_STANDARD_THREADING == 1
#    define G_THREAD_LOCAL_SUPPORTS_DESTRUCTOR 1
#    include <thread>
#    include <mutex>
#    define G_THREAD_LOCAL_VAR thread_local
namespace greenlet {
    typedef std::mutex Mutex;
    typedef std::lock_guard<Mutex> LockGuard;
};
#else
// NOTE: At this writing, the mutex isn't currently required;
// we don't use a shared cleanup queue or Py_AddPendingCall in this
// model, we rely on the thread state dictionary for cleanup.
#    if defined(_MSC_VER)
//       We should only hit this case for Python 2.7 on Windows.
#        define G_THREAD_LOCAL_VAR __declspec(thread)
#        include <windows.h>
namespace greenlet {
    class Mutex
    {
        CRITICAL_SECTION _mutex;
        G_NO_COPIES_OF_CLS(Mutex);
    public:
        Mutex()
        {
            InitializeCriticalSection(&this->_mutex);
        };

        void Lock()
        {
            EnterCriticalSection(&this->_mutex);
        };

        void UnLock()
        {
            LeaveCriticalSection(&this->_mutex);
        };
    };
};
#    elif (defined(__GNUC__) || defined(__clang__)) || (defined(__SUNPRO_C))
// GCC, clang, SunStudio all use __thread for thread-local variables.
// For locks, we can use PyThread APIs, officially added in 3.2, but
// present back to 2.7
#        define G_THREAD_LOCAL_VAR __thread
#        include "pythread.h"
namespace greenlet {
    class Mutex
    {
        PyThread_type_lock _mutex;
        G_NO_COPIES_OF_CLS(Mutex);
    public:
        Mutex()
        {
            this->_mutex = PyThread_allocate_lock();
            if (!this->_mutex) {
                throw LockInitError("Failed to initialize mutex.");
            }
        };

        void Lock()
        {
            PyThread_acquire_lock(this->_mutex, WAIT_LOCK);
        };

        void UnLock()
        {
            PyThread_release_lock(this->_mutex);
        };
    };
};
#    else
#        error Unable to declare thread-local variables.
#    endif
// the RAII lock keeper for all non-standard threading platforms.
namespace greenlet {
    class LockGuard
    {
        Mutex& _mutex;
        G_NO_COPIES_OF_CLS(LockGuard);
    public:
        LockGuard(Mutex& m) : _mutex(m)
        {
            this->_mutex.Lock();
        };
        ~LockGuard()
        {
            this->_mutex.UnLock();
        };
    };

};
#endif /* G_USE_STANDARD_THREADING == 1 */

#endif /* GREENLET_THREAD_SUPPORT_HPP */