medfall

A super great game engine
Log | Files | Refs

context_base.hpp (8701B)


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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*  Relacy Race Detector
 *  Copyright (c) 2008-2010, Dmitry S. Vyukov
 *  All rights reserved.
 *  This software is provided AS-IS with no warranty, either express or implied.
 *  This software is distributed under a license and may not be copied,
 *  modified or distributed except as expressly authorized under the
 *  terms of the license contained in the file LICENSE.TXT in this distribution.
 */

#ifndef RL_CONTEXT_BASE_HPP
#define RL_CONTEXT_BASE_HPP
#ifdef _MSC_VER
#   pragma once
#endif

#include "base.hpp"
#include "history.hpp"
#include "memory.hpp"
#include "test_result.hpp"
#include "slab_allocator.hpp"
#include "test_params.hpp"
#include "random.hpp"
#include "foreach.hpp"
#include "thread_base.hpp"
#include "context_addr_hash.hpp"


#ifdef RL_DEBUGBREAK_ON_ASSERT
#    ifdef _MSC_VER
#        define RL_DEBUGBREAK_ON_ASSERT_IMPL {if (IsDebuggerPresent()) __debugbreak();}
#    else
#        define RL_DEBUGBREAK_ON_ASSERT_IMPL {__asm("int3");}
#    endif
#else
#   define RL_DEBUGBREAK_ON_ASSERT_IMPL
#endif

#ifdef RL_DEBUGBREAK_ON_FAILURE
#    ifdef _MSC_VER
#        define RL_DEBUGBREAK_ON_FAILURE_IMPL {if (IsDebuggerPresent()) __debugbreak();}
#    else
#        define RL_DEBUGBREAK_ON_FAILURE_IMPL {__asm("int3");}
#    endif
#else
#   define RL_DEBUGBREAK_ON_FAILURE_IMPL
#endif



namespace rl
{

class thread_info_base;

struct atomic_data {};
struct var_data
{
    virtual void init(thread_info_base& th) = 0;
    virtual bool store(thread_info_base& th) = 0;
    virtual bool load(thread_info_base& th) = 0;
    virtual ~var_data() {} // just to calm down gcc
};

struct generic_mutex_data;
struct condvar_data;
struct sema_data;
struct event_data;


struct user_msg_event
{
    string msg_;

    void output(std::ostream& s) const
    {
        s << msg_;
    }            
};

class context;

template<int fake = 0>
struct context_holder
{
    static context* instance_;

