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