about summary refs log tree commit diff
path: root/src/rt/rust_task.h
blob: 42013ca1f5c1396d51dd53dc2802a3768e05a058 (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
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
/*
 *
 */

#ifndef RUST_TASK_H
#define RUST_TASK_H

#include "util/array_list.h"

#include "context.h"
#include "rust_obstack.h"

struct stk_seg {
    unsigned int valgrind_id;
    uintptr_t limit;
    uint8_t data[];
};

struct frame_glue_fns {
    uintptr_t mark_glue_off;
    uintptr_t drop_glue_off;
    uintptr_t reloc_glue_off;
};

// portions of the task structure that are accessible from the standard
// library. This struct must agree with the std::task::rust_task record.
struct rust_task_user {
    rust_task_id id;
    uint32_t notify_enabled;   // this is way more bits than necessary, but it
                               // simplifies the alignment.
    chan_handle notify_chan;
    context ctx;
    uintptr_t rust_sp;         // Saved sp when not running.
};

// std::lib::task::task_result
enum task_result {
    tr_success = 0,
    tr_failure = 1
};

// std::lib::task::task_notification
//
// since it's currently a unary tag, we only add the fields.
struct task_notification {
    rust_task_id id;
    task_result result; // task_result
};

struct
rust_task : public kernel_owned<rust_task>, rust_cond
{
    rust_task_user user;

    RUST_ATOMIC_REFCOUNT();

    // Fields known to the compiler.
    stk_seg *stk;
    uintptr_t runtime_sp;      // Runtime sp while task running.
    void *gc_alloc_chain;      // Linked list of GC allocations.
    rust_scheduler *sched;
    rust_crate_cache *cache;

    // Fields known only to the runtime.
    rust_kernel *kernel;
    const char *const name;
    rust_task_list *state;
    rust_cond *cond;
    const char *cond_name;
    rust_task *supervisor;     // Parent-link for failure propagation.
    int32_t list_index;

    rust_port_id next_port_id;

    // Keeps track of the last time this task yielded.
    timer yield_timer;

    // Rendezvous pointer for receiving data when blocked on a port. If we're
    // trying to read data and no data is available on any incoming channel,
    // we block on the port, and yield control to the scheduler. Since, we
    // were not able to read anything, we remember the location where the
    // result should go in the rendezvous_ptr, and let the sender write to
    // that location before waking us up.
    uintptr_t* rendezvous_ptr;

    // List of tasks waiting for this task to finish.
    array_list<rust_task *> tasks_waiting_to_join;

    // This flag indicates that a worker is either currently running the task
    // or is about to run this task.
    int running_on;
    int pinned_on;

    memory_region local_region;

    class wakeup_callback {
    public:
        virtual void on_wakeup() = 0;
    };

    wakeup_callback *_on_wakeup;

    // Indicates that the task ended in failure
    bool failed;
    bool propagate_failure;

    lock_and_signal lock;

    hash_map<rust_port_id, rust_port *> port_table;

    rust_obstack dynastack;

    // Only a pointer to 'name' is kept, so it must live as long as this task.
    rust_task(rust_scheduler *sched,
              rust_task_list *state,
              rust_task *spawner,
              const char *name);

    ~rust_task();

    void start(uintptr_t spawnee_fn,
               uintptr_t args);
    void start();
    void grow(size_t n_frame_bytes);
    bool running();
    bool blocked();
    bool blocked_on(rust_cond *cond);
    bool dead();

    void *malloc(size_t sz, const char *tag, type_desc *td=0);
    void *realloc(void *data, size_t sz, bool gc_mem=false);
    void free(void *p, bool gc_mem=false);

    void transition(rust_task_list *src, rust_task_list *dst);

    void block(rust_cond *on, const char* name);
    void wakeup(rust_cond *from);
    void die();
    void unblock();

    // Print a backtrace, if the "bt" logging option is on.
    void backtrace();

    // Save callee-saved registers and return to the main loop.
    void yield();

    // Yields for a specified duration of time.
    void yield(size_t time_in_ms);

    // Fail this task (assuming caller-on-stack is different task).
    void kill();

    // Fail self, assuming caller-on-stack is this task.
    void fail();

    // Disconnect from our supervisor.
    void unsupervise();

    // Notify tasks waiting for us that we are about to die.
    void notify_tasks_waiting_to_join();

    frame_glue_fns *get_frame_glue_fns(uintptr_t fp);
    rust_crate_cache * get_crate_cache();

    bool can_schedule(int worker);

    void *calloc(size_t size, const char *tag);

    void pin();
    void pin(int id);
    void unpin();

    void on_wakeup(wakeup_callback *callback);

    rust_port_id register_port(rust_port *port);
    void release_port(rust_port_id id);
    rust_port *get_port_by_id(rust_port_id id);

    // Use this function sparingly. Depending on the ref count is generally
    // not at all safe.
    intptr_t get_ref_count() const { return ref_count; }

    rust_chan *get_chan_by_handle(chan_handle *handle);
};

//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//

#endif /* RUST_TASK_H */