    static long volatile ctx_seq;
};

template<int fake>
long volatile context_holder<fake>::ctx_seq = 0;

class context
    : public thread_local_context_iface
    , public context_addr_hash_iface
    , nocopy<>
{
public:
    static context& instance()
    {
        //!!! disabled for check in operator new RL_VERIFY(context_holder<>::instance_);
        return *context_holder<>::instance_;
    }

    virtual atomic_data* atomic_ctor(void* ctx) = 0;
    virtual void atomic_dtor(atomic_data* data) = 0;

    virtual var_data* var_ctor() = 0;
    virtual void var_dtor(var_data* data) = 0;

    virtual generic_mutex_data* mutex_ctor(bool is_rw, bool is_exclusive_recursive, bool is_shared_recursive, bool failing_try_lock) = 0;
    virtual void mutex_dtor(generic_mutex_data* m) = 0;

    virtual condvar_data* condvar_ctor(bool allow_spurious_wakeups) = 0;
    virtual void condvar_dtor(condvar_data* cv) = 0;

    virtual sema_data* sema_ctor(bool spurious_wakeups, unsigned initial_count, unsigned max_count) = 0;
    virtual void sema_dtor(sema_data* cv) = 0;

    virtual event_data* event_ctor(bool manual_reset, bool initial_state) = 0;
    virtual void event_dtor(event_data* cv) = 0;

    virtual void rl_global_fence() = 0;
    virtual void sched() = 0;
    virtual void yield(unsigned count, debug_info_param info) = 0;
    virtual void fail_test(char const* desc, test_result_e res, debug_info_param info) = 0;
    virtual void rl_until(char const* desc, debug_info_param info) = 0;

    virtual void* alloc(size_t size, bool is_array, debug_info_param info) = 0;
#ifdef RL_GC
    virtual void* alloc(size_t size, bool is_array, void(*dtor)(void*), debug_info_param info) = 0;
#endif
    virtual void free(void* p, bool is_array, debug_info_param info) = 0;

    virtual void* alloc(size_t size) = 0;
    virtual void free(void* p) = 0;
    virtual size_t prev_alloc_size() = 0;
    virtual void set_debug_info(debug_info_param info) = 0;

    virtual void fiber_proc_impl(int thread_index) = 0;

    virtual unpark_reason park_current_thread(bool is_timed,
                                              bool allow_spurious_wakeup,
                                              bool do_switch,
                                              debug_info_param info) = 0;
    virtual void unpark_thread(thread_id_t th, bool do_switch, debug_info_param info) = 0;
    virtual void switch_back(debug_info_param info) = 0;

    virtual void atomic_thread_fence_acquire() = 0;
    virtual void atomic_thread_fence_release() = 0;
    virtual void atomic_thread_fence_acq_rel() = 0;
    virtual void atomic_thread_fence_seq_cst() = 0;

    virtual unsigned rand(unsigned limit, sched_type t) = 0;

    virtual win_waitable_object* create_thread(void*(*fn)(void*), void* ctx) = 0;

    virtual unpark_reason wfmo_park(void** ws,
                                    win_waitable_object** wo,
                                    size_t count,
                                    bool wait_all,
                                    bool is_timed,
                                    debug_info_param info) = 0;
	
    int get_errno();
    void set_errno(int value);

    thread_info_base* threadx_;
    timestamp_t* seq_cst_fence_order_;

    bool invariant_executing;

    RL_INLINE bool collecting_history() const
    {
        return params_.collect_history && false == invariant_executing;
    }

    template<typename event_t>
    void exec_log(debug_info_param info, event_t const& ev);

    void exec_log_msg(debug_info_param info, char const* msg)
    {
        user_msg_event ev = {msg};
        exec_log(info, ev);
    }

    bool is_random_sched() const
    {
        return is_random_sched_;
    }

    unsigned get_ctx_seq() const
    {
        return ctx_seq_;
    }

    void disable_preemption();
    void enable_preemption();

    virtual thread_id_t get_thread_count() const = 0;

    thread_id_t current_thread() const
    {
        return threadx_->index_;
    }

    void iteration_begin()
    {
    }

protected:
    history_mgr history_;
    test_params& params_;
    unsigned disable_preemption_;
    int                         disable_alloc_;

    context(thread_id_t thread_count, test_params& params)
        : history_(*params.output_stream, thread_count)
        , params_(params)
        , disable_alloc_(1)
    {
        RL_VERIFY(0 == context_holder<>::instance_);
        context_holder<>::instance_ = this;

        is_random_sched_ = params_.search_type == random_scheduler_type;

#ifdef _MSC_VER
        ctx_seq_ = _InterlockedExchangeAdd(&context_holder<>::ctx_seq, 1) + 1;
#else
        ctx_seq_ = __sync_fetch_and_add(&context_holder<>::ctx_seq, 1) + 1;
#endif
    }

    virtual ~context()
    {
        RL_VERIFY(this == context_holder<>::instance_);
        context_holder<>::instance_ = 0;
    }
    
private:
    bool is_random_sched_;
    unsigned ctx_seq_;
};


template<int fake>
context* context_holder<fake>::instance_ = 0;




inline context& ctx()
{
    return context::instance();
}


inline int get_errno()
{
    return ctx().get_errno();
}

inline void set_errno(int value)
{
    return ctx().set_errno(value);
}

class preemption_disabler : nocopy<>
{
public:
    preemption_disabler(context& c)
        : c_(c)
    {
        c_.disable_preemption();
    }

    ~preemption_disabler()
    {
        c_.enable_preemption();
    }

private:
    context& c_;
};


}


#define RL_HIST_IMPL(C, INFO, TYPE) \
    do { \
        if (C.collecting_history()) { \
            rl::debug_info const& rl_info_c = INFO; \
            rl::context& rl_hist_c = C; \
            TYPE ev = \
/**/

#define RL_HIST_END() \
                        ; \
            rl_hist_c.exec_log(rl_info_c, ev); \
        } \
    } while ((void)0, 0) \
/**/

#define RL_HIST_CTX(TYPE) RL_HIST_IMPL((*this), info, TYPE)

#define RL_HIST(TYPE) RL_HIST_IMPL(c, info, TYPE)

#define RL_LOG(desc) rl::ctx().exec_log_msg(RL_INFO, desc)



#ifdef _MSC_VER
#   define RL_ASSERT_IMPL(x, res, str, info) do {if (!((void)0, (x))) {{RL_DEBUGBREAK_ON_ASSERT_IMPL} rl::ctx().fail_test(str, res, info);}} while ((void)0, 0)
#else
#   define RL_ASSERT_IMPL(x, res, str, info) do {if (!((void)0, (x))) rl::ctx().fail_test(str, res, info);} while ((void)0, 0)
#endif
#define RL_ASSERT(x) RL_ASSERT_IMPL(x, rl::test_result_user_assert_failed, "assertion: " #x, RL_INFO)
#define RL_UNTIL(x) do {if ((x)) rl::ctx().rl_until(#x, RL_INFO);} while ((void)0, 0)


#endif