1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 /* Foreign builtins. */
13 #include "rust_sched_loop.h"
14 #include "rust_task.h"
15 #include "rust_util.h"
16 #include "rust_scheduler.h"
17 #include "sync/timer.h"
18 #include "sync/rust_thread.h"
20 #include "rust_port.h"
25 #include <crt_externs.h>
28 #if !defined(__WIN32__)
33 extern char **environ;
36 extern "C" CDECL rust_str*
38 rust_task *task = rust_get_current_task();
40 LOG(task, task, "last_os_error()");
42 #if defined(__WIN32__)
44 DWORD err = GetLastError();
45 DWORD res = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
46 FORMAT_MESSAGE_FROM_SYSTEM |
47 FORMAT_MESSAGE_IGNORE_INSERTS,
49 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
50 (LPTSTR) &buf, 0, NULL);
55 #elif defined(_GNU_SOURCE)
57 char *buf = strerror_r(errno, cbuf, sizeof(cbuf));
64 int err = strerror_r(errno, buf, sizeof(buf));
71 rust_str * st = make_str(task->kernel, buf, strlen(buf),
74 LocalFree((HLOCAL)buf);
79 extern "C" CDECL rust_str *
81 rust_task *task = rust_get_current_task();
82 LOG(task, task, "rust_getcwd()");
86 #if defined(__WIN32__)
87 if (!_getcwd(cbuf, sizeof(cbuf))) {
89 if (!getcwd(cbuf, sizeof(cbuf))) {
95 return make_str(task->kernel, cbuf, strlen(cbuf), "rust_str(getcwd)");
98 #if defined(__WIN32__)
99 extern "C" CDECL rust_vec_box *
101 rust_task *task = rust_get_current_task();
103 LPTCH ch = GetEnvironmentStringsA();
105 for (c = ch; *c; c += strlen(c) + 1) {
109 rust_vec_box *v = (rust_vec_box *)
110 task->kernel->malloc(vec_size<rust_vec_box*>(envc),
112 v->body.fill = v->body.alloc = sizeof(rust_vec*) * envc;
113 for (size_t i = 0; i < envc; ++i) {
114 size_t n = strlen(c);
115 rust_str *str = make_str(task->kernel, c, n, "str");
116 ((rust_str**)&v->body.data)[i] = str;
120 FreeEnvironmentStrings(ch);
125 extern "C" CDECL rust_vec_box *
127 rust_task *task = rust_get_current_task();
129 char **environ = *_NSGetEnviron();
136 return make_str_vec(task->kernel, envc, environ);
140 extern "C" CDECL void
141 vec_reserve_shared_actual(type_desc* ty, rust_vec_box** vp,
143 rust_task *task = rust_get_current_task();
144 reserve_vec_exact_shared(task, vp, n_elts * ty->size);
147 // This is completely misnamed.
148 extern "C" CDECL void
149 vec_reserve_shared(type_desc* ty, rust_vec_box** vp,
151 rust_task *task = rust_get_current_task();
152 reserve_vec_exact(task, vp, n_elts * ty->size);
155 extern "C" CDECL rust_vec*
157 size_t size = sizeof(ub4) * RANDSIZ;
158 rust_task *task = rust_get_current_task();
159 rust_vec *v = (rust_vec *) task->kernel->malloc(vec_size<uint8_t>(size),
161 v->fill = v->alloc = size;
162 isaac_seed(task->kernel, (uint8_t*) &v->data, size);
166 extern "C" CDECL void *
168 rust_task *task = rust_get_current_task();
169 rust_sched_loop *thread = task->sched_loop;
170 randctx *rctx = (randctx *) task->malloc(sizeof(randctx), "rand_new");
175 isaac_init(thread->kernel, rctx, NULL);
179 extern "C" CDECL void *
180 rand_new_seeded(rust_vec_box* seed) {
181 rust_task *task = rust_get_current_task();
182 rust_sched_loop *thread = task->sched_loop;
183 randctx *rctx = (randctx *) task->malloc(sizeof(randctx),
189 isaac_init(thread->kernel, rctx, seed);
193 extern "C" CDECL void *
194 rand_new_seeded2(rust_vec_box** seed) {
195 return rand_new_seeded(*seed);
198 extern "C" CDECL size_t
199 rand_next(randctx *rctx) {
200 return isaac_rand(rctx);
203 extern "C" CDECL void
204 rand_free(randctx *rctx) {
205 rust_task *task = rust_get_current_task();
210 /* Debug helpers strictly to verify ABI conformance.
212 * FIXME (#2665): move these into a testcase when the testsuite
213 * understands how to have explicit C files included.
230 debug_abi_1(quad q) {
239 debug_abi_2(floats f) {
240 floats ff = { f.c + 1.0,
246 /* Debug builtins for std::dbg. */
249 debug_tydesc_helper(type_desc *t)
251 rust_task *task = rust_get_current_task();
252 LOG(task, stdlib, " size %" PRIdPTR ", align %" PRIdPTR,
256 extern "C" CDECL void
257 debug_tydesc(type_desc *t) {
258 rust_task *task = rust_get_current_task();
259 LOG(task, stdlib, "debug_tydesc");
260 debug_tydesc_helper(t);
263 extern "C" CDECL void
264 debug_opaque(type_desc *t, uint8_t *front) {
265 rust_task *task = rust_get_current_task();
266 LOG(task, stdlib, "debug_opaque");
267 debug_tydesc_helper(t);
268 // FIXME (#2667) may want to actually account for alignment.
269 // `front` may not indeed be the front byte of the passed-in
271 for (uintptr_t i = 0; i < t->size; ++front, ++i) {
272 LOG(task, stdlib, " byte %" PRIdPTR ": 0x%" PRIx8, i, *front);
276 // FIXME (#2667) this no longer reflects the actual structure of boxes!
278 RUST_REFCOUNTED(rust_box)
280 // FIXME (#2667) `data` could be aligned differently from the actual
285 extern "C" CDECL void
286 debug_box(type_desc *t, rust_box *box) {
287 rust_task *task = rust_get_current_task();
288 LOG(task, stdlib, "debug_box(0x%" PRIxPTR ")", box);
289 debug_tydesc_helper(t);
290 LOG(task, stdlib, " refcount %" PRIdPTR,
291 box->ref_count - 1); // -1 because we ref'ed for this call
292 for (uintptr_t i = 0; i < t->size; ++i) {
293 LOG(task, stdlib, " byte %" PRIdPTR ": 0x%" PRIx8, i, box->data[i]);
298 uintptr_t discriminant;
302 extern "C" CDECL void
303 debug_tag(type_desc *t, rust_tag *tag) {
304 rust_task *task = rust_get_current_task();
306 LOG(task, stdlib, "debug_tag");
307 debug_tydesc_helper(t);
308 LOG(task, stdlib, " discriminant %" PRIdPTR, tag->discriminant);
310 for (uintptr_t i = 0; i < t->size - sizeof(tag->discriminant); ++i)
311 LOG(task, stdlib, " byte %" PRIdPTR ": 0x%" PRIx8, i,
320 extern "C" CDECL void
321 debug_fn(type_desc *t, rust_fn *fn) {
322 rust_task *task = rust_get_current_task();
323 LOG(task, stdlib, "debug_fn");
324 debug_tydesc_helper(t);
325 LOG(task, stdlib, " thunk at 0x%" PRIxPTR, fn->thunk);
326 LOG(task, stdlib, " closure at 0x%" PRIxPTR, fn->closure);
328 LOG(task, stdlib, " refcount %" PRIdPTR, fn->closure->ref_count);
332 extern "C" CDECL void *
333 debug_ptrcast(type_desc *from_ty,
336 rust_task *task = rust_get_current_task();
337 LOG(task, stdlib, "debug_ptrcast from");
338 debug_tydesc_helper(from_ty);
339 LOG(task, stdlib, "to");
340 debug_tydesc_helper(to_ty);
344 extern "C" CDECL void *
345 debug_get_stk_seg() {
346 rust_task *task = rust_get_current_task();
350 extern "C" CDECL rust_vec_box*
351 rust_list_files(rust_str *path) {
352 rust_task *task = rust_get_current_task();
353 array_list<rust_str*> strings;
354 #if defined(__WIN32__)
355 WIN32_FIND_DATA FindFileData;
356 HANDLE hFind = FindFirstFile((char*)path->body.data, &FindFileData);
357 if (hFind != INVALID_HANDLE_VALUE) {
359 rust_str *str = make_str(task->kernel, FindFileData.cFileName,
360 strlen(FindFileData.cFileName),
363 } while (FindNextFile(hFind, &FindFileData));
367 DIR *dirp = opendir((char*)path->body.data);
370 while ((dp = readdir(dirp))) {
371 rust_vec_box *str = make_str(task->kernel, dp->d_name,
380 rust_vec_box *vec = (rust_vec_box *)
381 task->kernel->malloc(vec_size<rust_vec_box*>(strings.size()),
383 size_t alloc_sz = sizeof(rust_vec*) * strings.size();
384 vec->body.fill = vec->body.alloc = alloc_sz;
385 memcpy(&vec->body.data[0], strings.data(), alloc_sz);
389 extern "C" CDECL rust_vec_box*
390 rust_list_files2(rust_str **path) {
391 return rust_list_files(*path);
395 rust_path_is_dir(char *path) {
397 if (stat(path, &buf)) {
400 return S_ISDIR(buf.st_mode);
404 rust_path_exists(char *path) {
406 if (stat(path, &buf)) {
412 extern "C" CDECL FILE* rust_get_stdin() {return stdin;}
413 extern "C" CDECL FILE* rust_get_stdout() {return stdout;}
414 extern "C" CDECL FILE* rust_get_stderr() {return stderr;}
417 rust_ptr_eq(type_desc *t, rust_box *a, rust_box *b) {
421 #if defined(__WIN32__)
422 extern "C" CDECL void
423 get_time(int64_t *sec, int32_t *nsec) {
425 GetSystemTimeAsFileTime(&fileTime);
427 // A FILETIME contains a 64-bit value representing the number of
428 // hectonanosecond (100-nanosecond) intervals since 1601-01-01T00:00:00Z.
429 // http://support.microsoft.com/kb/167296/en-us
431 ul.LowPart = fileTime.dwLowDateTime;
432 ul.HighPart = fileTime.dwHighDateTime;
433 uint64_t ns_since_1601 = ul.QuadPart / 10;
435 const uint64_t NANOSECONDS_FROM_1601_TO_1970 = 11644473600000000u;
436 uint64_t ns_since_1970 = ns_since_1601 - NANOSECONDS_FROM_1601_TO_1970;
437 *sec = ns_since_1970 / 1000000;
438 *nsec = (ns_since_1970 % 1000000) * 1000;
441 extern "C" CDECL void
442 get_time(int64_t *sec, int32_t *nsec) {
445 gettimeofday(&tv, NULL);
447 *nsec = tv.tv_usec * 1000;
450 clock_gettime(CLOCK_REALTIME, &ts);
457 extern "C" CDECL void
458 precise_time_ns(uint64_t *ns) {
478 void rust_tm_to_tm(rust_tm* in_tm, tm* out_tm) {
479 memset(out_tm, 0, sizeof(tm));
480 out_tm->tm_sec = in_tm->tm_sec;
481 out_tm->tm_min = in_tm->tm_min;
482 out_tm->tm_hour = in_tm->tm_hour;
483 out_tm->tm_mday = in_tm->tm_mday;
484 out_tm->tm_mon = in_tm->tm_mon;
485 out_tm->tm_year = in_tm->tm_year;
486 out_tm->tm_wday = in_tm->tm_wday;
487 out_tm->tm_yday = in_tm->tm_yday;
488 out_tm->tm_isdst = in_tm->tm_isdst;
491 void tm_to_rust_tm(tm* in_tm, rust_tm* out_tm, int32_t gmtoff,
492 const char *zone, int32_t nsec) {
493 out_tm->tm_sec = in_tm->tm_sec;
494 out_tm->tm_min = in_tm->tm_min;
495 out_tm->tm_hour = in_tm->tm_hour;
496 out_tm->tm_mday = in_tm->tm_mday;
497 out_tm->tm_mon = in_tm->tm_mon;
498 out_tm->tm_year = in_tm->tm_year;
499 out_tm->tm_wday = in_tm->tm_wday;
500 out_tm->tm_yday = in_tm->tm_yday;
501 out_tm->tm_isdst = in_tm->tm_isdst;
502 out_tm->tm_gmtoff = gmtoff;
503 out_tm->tm_nsec = nsec;
506 rust_task *task = rust_get_current_task();
507 size_t size = strlen(zone);
508 reserve_vec_exact(task, &out_tm->tm_zone, size + 1);
509 memcpy(out_tm->tm_zone->body.data, zone, size);
510 out_tm->tm_zone->body.fill = size + 1;
511 out_tm->tm_zone->body.data[size] = '\0';
515 #if defined(__WIN32__)
516 #define TZSET() _tzset()
517 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
518 #define GMTIME(clock, result) gmtime_s((result), (clock))
519 #define LOCALTIME(clock, result) localtime_s((result), (clock))
520 #define TIMEGM(result) _mkgmtime64(result)
522 struct tm* GMTIME(const time_t *clock, tm *result) {
523 struct tm* t = gmtime(clock);
524 if (t == NULL || result == NULL) { return NULL; }
528 struct tm* LOCALTIME(const time_t *clock, tm *result) {
529 struct tm* t = localtime(clock);
530 if (t == NULL || result == NULL) { return NULL; }
534 #define TIMEGM(result) mktime((result)) - _timezone
537 #define TZSET() tzset()
538 #define GMTIME(clock, result) gmtime_r((clock), (result))
539 #define LOCALTIME(clock, result) localtime_r((clock), (result))
540 #define TIMEGM(result) timegm(result)
543 extern "C" CDECL void
548 extern "C" CDECL void
549 rust_gmtime(int64_t *sec, int32_t *nsec, rust_tm *timeptr) {
554 tm_to_rust_tm(&tm, timeptr, 0, "UTC", *nsec);
557 extern "C" CDECL void
558 rust_localtime(int64_t *sec, int32_t *nsec, rust_tm *timeptr) {
563 #if defined(__WIN32__)
564 int32_t gmtoff = -timezone;
566 strftime(zone, sizeof(zone), "%Z", &tm);
568 int32_t gmtoff = tm.tm_gmtoff;
569 const char *zone = tm.tm_zone;
572 tm_to_rust_tm(&tm, timeptr, gmtoff, zone, *nsec);
575 extern "C" CDECL void
576 rust_timegm(rust_tm* timeptr, int64_t *out) {
578 rust_tm_to_tm(timeptr, &t);
582 extern "C" CDECL void
583 rust_mktime(rust_tm* timeptr, int64_t *out) {
585 rust_tm_to_tm(timeptr, &t);
589 extern "C" CDECL rust_sched_id
590 rust_get_sched_id() {
591 rust_task *task = rust_get_current_task();
592 return task->sched->get_id();
595 extern "C" CDECL uintptr_t
597 rust_task *task = rust_get_current_task();
598 return task->kernel->env->num_sched_threads;
603 rust_task *task = rust_get_current_task();
604 return task->kernel->env->argc;
607 extern "C" CDECL char**
609 rust_task *task = rust_get_current_task();
610 return task->kernel->env->argv;
613 extern "C" CDECL rust_sched_id
614 rust_new_sched(uintptr_t threads) {
615 rust_task *task = rust_get_current_task();
616 assert(threads > 0 && "Can't create a scheduler with no threads, silly!");
617 return task->kernel->create_scheduler(threads);
620 extern "C" CDECL rust_task_id
622 rust_task *task = rust_get_current_task();
627 new_task_common(rust_scheduler *sched, rust_task *parent) {
628 return sched->create_task(parent, NULL);
631 extern "C" CDECL rust_task*
633 rust_task *task = rust_get_current_task();
634 return new_task_common(task->sched, task);
637 extern "C" CDECL rust_task*
638 rust_new_task_in_sched(rust_sched_id id) {
639 rust_task *task = rust_get_current_task();
640 rust_scheduler *sched = task->kernel->get_scheduler_by_id(id);
643 return new_task_common(sched, task);
646 extern "C" rust_task *
648 return rust_get_current_task();
651 extern "C" CDECL stk_seg *
652 rust_get_stack_segment() {
653 return rust_get_current_task()->stk;
656 extern "C" CDECL void
657 start_task(rust_task *target, fn_env_pair *f) {
658 target->start(f->f, f->env, NULL);
661 extern "C" CDECL size_t
662 rust_sched_current_nonlazy_threads() {
663 rust_task *task = rust_get_current_task();
664 return task->sched->number_of_threads();
667 extern "C" CDECL size_t
668 rust_sched_threads() {
669 rust_task *task = rust_get_current_task();
670 return task->sched->max_number_of_threads();
673 extern "C" CDECL rust_port*
674 rust_port_take(rust_port_id id) {
675 rust_task *task = rust_get_current_task();
676 return task->kernel->get_port_by_id(id);
679 extern "C" CDECL void
680 rust_port_drop(rust_port *p) {
685 extern "C" CDECL rust_task_id
686 rust_port_task(rust_port *p) {
691 extern "C" CDECL rust_port*
692 new_port(size_t unit_sz) {
693 rust_task *task = rust_get_current_task();
694 LOG(task, comm, "new_port(task=0x%" PRIxPTR " (%s), unit_sz=%d)",
695 (uintptr_t) task, task->name, unit_sz);
696 // port starts with refcount == 1
697 return new (task->kernel, "rust_port") rust_port(task, unit_sz);
700 extern "C" CDECL void
701 rust_port_begin_detach(rust_port *port, uintptr_t *yield) {
702 rust_task *task = rust_get_current_task();
703 LOG(task, comm, "rust_port_detach(0x%" PRIxPTR ")", (uintptr_t) port);
704 port->begin_detach(yield);
707 extern "C" CDECL void
708 rust_port_end_detach(rust_port *port) {
712 extern "C" CDECL void
713 del_port(rust_port *port) {
714 rust_task *task = rust_get_current_task();
715 LOG(task, comm, "del_port(0x%" PRIxPTR ")", (uintptr_t) port);
719 extern "C" CDECL size_t
720 rust_port_size(rust_port *port) {
724 extern "C" CDECL rust_port_id
725 get_port_id(rust_port *port) {
729 extern "C" CDECL uintptr_t
730 rust_port_id_send(rust_port_id target_port_id, void *sptr) {
731 rust_task *task = rust_get_current_task();
732 return (uintptr_t)task->kernel->send_to_port(target_port_id, sptr);
735 // This is called by an intrinsic on the Rust stack and must run
736 // entirely in the red zone. Do not call on the C stack.
737 extern "C" CDECL MUST_CHECK bool
738 rust_task_yield(rust_task *task, bool *killed) {
739 return task->yield();
742 extern "C" CDECL void
743 port_recv(uintptr_t *dptr, rust_port *port, uintptr_t *yield) {
744 port->receive(dptr, yield);
747 extern "C" CDECL void
748 rust_port_select(rust_port **dptr, rust_port **ports,
749 size_t n_ports, uintptr_t *yield) {
750 rust_task *task = rust_get_current_task();
751 rust_port_selector *selector = task->get_port_selector();
752 selector->select(task, dptr, ports, n_ports, yield);
755 extern "C" CDECL void
756 rust_set_exit_status(intptr_t code) {
757 rust_task *task = rust_get_current_task();
758 task->kernel->set_exit_status((int)code);
761 extern void log_console_on();
763 extern "C" CDECL void
764 rust_log_console_on() {
768 extern void log_console_off(rust_env *env);
770 extern "C" CDECL void
771 rust_log_console_off() {
772 rust_task *task = rust_get_current_task();
773 log_console_off(task->kernel->env);
776 extern "C" CDECL lock_and_signal *
777 rust_dbg_lock_create() {
778 return new lock_and_signal();
781 extern "C" CDECL void
782 rust_dbg_lock_destroy(lock_and_signal *lock) {
787 extern "C" CDECL void
788 rust_dbg_lock_lock(lock_and_signal *lock) {
793 extern "C" CDECL void
794 rust_dbg_lock_unlock(lock_and_signal *lock) {
799 extern "C" CDECL void
800 rust_dbg_lock_wait(lock_and_signal *lock) {
805 extern "C" CDECL void
806 rust_dbg_lock_signal(lock_and_signal *lock) {
811 typedef void *(*dbg_callback)(void*);
813 extern "C" CDECL void *
814 rust_dbg_call(dbg_callback cb, void *data) {
818 extern "C" CDECL void rust_dbg_do_nothing() { }
820 extern "C" CDECL void
821 rust_dbg_breakpoint() {
825 extern "C" CDECL rust_sched_id
826 rust_osmain_sched_id() {
827 rust_task *task = rust_get_current_task();
828 return task->kernel->osmain_sched_id();
831 extern "C" CDECL bool
832 rust_compare_and_swap_ptr(intptr_t *address,
833 intptr_t oldval, intptr_t newval) {
834 return sync::compare_and_swap(address, oldval, newval);
837 extern "C" CDECL void
838 rust_task_weaken(rust_port_id chan) {
839 rust_task *task = rust_get_current_task();
840 task->kernel->weaken_task(chan);
843 extern "C" CDECL void
844 rust_task_unweaken(rust_port_id chan) {
845 rust_task *task = rust_get_current_task();
846 task->kernel->unweaken_task(chan);
849 extern "C" CDECL uintptr_t*
850 rust_global_env_chan_ptr() {
851 rust_task *task = rust_get_current_task();
852 return task->kernel->get_global_env_chan();
856 rust_task_inhibit_kill(rust_task *task) {
857 task->inhibit_kill();
861 rust_task_allow_kill(rust_task *task) {
866 rust_task_inhibit_yield(rust_task *task) {
867 task->inhibit_yield();
871 rust_task_allow_yield(rust_task *task) {
876 rust_task_kill_other(rust_task *task) { /* Used for linked failure */
881 rust_task_kill_all(rust_task *task) { /* Used for linked failure */
882 task->fail_sched_loop();
883 // This must not happen twice.
884 static bool main_taskgroup_failed = false;
885 assert(!main_taskgroup_failed);
886 main_taskgroup_failed = true;
890 bool rust_task_is_unwinding(rust_task *rt) {
891 return rt->unwinding;
894 extern "C" lock_and_signal*
895 rust_create_little_lock() {
896 return new lock_and_signal();
900 rust_destroy_little_lock(lock_and_signal *lock) {
905 rust_lock_little_lock(lock_and_signal *lock) {
910 rust_unlock_little_lock(lock_and_signal *lock) {
914 // set/get/atexit task_local_data can run on the rust stack for speed.
916 rust_get_task_local_data(rust_task *task) {
917 return task->task_local_data;
920 rust_set_task_local_data(rust_task *task, void *data) {
921 task->task_local_data = data;
924 rust_task_local_data_atexit(rust_task *task, void (*cleanup_fn)(void *data)) {
925 task->task_local_data_cleanup = cleanup_fn;
929 task_clear_event_reject(rust_task *task) {
930 task->clear_event_reject();
933 // Waits on an event, returning the pointer to the event that unblocked this
935 extern "C" MUST_CHECK bool
936 task_wait_event(rust_task *task, void **result) {
937 // Maybe (if not too slow) assert that the passed in task is the currently
938 // running task. We wouldn't want to wait some other task.
940 return task->wait_event(result);
944 task_signal_event(rust_task *target, void *event) {
945 target->signal_event(event);
948 // Can safely run on the rust stack.
950 rust_task_ref(rust_task *task) {
954 // Don't run on the rust stack!
956 rust_task_deref(rust_task *task) {
960 // Must call on rust stack.
961 extern "C" CDECL void
962 rust_call_tydesc_glue(void *root, size_t *tydesc, size_t glue_index) {
963 void (*glue_fn)(void *, void *, void *, void *) =
964 (void (*)(void *, void *, void *, void *))tydesc[glue_index];
966 glue_fn(0, 0, 0, root);
969 // Don't run on the Rust stack!
971 rust_log_str(uint32_t level, const char *str, size_t size) {
972 rust_task *task = rust_get_current_task();
973 task->sched_loop->get_log().log(task, level, "%.*s", (int)size, str);
976 extern "C" CDECL void record_sp_limit(void *limit);
978 class raw_thread: public rust_thread {
982 raw_thread(fn_env_pair *fn) : fn(fn) { }
986 fn->f(NULL, fn->env, NULL);
990 extern "C" raw_thread*
991 rust_raw_thread_start(fn_env_pair *fn) {
993 raw_thread *thread = new raw_thread(fn);
999 rust_raw_thread_join_delete(raw_thread *thread) {
1010 // indent-tabs-mode: nil
1011 // c-basic-offset: 4
1012 // buffer-file-coding-system: utf-8-unix