]> git.lizzy.rs Git - rust.git/blob - src/rt/rust_builtin.cpp
4f951a7faf0af67daf40eac0bf29ba8829f27074
[rust.git] / src / rt / rust_builtin.cpp
1 /* Native builtins. */
2
3 #include "rust_internal.h"
4 #include "rust_scheduler.h"
5 #include "rust_task.h"
6
7 #if !defined(__WIN32__)
8 #include <sys/time.h>
9 #endif
10
11 extern "C" CDECL rust_str*
12 last_os_error() {
13     rust_task *task = rust_scheduler::get_task();
14
15     LOG(task, task, "last_os_error()");
16
17 #if defined(__WIN32__)
18     LPTSTR buf;
19     DWORD err = GetLastError();
20     DWORD res = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
21                               FORMAT_MESSAGE_FROM_SYSTEM |
22                               FORMAT_MESSAGE_IGNORE_INSERTS,
23                               NULL, err,
24                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
25                               (LPTSTR) &buf, 0, NULL);
26     if (!res) {
27         task->fail();
28         return NULL;
29     }
30 #elif defined(_GNU_SOURCE)
31     char cbuf[BUF_BYTES];
32     char *buf = strerror_r(errno, cbuf, sizeof(cbuf));
33     if (!buf) {
34         task->fail();
35         return NULL;
36     }
37 #else
38     char buf[BUF_BYTES];
39     int err = strerror_r(errno, buf, sizeof(buf));
40     if (err) {
41         task->fail();
42         return NULL;
43     }
44 #endif
45
46     rust_str * st = make_str(task->kernel, buf, strlen(buf),
47                              "last_os_error");
48 #ifdef __WIN32__
49     LocalFree((HLOCAL)buf);
50 #endif
51     return st;
52 }
53
54 extern "C" CDECL rust_str *
55 rust_getcwd() {
56     rust_task *task = rust_scheduler::get_task();
57     LOG(task, task, "rust_getcwd()");
58
59     char cbuf[BUF_BYTES];
60
61 #if defined(__WIN32__)
62     if (!_getcwd(cbuf, sizeof(cbuf))) {
63 #else
64         if (!getcwd(cbuf, sizeof(cbuf))) {
65 #endif
66         task->fail();
67         return NULL;
68     }
69
70     return make_str(task->kernel, cbuf, strlen(cbuf), "rust_str(getcwd");
71 }
72
73 // TODO: Allow calling native functions that return double results.
74 extern "C" CDECL
75 void squareroot(double *input, double *output) {
76     *output = sqrt(*input);
77 }
78
79 extern "C" CDECL void
80 leak(void *thing) {
81     // Do nothing. Call this with move-mode in order to say "Don't worry rust,
82     // I'll take care of this."
83 }
84
85 extern "C" CDECL intptr_t
86 refcount(intptr_t *v) {
87     // Passed-in value has refcount 1 too high
88     // because it was ref'ed while making the call.
89     return (*v) - 1;
90 }
91
92 extern "C" CDECL void
93 do_gc() {
94     // TODO
95 }
96
97 extern "C" CDECL void
98 unsupervise() {
99     rust_task *task = rust_scheduler::get_task();
100     task->unsupervise();
101 }
102
103 extern "C" CDECL void
104 vec_reserve_shared(type_desc* ty, rust_vec** vp,
105                    size_t n_elts) {
106     rust_task *task = rust_scheduler::get_task();
107     reserve_vec(task, vp, n_elts * ty->size);
108 }
109
110 /**
111  * Copies elements in an unsafe buffer to the given interior vector. The
112  * vector must have size zero.
113  */
114 extern "C" CDECL rust_vec*
115 vec_from_buf_shared(type_desc *ty, void *ptr, size_t count) {
116     rust_task *task = rust_scheduler::get_task();
117     size_t fill = ty->size * count;
118     rust_vec* v = (rust_vec*)task->kernel->malloc(fill + sizeof(rust_vec),
119                                                     "vec_from_buf");
120     v->fill = v->alloc = fill;
121     memmove(&v->data[0], ptr, fill);
122     return v;
123 }
124
125 extern "C" CDECL void
126 rust_str_push(rust_vec** sp, uint8_t byte) {
127     rust_task *task = rust_scheduler::get_task();
128     size_t fill = (*sp)->fill;
129     reserve_vec(task, sp, fill + 1);
130     (*sp)->data[fill-1] = byte;
131     (*sp)->data[fill] = 0;
132     (*sp)->fill = fill + 1;
133 }
134
135 extern "C" CDECL void *
136 rand_new() {
137     rust_task *task = rust_scheduler::get_task();
138     rust_scheduler *sched = task->sched;
139     randctx *rctx = (randctx *) task->malloc(sizeof(randctx), "randctx");
140     if (!rctx) {
141         task->fail();
142         return NULL;
143     }
144     isaac_init(sched, rctx);
145     return rctx;
146 }
147
148 extern "C" CDECL size_t
149 rand_next(randctx *rctx) {
150     return isaac_rand(rctx);
151 }
152
153 extern "C" CDECL void
154 rand_free(randctx *rctx) {
155     rust_task *task = rust_scheduler::get_task();
156     task->free(rctx);
157 }
158
159 /* Debug builtins for std::dbg. */
160
161 static void
162 debug_tydesc_helper(type_desc *t)
163 {
164     rust_task *task = rust_scheduler::get_task();
165     LOG(task, stdlib, "  size %" PRIdPTR ", align %" PRIdPTR
166         ", first_param 0x%" PRIxPTR,
167         t->size, t->align, t->first_param);
168 }
169
170 extern "C" CDECL void
171 debug_tydesc(type_desc *t) {
172     rust_task *task = rust_scheduler::get_task();
173     LOG(task, stdlib, "debug_tydesc");
174     debug_tydesc_helper(t);
175 }
176
177 extern "C" CDECL void
178 debug_opaque(type_desc *t, uint8_t *front) {
179     rust_task *task = rust_scheduler::get_task();
180     LOG(task, stdlib, "debug_opaque");
181     debug_tydesc_helper(t);
182     // FIXME may want to actually account for alignment.  `front` may not
183     // indeed be the front byte of the passed-in argument.
184     for (uintptr_t i = 0; i < t->size; ++front, ++i) {
185         LOG(task, stdlib, "  byte %" PRIdPTR ": 0x%" PRIx8, i, *front);
186     }
187 }
188
189 struct rust_box {
190     RUST_REFCOUNTED(rust_box)
191
192     // FIXME `data` could be aligned differently from the actual box body data
193     uint8_t data[];
194 };
195
196 extern "C" CDECL void
197 debug_box(type_desc *t, rust_box *box) {
198     rust_task *task = rust_scheduler::get_task();
199     LOG(task, stdlib, "debug_box(0x%" PRIxPTR ")", box);
200     debug_tydesc_helper(t);
201     LOG(task, stdlib, "  refcount %" PRIdPTR,
202         box->ref_count - 1);  // -1 because we ref'ed for this call
203     for (uintptr_t i = 0; i < t->size; ++i) {
204         LOG(task, stdlib, "  byte %" PRIdPTR ": 0x%" PRIx8, i, box->data[i]);
205     }
206 }
207
208 struct rust_tag {
209     uintptr_t discriminant;
210     uint8_t variant[];
211 };
212
213 extern "C" CDECL void
214 debug_tag(type_desc *t, rust_tag *tag) {
215     rust_task *task = rust_scheduler::get_task();
216
217     LOG(task, stdlib, "debug_tag");
218     debug_tydesc_helper(t);
219     LOG(task, stdlib, "  discriminant %" PRIdPTR, tag->discriminant);
220
221     for (uintptr_t i = 0; i < t->size - sizeof(tag->discriminant); ++i)
222         LOG(task, stdlib, "  byte %" PRIdPTR ": 0x%" PRIx8, i,
223             tag->variant[i]);
224 }
225
226 struct rust_obj {
227     uintptr_t *vtbl;
228     rust_box *body;
229 };
230
231 extern "C" CDECL void
232 debug_obj(type_desc *t, rust_obj *obj, size_t nmethods, size_t nbytes) {
233     rust_task *task = rust_scheduler::get_task();
234
235     LOG(task, stdlib, "debug_obj with %" PRIdPTR " methods", nmethods);
236     debug_tydesc_helper(t);
237     LOG(task, stdlib, "  vtbl at 0x%" PRIxPTR, obj->vtbl);
238     LOG(task, stdlib, "  body at 0x%" PRIxPTR, obj->body);
239
240     for (uintptr_t *p = obj->vtbl; p < obj->vtbl + nmethods; ++p)
241         LOG(task, stdlib, "  vtbl word: 0x%" PRIxPTR, *p);
242
243     for (uintptr_t i = 0; i < nbytes; ++i)
244         LOG(task, stdlib, "  body byte %" PRIdPTR ": 0x%" PRIxPTR,
245             i, obj->body->data[i]);
246 }
247
248 struct rust_fn {
249     uintptr_t *thunk;
250     rust_box *closure;
251 };
252
253 extern "C" CDECL void
254 debug_fn(type_desc *t, rust_fn *fn) {
255     rust_task *task = rust_scheduler::get_task();
256     LOG(task, stdlib, "debug_fn");
257     debug_tydesc_helper(t);
258     LOG(task, stdlib, "  thunk at 0x%" PRIxPTR, fn->thunk);
259     LOG(task, stdlib, "  closure at 0x%" PRIxPTR, fn->closure);
260     if (fn->closure) {
261         LOG(task, stdlib, "    refcount %" PRIdPTR, fn->closure->ref_count);
262     }
263 }
264
265 extern "C" CDECL void *
266 debug_ptrcast(type_desc *from_ty,
267               type_desc *to_ty,
268               void *ptr) {
269     rust_task *task = rust_scheduler::get_task();
270     LOG(task, stdlib, "debug_ptrcast from");
271     debug_tydesc_helper(from_ty);
272     LOG(task, stdlib, "to");
273     debug_tydesc_helper(to_ty);
274     return ptr;
275 }
276
277 extern "C" CDECL void *
278 debug_get_stk_seg() {
279     rust_task *task = rust_scheduler::get_task();
280     return task->stk;
281 }
282
283 extern "C" CDECL rust_vec*
284 rust_list_files(rust_str *path) {
285     rust_task *task = rust_scheduler::get_task();
286     array_list<rust_str*> strings;
287 #if defined(__WIN32__)
288     WIN32_FIND_DATA FindFileData;
289     HANDLE hFind = FindFirstFile((char*)path->data, &FindFileData);
290     if (hFind != INVALID_HANDLE_VALUE) {
291         do {
292             rust_str *str = make_str(task->kernel, FindFileData.cFileName,
293                                      strlen(FindFileData.cFileName),
294                                      "list_files_str");
295             strings.push(str);
296         } while (FindNextFile(hFind, &FindFileData));
297         FindClose(hFind);
298     }
299 #else
300     DIR *dirp = opendir((char*)path->data);
301   if (dirp) {
302       struct dirent *dp;
303       while ((dp = readdir(dirp))) {
304           rust_vec *str = make_str(task->kernel, dp->d_name,
305                                     strlen(dp->d_name),
306                                     "list_files_str");
307           strings.push(str);
308       }
309       closedir(dirp);
310   }
311 #endif
312
313   rust_vec *vec = (rust_vec *)
314       task->kernel->malloc(vec_size<rust_vec*>(strings.size()),
315                            "list_files_vec");
316   size_t alloc_sz = sizeof(rust_vec*) * strings.size();
317   vec->fill = vec->alloc = alloc_sz;
318   memcpy(&vec->data[0], strings.data(), alloc_sz);
319   return vec;
320 }
321
322 extern "C" CDECL int
323 rust_path_is_dir(char *path) {
324     struct stat buf;
325     if (stat(path, &buf)) {
326         return 0;
327     }
328     return S_ISDIR(buf.st_mode);
329 }
330
331 extern "C" CDECL int
332 rust_path_exists(char *path) {
333     struct stat buf;
334     if (stat(path, &buf)) {
335         return 0;
336     }
337     return 1;
338 }
339
340 extern "C" CDECL FILE* rust_get_stdin() {return stdin;}
341 extern "C" CDECL FILE* rust_get_stdout() {return stdout;}
342 extern "C" CDECL FILE* rust_get_stderr() {return stderr;}
343
344 extern "C" CDECL int
345 rust_ptr_eq(type_desc *t, rust_box *a, rust_box *b) {
346     return a == b;
347 }
348
349 #if defined(__WIN32__)
350 extern "C" CDECL void
351 get_time(uint32_t *sec, uint32_t *usec) {
352     rust_task *task = rust_scheduler::get_task();
353     SYSTEMTIME systemTime;
354     FILETIME fileTime;
355     GetSystemTime(&systemTime);
356     if (!SystemTimeToFileTime(&systemTime, &fileTime)) {
357         task->fail();
358         return;
359     }
360
361     // FIXME: This is probably completely wrong.
362     *sec = fileTime.dwHighDateTime;
363     *usec = fileTime.dwLowDateTime;
364 }
365 #else
366 extern "C" CDECL void
367 get_time(uint32_t *sec, uint32_t *usec) {
368     struct timeval tv;
369     gettimeofday(&tv, NULL);
370     *sec = tv.tv_sec;
371     *usec = tv.tv_usec;
372 }
373 #endif
374
375 extern "C" CDECL void
376 nano_time(uint64_t *ns) {
377     timer t;
378     *ns = t.time_ns();
379 }
380
381 extern "C" CDECL void
382 pin_task() {
383     rust_task *task = rust_scheduler::get_task();
384     task->pin();
385 }
386
387 extern "C" CDECL void
388 unpin_task() {
389     rust_task *task = rust_scheduler::get_task();
390     task->unpin();
391 }
392
393 extern "C" CDECL rust_task_id
394 get_task_id() {
395     rust_task *task = rust_scheduler::get_task();
396     return task->user.id;
397 }
398
399 extern "C" CDECL rust_task_id
400 new_task() {
401     rust_task *task = rust_scheduler::get_task();
402     return task->kernel->create_task(task, NULL);
403 }
404
405 extern "C" CDECL void
406 drop_task(rust_task *target) {
407     if(target) {
408         target->deref();
409     }
410 }
411
412 extern "C" CDECL rust_task *
413 get_task_pointer(rust_task_id id) {
414     rust_task *task = rust_scheduler::get_task();
415     return task->kernel->get_task_by_id(id);
416 }
417
418 extern "C" rust_task *
419 rust_get_task() {
420     return rust_scheduler::get_task();
421 }
422
423 struct fn_env_pair {
424     spawn_fn f;
425     rust_boxed_closure *env;
426 };
427
428 extern "C" CDECL void
429 start_task(rust_task_id id, fn_env_pair *f) {
430     rust_task *task = rust_scheduler::get_task();
431     rust_task *target = task->kernel->get_task_by_id(id);
432     target->start(f->f, f->env, NULL);
433     target->deref();
434 }
435
436 extern "C" CDECL void
437 migrate_alloc(void *alloc, rust_task_id tid) {
438     rust_task *task = rust_scheduler::get_task();
439     if(!alloc) return;
440     rust_task *target = task->kernel->get_task_by_id(tid);
441     if(target) {
442         const type_desc *tydesc = task->release_alloc(alloc);
443         target->claim_alloc(alloc, tydesc);
444         target->deref();
445     }
446     else {
447         // We couldn't find the target. Maybe we should just free?
448         task->fail();
449     }
450 }
451
452 // defined in rust_task.cpp
453 extern size_t g_custom_min_stack_size;
454 extern "C" CDECL void
455 set_min_stack(uintptr_t stack_size) {
456     g_custom_min_stack_size = stack_size;
457 }
458
459 extern "C" CDECL int
460 sched_threads() {
461     rust_task *task = rust_scheduler::get_task();
462     return task->kernel->num_threads;
463 }
464
465 extern "C" CDECL rust_port*
466 new_port(size_t unit_sz) {
467     rust_task *task = rust_scheduler::get_task();
468     LOG(task, comm, "new_port(task=0x%" PRIxPTR " (%s), unit_sz=%d)",
469         (uintptr_t) task, task->name, unit_sz);
470     // port starts with refcount == 1
471     return new (task->kernel, "rust_port") rust_port(task, unit_sz);
472 }
473
474 extern "C" CDECL void
475 rust_port_detach(rust_port *port) {
476     rust_task *task = rust_scheduler::get_task();
477     LOG(task, comm, "rust_port_detach(0x%" PRIxPTR ")", (uintptr_t) port);
478     port->detach();
479     // FIXME: Busy waiting until we're the only ref
480     bool done = false;
481     while (!done) {
482         scoped_lock with(port->lock);
483         done = port->ref_count == 1;
484     }
485 }
486
487 extern "C" CDECL void
488 del_port(rust_port *port) {
489     rust_task *task = rust_scheduler::get_task();
490     LOG(task, comm, "del_port(0x%" PRIxPTR ")", (uintptr_t) port);
491     A(task->sched, port->ref_count == 1, "Expected port ref_count == 1");
492     port->deref();
493 }
494
495 extern "C" CDECL size_t
496 rust_port_size(rust_port *port) {
497     return port->size();
498 }
499
500 extern "C" CDECL rust_port_id
501 get_port_id(rust_port *port) {
502     return port->id;
503 }
504
505 extern "C" CDECL uintptr_t
506 chan_id_send(type_desc *t, rust_task_id target_task_id,
507              rust_port_id target_port_id, void *sptr) {
508     // FIXME: make sure this is thread-safe
509     bool sent = false;
510     rust_task *task = rust_scheduler::get_task();
511     rust_task *target_task = task->kernel->get_task_by_id(target_task_id);
512     if(target_task) {
513         rust_port *port = target_task->get_port_by_id(target_port_id);
514         if(port) {
515             port->send(sptr);
516             scoped_lock with(target_task->lock);
517             port->deref();
518             sent = true;
519         }
520         target_task->deref();
521     }
522     return (uintptr_t)sent;
523 }
524
525 // This is called by an intrinsic on the Rust stack and must run
526 // entirely in the red zone. Do not call on the C stack.
527 extern "C" CDECL void
528 rust_task_sleep(rust_task *task, size_t time_in_us, bool *killed) {
529     task->yield(time_in_us, killed);
530 }
531
532 extern "C" CDECL void
533 port_recv(uintptr_t *dptr, rust_port *port,
534           uintptr_t *yield, uintptr_t *killed) {
535     *yield = false;
536     *killed = false;
537     rust_task *task = rust_scheduler::get_task();
538     {
539         scoped_lock with(port->lock);
540
541         LOG(task, comm, "port: 0x%" PRIxPTR ", dptr: 0x%" PRIxPTR
542             ", size: 0x%" PRIxPTR,
543             (uintptr_t) port, (uintptr_t) dptr, port->unit_sz);
544
545         if (port->receive(dptr)) {
546             return;
547         }
548
549         // If this task has been killed then we're not going to bother
550         // blocking, we have to unwind.
551         if (task->killed) {
552             *killed = true;
553             return;
554         }
555
556         // No data was buffered on any incoming channel, so block this task on
557         // the port. Remember the rendezvous location so that any sender task
558         // can write to it before waking up this task.
559
560         LOG(task, comm, "<=== waiting for rendezvous data ===");
561         task->rendezvous_ptr = dptr;
562         task->block(port, "waiting for rendezvous data");
563     }
564     *yield = true;
565     return;
566 }
567
568 //
569 // Local Variables:
570 // mode: C++
571 // fill-column: 78;
572 // indent-tabs-mode: nil
573 // c-basic-offset: 4
574 // buffer-file-coding-system: utf-8-unix
575 // End:
576 //