about summary refs log tree commit diff
path: root/src/lib/task.rs
blob: d010afc5217a0be5c72e715e4b943168378adfcf (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
import cast = unsafe::reinterpret_cast;
import comm;
import comm::_chan;
import option::some;
import option::none;
import option = option::t;

native "rust" mod rustrt {
    fn task_sleep(time_in_us: uint);
    fn task_yield();
    fn task_join(t: task_id) -> int;
    fn unsupervise();
    fn pin_task();
    fn unpin_task();
    fn get_task_id() -> task_id;

    type rust_chan;

    fn set_min_stack(stack_size: uint);

    fn new_task() -> task_id;
    fn drop_task(id : task_id);
    fn get_task_pointer(id : task_id) -> *rust_task;
    fn get_task_context(id : task_id) -> *x86_registers;
    fn start_task(id : task_id);
    fn get_task_trampoline() -> u32;

    fn migrate_alloc(alloc : *u8, target : task_id);

    fn leak<@T>(thing : -T);
}

type rust_task = {
    mutable notify_enabled : u8,
    mutable notify_chan : _chan<task_notification>
};

type task = int;
type task_id = task;

fn get_task_id() -> task_id {
    rustrt::get_task_id()
}

/**
 * Hints the scheduler to yield this task for a specified ammount of time.
 *
 * arg: time_in_us maximum number of microseconds to yield control for
 */
fn sleep(time_in_us: uint) { ret rustrt::task_sleep(time_in_us); }

fn yield() { ret rustrt::task_yield(); }

tag task_result { tr_success; tr_failure; }

tag task_notification {
    exit(task, task_result);
}

fn join(t: task) -> task_result {
    join_id(cast(t))
}

fn join_id(t : task_id) -> task_result {
    alt rustrt::task_join(t) { 0 { tr_success } _ { tr_failure } }
}

fn unsupervise() { ret rustrt::unsupervise(); }

fn pin() { rustrt::pin_task(); }

fn unpin() { rustrt::unpin_task(); }

fn set_min_stack(stack_size : uint) {
    rustrt::set_min_stack(stack_size);
}

fn _spawn(thunk : -fn() -> ()) -> task {
    spawn(thunk)
}

fn spawn(thunk : -fn() -> ()) -> task {
    spawn_inner(thunk, none)
}

fn spawn_notify(thunk : -fn() -> (), notify : _chan<task_notification>)
    -> task {
    spawn_inner(thunk, some(notify))
}

// FIXME: make this a fn~ once those are supported.
fn spawn_inner(thunk : -fn() -> (),
               notify : option<_chan<task_notification>>)
    -> task_id {
    let id = rustrt::new_task();

    // the order of arguments are outptr, taskptr, envptr.

    // In LLVM fastcall puts the first two in ecx, edx, and the rest on the
    // stack.
    let regs = rustrt::get_task_context(id);

    // set up the task pointer
    let task_ptr = rustrt::get_task_pointer(id);
    (*regs).edx = cast(task_ptr);

    let raw_thunk : { code: u32, env: u32 } = cast(thunk);
    (*regs).eip = raw_thunk.code;

    // set up notifications if they are enabled.
    alt notify {
      some(c) {
        (*task_ptr).notify_enabled = 1u8;
        (*task_ptr).notify_chan = c;
      }
      none {}
    };

    // okay, now we align the stack and add the environment pointer and a fake
    // return address.

    // -12 for the taskm output location, the env pointer
    // -4 for the return address.
    (*regs).esp = align_down((*regs).esp - 12u32) - 4u32;

    let ra : *mutable u32 = cast((*regs).esp);
    let env : *mutable u32 = cast((*regs).esp+4u32);
    let tptr : *mutable u32 = cast((*regs).esp+12u32);

    // put the return pointer in ecx.
    (*regs).ecx = (*regs).esp + 8u32;

    *tptr = cast(task_ptr);
    *env = raw_thunk.env;
    *ra = rustrt::get_task_trampoline();

    rustrt::migrate_alloc(cast(raw_thunk.env), id);
    rustrt::start_task(id);

    rustrt::leak(thunk);

    // Drop twice because get_task_context and get_task_pounter both bump the
    // ref count and expect us to free it.
    rustrt::drop_task(id);
    rustrt::drop_task(id);

    ret id;
}

// Who says we can't write an operating system in Rust?
type x86_registers = {
    // This needs to match the structure in context.h
    mutable eax : u32,
    mutable ebx : u32,
    mutable ecx : u32,
    mutable edx : u32,
    mutable ebp : u32,
    mutable esi : u32,
    mutable edi : u32,
    mutable esp : u32,

    mutable cs : u16,
    mutable ds : u16,
    mutable ss : u16,
    mutable es : u16,
    mutable fs : u16,
    mutable gs : u16,

    mutable eflags : u32,
    mutable eip : u32
};

fn align_down(x : u32) -> u32 {
    // Aligns x down to 16 bytes
    x & !(15u32)
}

// Local Variables:
// mode: rust;
// 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: